您的位置:首页 > 其它

20、Bootloader(4) -- U-Boot第二阶段分析

2011-08-23 15:08 211 查看
 

********************************************************************/

Bootloader第二阶段的功能:(运行在内存当中)

         -- 初始化本阶段要使用到的硬件设备(串口、网口类的硬件)

         -- 检测系统内存映射(memory map)

         -- 将内核映象和根文件系统映象从Flash上读到RAM空间中

         -- 为内核设置启动参数

         -- 调用内核

   调用内核前,下列条件要满足:

        (1)CPU寄存器的设置

              R0 = 0

              R1 = 机器类型ID

              R2 = 启动参数标记列表在RAM中起始基地址

        (2)CPU工作模式

              必须禁止中断(IRQs和F
4000
IQs)

              CPU必须为SVC模式

        (3)Cache和MMU的设置

              MMU必须关闭

              指令Cache可以打开也可以关闭,数据Cache必须关闭

********************************************************************/

(1)初始化本阶段要使用到的硬件设备

    最主要的是设置系统时钟、初始化串口,只要这两个设置好了,就可以从串口看到打印信息。

board_init函数设置MPLL、改变系统时钟,它是开发板相关的函数,在board/smdk2410/smdk2410.c中实现。值得注意的是,board_init函数中还保存了机器类型ID,这将在调用内核时传给内核,代码如下:

 

  gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

串口的初始化函数主要是serial_init,它设置UART控制器,是CPU相关的函数,在cpu/arm920t/s3c24x0/serial.c中实现。



(2)检测系统内存映射(memory map)

    对于特定的开发板,其内存的分布是明确的,所以可以直接设置。board/smdk2410/smdk2410.c中的dram_init函数指定了本开发板的内存起始地址为0x30000000,大小为0x4000000。代码如下:

int dram_init (void)

{

   gd->bd->bi_dram[0].start = PHYS_SDRAM_1;

   gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

   return 0;

}

 这些设置的参数,将在后面向内核传递参数时用到。



(3)U-Boot命令的格式
即使是内核的启动,也是通过U-Boot命令来实现的。U-Boot中每个命令都通过U_BOOT_CMD宏来定义,格式如下:

U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")

各项参数的意义为:



① name:命令的名字,注意,它不是一个字符串(不要用双引号括起来)。

② maxargs:最大的参数个数

③ repeatable:命令是否可重复,可重复是指运行一个命令后,下次敲回

       车即可再次运行。

④ command:对应的函数指针,类型为(*cmd)(struct cmd_tbl_s *, int,  

       int,char *[])。

⑤ usage:简短的使用说明,这是个字符串。

⑥ help:较详细的使用说明,这是个字符串。

宏U_BOOT_CMD在include/command.h中定义:

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep,  

                                                   
cmd, usage, help}

Struct_Section也是在include/command.h中定义:

#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))

比如对于bootm命令,它如此定义:

U_BOOT_CMD(

bootm,CFG_MAXARGS,1,do_bootm,

“string1”,

“string2”

);

宏U_BOOT_CMD扩展开后就是:

   cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd"))) = {“bootm”, CFG_MAXARGS, 1, do_bootm, “string1”, “string2”};



  对于每个使用U_BOOT_CMD宏来定义的命令,其实都是在".u_boot_cmd"段中定义一个cmd_tbl_t结构。连接脚本u-boot.lds中有这么一段:

__u_boot_cmd_start = .;

.u_boot_cmd : { *(.u_boot_cmd) }



__u_boot_cmd_end = .;

程序中就是根据命令的名字在内存段__u_boot_cmd_start~__u_boot_cmd_end找到它的cmd_tbl_t结构,然后调用它的函数(请参考common/command.c中的find_cmd函数)。

内核的复制和启动,可以通过如下命令来完成:bootm从内存、ROM、NOR Flash中启动内核,bootp则通过网络来启动,而nboot从NAND Flash启动内核。它们都是先将内核映像从各种媒介中读出,存放在指定的位置;然后设置标记列表以给内核传递参数;最后跳到内核的入口点去执行。具体实现的细节不再描述,有兴趣的读者可以阅读common/cmd_boot.c、common/cmd_net.c、common/cmd_nand.c来了解它们的实现。



(4)为内核设置启动参数
与《Bootloader与内核的交互》所描述的一样,U-Boot也是通过标记列表向内核传递参数。并且,内存标记、命令行标记的示例代码就是取自U-Boot中的setup_memory_tags、setup_commandline_tag函数,它们都是在lib_arm/armlinux.c中定义。一般而言,设置这两个标记就可以了,在配置文件include/configs/smdk2410.h 中增加如下两个配置项即可:

#define CONFIG_SETUP_MEMORY_TAGS 1

#define CONFIG_CMDLINE_TAG 1

对于ARM架构的CPU,都是通过lib_arm/armlinux.c中的do_bootm_linux函数来启动内核。这个函数中,设置标记列表,最后通过“theKernel (0, bd->bi_arch_number, bd->bi_boot_params)”调用内核。其中,theKernel指向内核存放的地址(对于ARM架构的CPU,通常是 0x30008000),bd->bi_arch_number就是前面board_init函数设置的机器类型ID,而bd-> bi_boot_params就是标记列表的开始地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息