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

 

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

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

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

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

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

         -- 为内核设置启动参数

         -- 调用内核

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

        (1)CPU寄存器的设置

              R0 = 0

              R1 = 机器类型ID

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

        (2)CPU工作模式

              必须禁止中断(IRQs和FIQs)

              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就是标记列表的开始地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值