s = getenv ("bootcmd");
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
......
run_command (s, 0);
......
}
int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int rcode = 0;
#ifndef CFG_HUSH_PARSER
if (run_command (getenv ("bootcmd"), flag) < 0) rcode = 1;
#else
if (parse_string_outer(getenv("bootcmd"),
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0 ) rcode = 1;
#endif
return rcode;
}
U_BOOT_CMD(
boot, 1, 1, do_bootd,
"boot - boot default, i.e., run 'bootcmd'\n",
NULL
);
比如我这里bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
在run_command中会将命令分离并找对应的处理函数。
#define MTDIDS_DEFAULT "nand0=nandflash0"
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \
"128k(params)," \
"2m(kernel)," \
"-(root)"
在/common/cmd_nand.c中do_nand函数中
/* read write */
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0)
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;
下面要开始分析如何启动内核,主要是do_bootm函数:
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
......
image_header_t *hdr = &header; //uimage 是内核加了一个4K的头部,这个头部的内容是按照结构体image_header_t来放在,是image传递给Uboot的信息。
......
if (argc < 2) {
addr = load_addr; //如果bootm的参数小于2 则使用默认的加载地址
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
......
switch (hdr->ih_comp) {
case IH_COMP_NONE:
if(ntohl(hdr->ih_load) == addr) {
/*
这里判断“uimage头部里指定的加载地址”与bootm指定的加载地址是否相等,不相等则需要移动
判断的方式有两种
1、判断 uimage头部里指定的加载地址 == bootm指定的加载地址 (hdr->ih_load == addr)
此时:
实际存放的地址 == uimage加载地址 == uimage连接地址-64字节
bootm == 实际存放的地址
例子:
实际存放在 0x30007fc0
bootm 0x30007fc0
加载地址 0x30007fc0
连接地址 0x30008000
1、uboot根据Bootm指定的0x30007fc0去找image,实际地址为0x30007fc0,找到头部
2、读出头部里的加载地址,判断是否和Bootm相等,相等则不移动,启动
2、判断 uimage头部里指定的加载地址 == bootm指定的加载地址 + 64字节 (hdr->ih_load == addr+64字节/data)
此时:
实际存放地址+64字节 == uimage加载地址 == uimage连接地址
bootm == 实际存放的地址
例子:
实际存放在 0x30007fc0
bootm 0x30007fc0
加载地址 0x30008000
连接地址 0x30008000
1、uboot根据Bootm指定的0x30007fc0去找image,实际地址为0x30007fc0,找到头部
2、读出头部里的加载地址,判断是否和Bootm + 字节相等,相等则不移动,启动
首先bootm的地址要和我们 实际存放(不管它的加载地址是多少) 整个uimage的首地址吻合,这样就可以找到头部。
这里存在两种情况,我们可以看到 Uboot源码里
1、 hdr->ih_load == addr
也就是说判断的是bootm_addr 与uimage里的ih_load加载地址是否相等,这样的话,我们在制作uimage的时候就应该让ih_load=bootmaddr
那么uimage里的另一个参数,连接地址就应该等于bootm+64字节
2、hdr->ih_load == addr+64字节 友善以及韦东山老师的uboot里判断条件都被改成了这样
那么,uimage里的Load地址和连接地址应该相等,等于bootm+64字节
*/
printf (" XIP %s ... ", name);
} else {
......do_bootm_linux (cmdtp, flag, argc, argv,addr, len_ptr, verify);
}
//uboot参数设置
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif
printf ("\nStarting kernel ...\n\n");
//启动内核
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
theKernel定义:
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
setup_start_tag (bd);setup_memory_tags (bd);setup_commandline_tag (bd, commandline);setup_end_tag (bd);
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
看board\100ask24x0\100ask24x0.c
1.启动标记:
setup_start_tag:
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
由代码可以得到tag是一个结构体,bi_boot_params为0x300000100,ATAG_CORE为54410001
2.内存标记:
setup_memory_tag:
#ifdef CONFIG_SETUP_MEMORY_TAGS
static void setup_memory_tags (bd_t *bd)
{
int i;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = bd->bi_dram[i].start;
params->u.mem.size = bd->bi_dram[i].size;
params = tag_next (params);
}
}
#endif
bi_dram[i].start;内存的初始地址,bi_dram[i].start;内存的大小
这两个参数的初始值在start_armboot()函数中dram_init可以设置。
3.命令行标记:
static void setup_commandline_tag (bd_t *bd, char *commandline)
{
char *p;
if (!commandline)
return;
把命令前面的空格给干掉
for (p = commandline; *p == ' '; p++);
if (*p == '\0')
return;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size =
(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
}
*commandlinechar
的来源为*commandline = getenv ("bootargs");
那么在终端所获得的信息是bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
root=/dev/mtdblock3 根文件系统在Flash中的第三分区
init=/linuxrc 第一个进程为linuxrc
console=ttySAC0 内核打印信息从串口输出
4.结束标记:
setup_end_tag:
static void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
参数设置结束的时候将tag设置为空,size设置为0.