20.4 建立目标平台代码框架

20.4  建立目标平台代码框架

20.3.3节编译的内核代码最后出现了链接错误,提示vmlinux.lds文件链接失败。lds文件是GNU ld工具使用的一种脚本文件,该文件描述了如何分配链接后的内存区域和地址等信息,通过lds文件报的错误可以推理分析问题产生的原因。

20.4.1  ARM处理器相关结构

首先打开arch/arm/kernel/vmlinux.lds文件,找到815行,代码如下:

 

815 ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")

 

该行代码使用一个ASSERT宏判断__proc_info_end标号的地址与__proc_info_begin标号地址是否相同。__proc_info_end__proc_info_begin标号之间定义了一个初始化阶段使用的结构,在ARM处理器启动的时候,内核会调用该结构初始化ARM处理器。

arch/arm目录下搜索__proc_info_begin标号:

 

$ grep -nR '__proc_info_begin' *

kernel/head.S:516:      .long   __proc_info_begin

kernel/vmlinux.lds.S:25:                __proc_info_begin = .;

kernel/vmlinux.lds.S:166:ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")

 

使用grep命令搜索得到3条结果。从结果看出,__proc_info_begin标号定义在kernel/ head.S文件的第516行,在kernel/vmlinux.lds.S文件使用到了__proc_info_begin标号。打开kernel/vmlinux.lds.S文件查看:

 

25     __proc_info_begin = .;        // .proc.info段的起始地址

26       *(.proc.info)                // 段名称

27     __proc_info_end = .;            // .proc.info段的结束地址

 

vmlinux.lds.S文件中定义了.proc.info代码段,该段代码的起始地址和结束地址分别由__proc_info_begin__proc_info_end标号标示,这两个标号表示的地址是通过计算得         到的。

ARM体系代码中,使用machine_desc结构描述与处理器相关的代码,该结构定义在include/asm-arm/mach/arch.h头文件定义如下:

 

17 struct machine_desc {

18   /*

19    * Note! The first five elements are used

20    * by assembler code in head-armv.S

21    */

22   unsigned int    nr;   /* architecture number  */ 
                                                    //
处理器编号,自动生成

23   unsigned int    phys_ram; /* start of physical ram */
                                                    //
物理内存起始地址

24   unsigned int    phys_io;  /* start of physical io */
                                                    //
物理I/O端口起始地址

25   unsigned int    io_pg_offst;  /* byte offset for io

26              * page tabe entry  */

27

28   const char    *name;    /* architecture name  */      // 处理器名称

29   unsigned long   boot_params;  /* tagged list    */    // 启动参数列表地址

30

31   unsigned int    video_start;  /* start of video RAM */
                                                    //
视频设备存储器起始地址

32   unsigned int    video_end;  /* end of video RAM */
                                                    //
视频设备存储器结束地址

33

34   unsigned int    reserve_lp0 :1; /* never has lp0  */

35   unsigned int    reserve_lp1 :1; /* never has lp1  */

36   unsigned int    reserve_lp2 :1; /* never has lp2  */

37   unsigned int    soft_reboot :1; /* soft reboot    */ // 是否软启动

38   void      (*fixup)(struct machine_desc *,

39            struct tag *, char **,

40            struct meminfo *);

41   void      (*map_io)(void);/* IO mapping function  */ 
                                                    // I/O
中断处理映射函数

42   void      (*init_irq)(void);                    // 中断响应函数

43   struct sys_timer  *timer;   /* system tick timer  */ 
                                                    //
定时器

44   void      (*init_machine)(void);                // 初始化函数

45 };

 

machine_desc结构描述了处理器体系结构编号、物理内存大小、处理器名称、I/O处理函数、定时器处理函数等。每种ARM核的处理器都必须实现一个machine_desc结构,内核代码会使用该结构。

20.4.2  建立machine_desc结构

Linux内核提供了MACHINE_STARTMACHINE_END宏供建立machine_desc结构使用,建议使用宏建立结构。打开arch/arm/mach-s3c2410/mach-mini2440.c文件,加入下面的代码:

 

53 MACHINE_START(MINI2440, "MINI2440")          // 定义结构名称

54   .phys_ram = S3C2410_SDRAM_PA,                // 物理内存起始地址

55   .phys_io  = S3C2410_PA_UART,             // 物理端口起始地址

56   .io_pg_offst  = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

57   .boot_params  = S3C2410_SDRAM_PA + 0x100,    // 启动参数存放地址

58

59   .init_irq = mini2440_init_irq,                // 中断初始化函数

60   .map_io   = mini2440_map_io,             // I/O端口内存映射函数

61   .init_machine = mini2440_init,               // 初始化函数

62   .timer    = &s3c24xx_timer,                  // 定时器

63 MACHINE_END

 

MACHINE_START宏定义了一个名为MINI2440的结构,并且定义了相关的内存和端口地址、处理函数等。请读者注意定义结构的行号不是从第1行开始的,前面的代码行包含了头文件。头文件可以从其他文件复制一份,如从mach-smdk2440.c文件复制一份头文件定义。

%提示:参考其他类似工程的代码是一个捷径,对于能复用的代码和变量建议使用已经定义好的,这样可以减轻编码和调试的工作量,减少出错机会。

MINI2440结构中使用到了S3C2410_SDRAM_PAS3C2410_PA_UART宏,这两个宏分别定义了开发板物理内存起始地址和物理端口起始地址。由于24102440处理器对内存地址映射关系相同,可以直接使用S3C2410_SDRAM_PAS3C2410_PA_UART宏。有关S3C2440处理器内存映射请参考处理器手册的内存管理章节。

20.4.3  加入处理函数

mach-mini2440.c文件中加入MINI2440结构指定的几个函数,定义如下:

 

52 void __init  mini2440_init_irq(void)       // 中断初始化函数

53 {

54 }

55

56 void __init  mini2440_init(void)            // 处理器初始化函数

57 {

58 }

59

60 void __init  mini2440_map_io(void)     // I/O端口映射初始化函数

61 {

62 }

 

mach-mini2440.c文件中加入了mini2440_init_irq()mini2440_init()mini2440_ map_io()3个函数。请读者注意这3个函数定义的时候使用了__init关键字,__init关键字告诉ld链接器把函数放在初始化段,初始化段的代码仅在初始化的时候被调用一次。

%提示:在本节函数留空即可,后面的章节会不断增加代码,本节主要是搭建代码框架。

20.4.4  加入定时器结构

MINI2440结构定义中,使用了一个名为s3c24xx_timersys_timer结构变量,该变量定义在arch/arm/mach-s3c2410/timer.c文件定义如下:

 

252 struct sys_timer s3c24xx_timer = {

253   .init   = s3c2410_timer_init,            // 定时器初始化函数

254   .offset   = s3c2410_gettimeoffset,    // 读取定时器延时

255   .resume   = s3c2410_timer_setup       // 恢复定时器

256 };

 

S3C24xx系列处理器定时器的操作相同,因此使用内核代码已经定义好的定时器结构即可,无须从头开发。

20.4.5  测试代码结构

回到内核源代码根目录,执行make ARCH=arm CROSS_COMPILE=arm-linux- bzImage开始编译内核。这次编译没有出错信息,会得到正确的编译结果。查看arch/arm/boot目录已经有目标文件Image.gz,表示已经编译生成运行于ARM处理器的内核。

到目前为止,已经可以编译工作在ARM处理器上的代码,但是内核代码还不能启动,因为还没有加入实际的代码,在20.5节中将介绍如何加入目标平台相关的代码。

你可能感兴趣的:(20.4 建立目标平台代码框架)