uboot版本2016.03
bootz命令加载linux内核流程分析
bootz命令在启动内核的过程中会一直伴随一个全局变量images,images是一个bootm_headers_t形式的结构体,结构体的定义在文件/include/image.h中:
typedef struct bootm_headers {
/*
* Legacy os image header, if it is a multi component image
* then boot_get_ramdisk() and get_fdt() will attempt to get
* data from second and third component accordingly.
*/
image_header_t *legacy_hdr_os; /* image header pointer */
image_header_t legacy_hdr_os_copy; /* header copy */
ulong legacy_hdr_valid;
#if defined(CONFIG_FIT)
const char *fit_uname_cfg; /* configuration node unit name */
void *fit_hdr_os; /* os FIT image header */
const char *fit_uname_os; /* os subimage node unit name */
int fit_noffset_os; /* os subimage node offset */
void *fit_hdr_rd; /* init ramdisk FIT image header */
const char *fit_uname_rd; /* init ramdisk subimage node unit name */
int fit_noffset_rd; /* init ramdisk subimage node offset */
void *fit_hdr_fdt; /* FDT blob FIT image header */
const char *fit_uname_fdt; /* FDT blob subimage node unit name */
int fit_noffset_fdt;/* FDT blob subimage node offset */
void *fit_hdr_setup; /* x86 setup FIT image header */
const char *fit_uname_setup; /* x86 setup subimage node name */
int fit_noffset_setup;/* x86 setup subimage node offset */
#endif
#ifndef USE_HOSTCC
image_info_t os; /* os image info */
ulong ep; /* entry point of OS */
ulong rd_start, rd_end;/* ramdisk start/end */
char *ft_addr; /* flat dev tree address */
ulong ft_len; /* length of flat device tree */
ulong initrd_start;
ulong initrd_end;
ulong cmdline_start;
ulong cmdline_end;
bd_t *kbd;
#endif
int verify; /* getenv("verify")[0] != 'n' */
#define BOOTM_STATE_START (0x00000001)
#define BOOTM_STATE_FINDOS (0x00000002)
#define BOOTM_STATE_FINDOTHER (0x00000004)
#define BOOTM_STATE_LOADOS (0x00000008)
#define BOOTM_STATE_RAMDISK (0x00000010)
#define BOOTM_STATE_FDT (0x00000020)
#define BOOTM_STATE_OS_CMDLINE (0x00000040)
#define BOOTM_STATE_OS_BD_T (0x00000080)
#define BOOTM_STATE_OS_PREP (0x00000100)
#define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */
#define BOOTM_STATE_OS_GO (0x00000400)
int state;
#ifdef CONFIG_LMB
struct lmb lmb; /* for memory mgmt */
#endif
} bootm_headers_t;
第32行,定义了一个image_info_t类型的成员变量os,通过此变量描述系统的镜像信息。定义如下:
typedef struct image_info {
ulong start, end; /* blob 开始和结束位置*/
ulong image_start, image_len; /* 镜像起始地址(包括 blob)和长度 */
ulong load; /* 系统镜像加载地址*/
uint8_t comp, type, os; /* 镜像压缩、类型, OS 类型 */
uint8_t arch; /* CPU 架构 */
} image_info_t;
第49~59行,宏定义用来描述uboot启动内核的不同阶段。
do_bootz 函数
bootz命令对应调用的函数就是do_bootz函数,函数定义在/cmd/bootm.c中,如下:
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int ret;
/* Consume 'bootz' */
argc--; argv++;
if (bootz_start(cmdtp, flag, argc, argv, &images))
return 1;
/*
* We are doing the BOOTM_STATE_LOADOS state ourselves, so must
* disable interrupts ourselves
*/
bootm_disable_interrupts();
images.os.os = IH_OS_LINUX;
ret = do_bootm_states(cmdtp, flag, argc, argv,
BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
BOOTM_STATE_OS_GO,
&images, 1);
return ret;
}
第6行,相当于把命令传参的boot命令去掉。
第8行,调用了bootz_start函数。
第15行,调用了bootm_disable_interrupts函数,关闭了中断。
第17行,设置了要启动内核的类型为LINUX。
第18行,调用了do_bootm_states函数执行boot的不同阶段。
bootz_start函数
/*
* zImage booting support
*/
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[], bootm_headers_t *images)
{
int ret;
ulong zi_start, zi_end;
ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START,
images, 1);
/* Setup Linux kernel zImage entry point */
if (!argc) {
images->ep = load_addr;
debug("* kernel: default image load address = 0x%08lx\n",
load_addr);
} else {
images->ep = simple_strtoul(argv[0], NULL, 16);
debug("* kernel: cmdline image address = 0x%08lx\n",
images->ep);
}
ret = bootz_setup(images->ep, &zi_start, &zi_end);
if (ret != 0)
return 1;
lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);
/*
* Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not
* have a header that provide this informaiton.
*/
if (bootm_find_images(flag, argc, argv))
return 1;
#ifdef CONFIG_SECURE_BOOT
extern uint32_t authenticate_image(
uint32_t ddr_start, uint32_t image_size);
if (authenticate_image(images->ep, zi_end - zi_start) == 0) {
printf("Authenticate zImage Fail, Please check\n");
return 1;
}
#endif
return 0;
}
bootz_start函数的作用是完成对uboot启动zImage镜像文件的支持工作的。
第10行,调用do_bootm_states函数完成执行BOOTM_STATE_START阶段。
第18~22行,设置images的成员变量ep即系统镜像的入口地址,并打印出地址。
第24行,调用bootz_setup函数,该函数较为简单,在该函数中获取了镜像文件的zimage_header类型的文件头,并判断当前镜像文件是否为ARM的LINUX镜像。并将镜像文件的入口地址和结束地址分别保存到变量zi_start和zi_end中。
第34行,调用bootm_find_images函数,设置了images->ftaddr(设备树的起始地址)和images->ft_len(设备树的长度)。
do_bootm_states函数
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
int states, bootm_headers_t *images, int boot_progress)
{
boot_os_fn *boot_fn;
ulong iflag = 0;
int ret = 0, need_boot_fn;
images->state |= states;
/*
* Work through the states and see how far we get. We stop on
* any error.
*/
if (states & BOOTM_STATE_START)
ret = bootm_start(cmdtp, flag, argc, argv);
/*****省略部分代码*****/
/* From now on, we need the OS boot function */
if (ret)
return ret;
boot_fn = bootm_os_get_boot_func(images->os.os);
need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
if (boot_fn == NULL && need_boot_fn) {
if (iflag)
enable_interrupts();
printf("ERROR: booting os '%s' (%d) is not supported\n",
genimg_get_os_name(images->os.os), images->os.os);
bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
return 1;
}
if (!ret && (states & BOOTM_STATE_OS_PREP))
ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
#ifdef CONFIG_TRACE
/* Pretend to run the OS, then run a user command */
if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
char *cmd_list = getenv("fakegocmd");
ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
images, boot_fn);
if (!ret && cmd_list)
ret = run_command_list(cmd_list, -1, flag);
}
#endif
/* Check for unsupported subcommand. */
if (ret) {
puts("subcommand not supported\n");
return ret;
}
/* Now run the OS! We hope this doesn't return */
if (!ret && (states & BOOTM_STATE_OS_GO))
ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
images, boot_fn);
/* Deal with any fallout */
err:
if (iflag)
enable_interrupts();
if (ret == BOOTM_ERR_UNIMPLEMENTED)
bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL);
else if (ret == BOOTM_ERR_RESET)
do_reset(cmdtp, flag, argc, argv);
return ret;
}
do_bootm_states函数会跟根据参数states,实现uboot启动内核不同阶段的代码。
第14行处理了BOOTM_STATE_START阶段。在该阶段调用了bootm_start函数,完成了images内存清空和images.verify成员变量设置,并将images.state设置为BOOTM_STATE_START。
第24行,通过函数bootm_os_get_boot_func查找系统的启动函数,参数images->os.os就是在do_bootz函数中设置的IH_OS_LINUX。至此boot_fn这个函数指针指向的就是LINUX的启动函数do_bootm_linux。
第37行,调用do_bootm_linux函数处理BOOTM_STATE_OS_PREP状态,do_bootm_linux函数如下:
int do_bootm_linux(int flag, int argc, char * const argv[],
bootm_headers_t *images)
{
/* No need for those on ARM */
if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
return -1;
if (flag & BOOTM_STATE_OS_PREP) {
boot_prep_linux(images);
return 0;
}
if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
boot_jump_linux(images, flag);
return 0;
}
boot_prep_linux(images);
boot_jump_linux(images, flag);
return 0;
}
在该函数中调用boot_prep_linux处理BOOTM_STATE_OS_PREP状态。在boot_prep_linux函数中主要处理uboot用于启动linux的环境变量bootargs。
第42行,是处理BOOTM_STATE_OS_FAKE_GO状态,由于没有定义宏因此不会执行。
第59行,是处理BOOTM_STATE_OS_GO状态,即开始启动内核,调用boot_selected_os函数->调用boot_fn(do_bootm_linux)函数->boot_jump_linux函数,函数定义如下:
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
/*省略64位代码*/
unsigned long machid = gd->bd->bi_arch_number;
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;
s = getenv("machid");
if (s) {
if (strict_strtoul(s, 16, &machid) < 0) {
debug("strict_strtoul failed!\n");
return;
}
printf("Using machid 0x%lx from environment\n", machid);
}
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;
if (!fake) {
#ifdef CONFIG_ARMV7_NONSEC
if (armv7_boot_nonsec()) {
armv7_init_nonsec();
secure_ram_addr(_do_nonsec_entry)(kernel_entry,
0, machid, r2);
} else
#endif
kernel_entry(0, machid, r2);
}
#endif
}
第5行,定义了变量machid,不采用设备树此变量uboot会传递给linux内核,内核会在机器ID表中查找是否支持该机器。如果采用设备树,该变量则无效。
第7行,定义了一个函数指针kernel_entry,该函数有3个参数,第一个参数为0,第二个参数是机器ID,第三个参数为ATAGS 或者设备树(DTB)首地址。
第11行,获取kernel_entry函数,该函数为linux内核定义,是linux镜像的第一行代码。
第27行,如果使用设备树将r2寄存器的值赋值为设备树首地址。
第30行,不适用设备树,r2就是bootargs参数首地址。
最后第40行,调用kernel_entry函数启动linux内核。
至此uboot所有工作都已经结束!