一、uboot最终目的:
1.读出内核 do_nand read kernel
{
flash上存的内核:uImage = 头部 + 真正的内核;
}
2.启动内核。 do_bootm_linux
{
(1)设置启动参数; uboot到kernel的启动参数的传递, 靠的就是告诉kernel参数存放的绝对地址,并按照约定好的格式存放。具体的格式约定比较复杂,见uboot
(2)跳到入口地址.
}
二、在uboot里,打印一下环境变量,下面两句是启动kernel的关键字:
bootcmd=nand read 0x30000000 0x60000 0x200000;bootm 0x30000000
bootargs=noinitrd root=/dev/nfs nfsroot=192.168.2.109:/home/fs/work/nfs_root/fs_mini_mdev ip=192.168.2.111:192.168.2.109:192.168.2.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
三、分析uboot代码,看它是如何实现上述功能的:
写的比较抽象,仅供个人理解。
//uImage头部 typedef struct image_header { uint32_t ih_magic; /* Image Header Magic Number */ uint32_t ih_hcrc; /* Image Header CRC Checksum */ uint32_t ih_time; /* Image Creation Timestamp */ uint32_t ih_size; /* Image Data Size */ uint32_t ih_load; /* Data Load Address 加载地址, 要运行内核时, 先把内核放在哪里*/ uint32_t ih_ep; /* Entry Point Address 要运行内核时,直接跳到这个地址就可以了*/ uint32_t ih_dcrc; /* Image Data CRC Checksum */ uint8_t ih_os; /* Operating System */ uint8_t ih_arch; /* CPU architecture */ uint8_t ih_type; /* Image Type */ uint8_t ih_comp; /* Compression Type */ uint8_t ih_name[IH_NMLEN]; /* Image Name */ } image_header_t; //uboot和kernel传递参数所依赖的结构体,详细内容见 include/asm-arm/setup.h struct tag { struct tag_header hdr; union { struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u; }; //cmd_nand.c //bootcmd-- nand read 0x30000000 0x60000 0x200000 int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { nand_read_opts(nand, &opts); // nand_read_opts : drivers/nand/nand_util.c } //cmd_bootm.c //bootcmd-- bootm 0x30000000 int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { addr = simple_strtoul(argv[1], NULL, 16); //printf ("## Booting image at %08lx ... ", addr); memmove (&header, (char *)addr, sizeof(image_header_t)); //puts (" Verifying Checksum ... "); //unsigned long int data = start addr of kernel int the sdram after the cmd of nand read; if(ntohl(hdr->ih_load) == data) //如果期望的kernel运行地址 = 实际从nand 读入内存的kernel起始地址。(都不含头) {// 不需要移动 printf (" XIP %s ... ", name); } else {// 需要移动 //printf (" Loading %s ... ", name); len = ntohl(hdr->ih_size); memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); } do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify) { void (*theKernel)(int zero, int arch, uint params); theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); setup_start_tag (bd)/设置内核启动参数的其实存放地址bi_boot_params { bd_t *bd = gd->bd; //gd->bd->bi_boot_params = 0x30000100; } //这之后从bi_boot_params开始,依次存入各启动参数 setup_memory_tags (bd); setup_commandline_tag (bd, commandline);// char *commandline = getenv ("bootargs"); setup_end_tag (bd);//表示完成启动参数的传递。 theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//跳到入口地址 } } //uboot的核心:通过run_command实现各种功能 void main_loop (void) { getenv ("bootdelay"); //uboot如何启动内核: s = getenv ("bootcmd"); printf("Booting Linux ... "); run_command (s, 0) { (cmdtp->cmd) (cmdtp, flag, argc, argv); } //uboot如何接受用户命令: for (;;) { readline (CFG_PROMPT); //read input into console_buffer strcpy (lastcommand, console_buffer); run_command (lastcommand, flag); } }