一、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[], NULL, );
//printf ("## Booting image at %08lx ...\n", 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 (, bd->bi_arch_number, bd->bi_boot_params);//跳到入口地址
}
} //uboot的核心:通过run_command实现各种功能
void main_loop (void)
{
getenv ("bootdelay"); //uboot如何启动内核:
s = getenv ("bootcmd");
printf("Booting Linux ...\n");
run_command (s, )
{
(cmdtp->cmd) (cmdtp, flag, argc, argv);
} //uboot如何接受用户命令:
for (;;)
{
readline (CFG_PROMPT); //read input into console_buffer
strcpy (lastcommand, console_buffer);
run_command (lastcommand, flag);
} }