记录自己学习android系统启动以及 recovery过程(1)----------uboot
欢迎指出其中错误。
启动uboot bootm文件作为uboot->kernel的最后一个步骤,负责引导内核,并配置需要传递进内核的相关参数
在uboot启动后,会检查是否需要recovery,一般通过两个途径, 1.挂载boot device,并查看/cache/recovery文件,2.按键检测,当需要进行recovery时,会重新设置启动的环境变量。
system image和recovery的环境变量主要区别在于:
system : bootm (kernel_addr) (ramdisk_addr)
recovery: bootm或者bootm (kernel_addr) root=/dev/mmcblk0px
uboot阶段通过对bootm代码的执行,来设置传递进内核的参数,通知内核是挂载recovery 还是运行system。
相关代码分析:
1. do_bootm函数
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong load_end = 0;
int ret;
boot_os_fn *boot_fn;
/* relocate boot function table */
/*重定位 boot_os内部函数指针地址,启动包含do_bootm_linux。因为在uboot启动的最初阶段,会对uboot的text,data,bss..进行一次搬移*/
if (!relocated) {
int i;
for (i = 0; i < ARRAY_SIZE(boot_os); i++)
if (boot_os[i] != NULL)
boot_os[i] += gd->reloc_off;
relocated = 1;
}
/* determine if we have a sub command */
/* 当传进来参数多于一个时,判断参数类型,并执行。在android正常启动模式下,参数模式是 bootm kernel_addr rd_addr,多于3个,但是参数endp是0,不会return*/
if (argc > 1) {
char *endp;
simple_strtoul(argv[1], &endp, 16);
/* endp pointing to NULL means that argv[1] was just a
* valid number, pass it along to the normal bootm processing
*
* If endp is ':' or '#' assume a FIT identifier so pass
* along for normal processing.
*
* Right now we assume the first arg should never be '-'
*/
if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
return do_bootm_subcommand(cmdtp, flag, argc, argv);
}
/* do_bootm重要函数,主要用来查看,验证 uImage, ramdisk,并初始化一个结构体images,类型为bootm_headers_t,后续会详细解析*/
if (bootm_start(cmdtp, flag, argc, argv))
return 1;
/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
* recover from any failures any more...
*/
iflag = disable_interrupts();
#if defined(CONFIG_CMD_USB)
/*
* turn off USB to prevent the host controller from writing to the
* SDRAM while Linux is booting. This could happen (at least for OHCI
* controller), because the HCCA (Host Controller Communication Area)
* lies within the SDRAM and the host controller writes continously to
* this area (as busmaster!). The HccaFrameNumber is for example
* updated every 1 ms within the HCCA structure in SDRAM! For more
* details see the OpenHCI specification.
*/
usb_stop();
#endif
#ifdef CONFIG_AMIGAONEG3SE
/*
* We've possible left the caches enabled during
* bios emulation, so turn them off again
*/
icache_disable();
dcache_disable();
#endif
/*下面函数,是用来将kernel的uImage 解压并拷贝到指定的运行地址*/
ret = bootm_load_os(images.os, &load_end, 1);
if (ret < 0) {
if (ret == BOOTM_ERR_RESET)
do_reset (cmdtp, flag, argc, argv);
if (ret == BOOTM_ERR_OVERLAP) {
if (images.legacy_hdr_valid) {
if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
puts ("WARNING: legacy format multi component "
"image overwritten\n");
} else {
puts ("ERROR: new format image overwritten - "
"must RESET the board to recover\n");
show_boot_progress (-113);
do_reset (cmdtp, flag, argc, argv);
}
}
if (ret == BOOTM_ERR_UNIMPLEMENTED) {
if (iflag)
enable_interrupts();
show_boot_progress (-7);
return 1;
}
}
lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));
if (images.os.type == IH_TYPE_STANDALONE) {
if (iflag)
enable_interrupts();
/* This may return when 'autostart' is 'no' */
bootm_start_standalone(iflag, argc, argv);
return 0;
}
show_boot_progress (8);
#ifdef CONFIG_SILENT_CONSOLE
if (images.os.os == IH_OS_LINUX)
fixup_silent_linux();
#endif
/*image.os.os在bootm_start函数中被初始化,此处,boot_fn就等于do_bootm_linux*/
boot_fn = boot_os[images.os.os];
if (boot_fn == NULL) {
if (iflag)
enable_interrupts();
printf ("ERROR: booting os '%s' (%d) is not supported\n",
genimg_get_os_name(images.os.os), images.os.os);
show_boot_progress (-8);
return 1;
}
/*调用do_bootm_linux引导内核并启动*/
boot_fn(0, argc, argv, &images);
show_boot_progress (-9);
#ifdef DEBUG
puts ("\n## Control returned to monitor - resetting...\n");
#endif
do_reset (cmdtp, flag, argc, argv);
return 1;
}各个大体部分的解释如上,在do_bootm函数中,需要详细看的函数有
bootm_start-------相关结构体初始化
bootm_load_os--------解压
do_bootm_linux---------引导内核启动
2. bootm_start
首先看bootm_hearders_t结构体
/*
* Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()
* routines.
*/
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 */
#if defined(CONFIG_PPC)
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 */
#endif
#endif
#ifndef USE_HOSTCC /*需要认真查看如何完成这个宏定义包含的部分*/
image_info_t os; /* os image info */ /*linux image 相关*/
ulong ep; /* entry point of OS */ /* 解压后,指向image入口*/
ulong rd_start, rd_end;/* ramdisk start/end */ /* ramdisk,用来引导systemimage,当为0时,引导recovery,kernel里有实现方法,会另写*/
#ifdef CONFIG_OF_LIBFDT
char *ft_addr; /* flat dev tree address */
#endif
ulong ft_len; /* length of flat device tree */
ulong initrd_start;
ulong initrd_end;
ulong cmdline_start; /*command line 相关*/
ulong cmdline_end;
bd_t *kbd;
#endif
int verify; /* getenv("verify")[0] != 'n' */
#define BOOTM_STATE_START (0x00000001)
#define BOOTM_STATE_LOADOS (0x00000002)
#define BOOTM_STATE_RAMDISK (0x00000004)
#define BOOTM_STATE_FDT (0x00000008)
#define BOOTM_STATE_OS_CMDLINE (0x00000010)
#define BOOTM_STATE_OS_BD_T (0x00000020)
#define BOOTM_STATE_OS_PREP (0x00000040)
#define BOOTM_STATE_OS_GO (0x00000080)
int state;
#ifndef USE_HOSTCC
struct lmb lmb; /* for memory mgmt */
#endif
} bootm_headers_t;主要需要注意ifndef USE_HOSTCC宏内部结构体或变量的初始化过程,关系到内核的加载以及ramdisk的引导
image_header结构体
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;
bootm_start函数如下:
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong mem_start;
phys_size_t mem_size;
void *os_hdr;
int ret;
memset ((void *)&images, 0, sizeof (images));
images.verify = getenv_yesno ("verify");
/*lmb相关代码,后续研究,暂时也搞不懂到底干啥以及用来干啥的*/
lmb_init(&images.lmb);
mem_start = getenv_bootm_low();
mem_size = getenv_bootm_size();
lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size);
arch_lmb_reserve(&images.lmb);
board_lmb_reserve(&images.lmb);
/* 重要函数,获取内核uImage信息,返回uImage头,并初始化image_start,image_len,以及images内部相关参数*/
/* get kernel image header, start address and length */
os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
&images, &images.os.image_start, &images.os.image_len);
if (images.os.image_len == 0) {
puts ("ERROR: can't get kernel image!\n");
return 1;
}
/* get image parameters */
switch (genimg_get_format (os_hdr)) {
case IMAGE_FORMAT_LEGACY:
images.os.type = image_get_type (os_hdr); /*初始化bootm_headers_t结构体images.os内部参数,在打包image至uImage时,设置的*/
images.os.comp = image_get_comp (os_hdr);
images.os.os = image_get_os (os_hdr);
images.os.end = image_get_image_end (os_hdr);
images.os.load = image_get_load (os_hdr);
break;
#if defined(CONFIG_FIT)
case IMAGE_FORMAT_FIT:
if (fit_image_get_type (images.fit_hdr_os,
images.fit_noffset_os, &images.os.type)) {
puts ("Can't get image type!\n");
show_boot_progress (-109);
return 1;
}
if (fit_image_get_comp (images.fit_hdr_os,
images.fit_noffset_os, &images.os.comp)) {
puts ("Can't get image compression!\n");
show_boot_progress (-110);
return 1;
}
if (fit_image_get_os (images.fit_hdr_os,
images.fit_noffset_os, &images.os.os)) {
puts ("Can't get image OS!\n");
show_boot_progress (-111);
return 1;
}
images.os.end = fit_get_end (images.fit_hdr_os);
if (fit_image_get_load (images.fit_hdr_os, images.fit_noffset_os,
&images.os.load)) {
puts ("Can't get image load address!\n");
show_boot_progress (-112);
return 1;
}
break;
#endif
default:
puts ("ERROR: unknown image format type!\n");
return 1;
}
/* find kernel entry point */
/* 获取enter point*/
if (images.legacy_hdr_valid) {
images.ep = image_get_ep (&images.legacy_hdr_os_copy);
#if defined(CONFIG_FIT)
} else if (images.fit_uname_os) {
ret = fit_image_get_entry (images.fit_hdr_os,
images.fit_noffset_os, &images.ep);
if (ret) {
puts ("Can't get entry point property!\n");
return 1;
}
#endif
} else {
puts ("Could not find kernel entry point!\n");
return 1;
}
if (images.os.os == IH_OS_LINUX) {
/* find ramdisk */
/*当是linux类型时,会查找ramdisk,当ramdisk存在时,rd_start,rd_end为实际值,否则为0,在kernel中,会判断这两个值,用来确认挂载root*/
ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,
&images.rd_start, &images.rd_end);
if (ret) {
puts ("Ramdisk image is corrupt or invalid\n");
return 1;
}
#if defined(CONFIG_OF_LIBFDT)
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_SPARC)
/* find flattened device tree */
ret = boot_get_fdt (flag, argc, argv, &images,
&images.ft_addr, &images.ft_len);
if (ret) {
puts ("Could not find a valid device tree\n");
return 1;
}
set_working_fdt_addr(images.ft_addr);
#endif
#endif
}
images.os.start = (ulong)os_hdr;
images.state = BOOTM_STATE_START;
return 0;
}
在这个bootm_start函数中,主要是boot_get_kernel和boot_get_ramdisk两个函数,用来校验并完成image结构体的初始化。
boot_get_kernel
static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
bootm_headers_t *images, ulong *os_data, ulong *os_len)
{
image_header_t *hdr;
ulong img_addr;
#if defined(CONFIG_FIT)
void *fit_hdr;
const char *fit_uname_config = NULL;
const char *fit_uname_kernel = NULL;
const void *data;
size_t len;
int cfg_noffset;
int os_noffset;
#endif
/* find out kernel image address */
if (argc < 2) {
/*当小于2时,使用默认的addr配置*/
img_addr = load_addr;
debug ("* kernel: default image load address = 0x%08lx\n",
load_addr);
#if defined(CONFIG_FIT)
} else if (fit_parse_conf (argv[1], load_addr, &img_addr,
&fit_uname_config)) {
debug ("* kernel: config '%s' from image at 0x%08lx\n",
fit_uname_config, img_addr);
} else if (fit_parse_subimage (argv[1], load_addr, &img_addr,
&fit_uname_kernel)) {
debug ("* kernel: subimage '%s' from image at 0x%08lx\n",
fit_uname_kernel, img_addr);
#endif
} else {
/*使用传递进来的image_addr进行初始化*/
img_addr = simple_strtoul(argv[1], NULL, 16);
debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr);
}
show_boot_progress (1);
/* copy from dataflash if needed */
/*genimg_get_image是判断镜像是否存在于特殊的存储设备中,是否需要读取至指定内存位置,不管需不需要,在这步完成后,内存img_addr就存在了内核镜像,并在后面对其进行解压缩等*/
img_addr = genimg_get_image (img_addr);
/* check image type, for FIT images get FIT kernel node */
*os_data = *os_len = 0;
switch (genimg_get_format ((void *)img_addr)) {
case IMAGE_FORMAT_LEGACY:
printf ("## Booting kernel from Legacy Image at %08lx ...\n",
img_addr);
/*验证image的CRC校验,并返回image头部*/
hdr = image_get_kernel (img_addr, images->verify);
if (!hdr)
return NULL;
show_boot_progress (5);
/* get os_data and os_len */
switch (image_get_type (hdr)) {
case IH_TYPE_KERNEL:
/*设置image中,除去头部分,真实kernel data的起始地址,以及长度*/
*os_data = image_get_data (hdr);
*os_len = image_get_data_size (hdr);
break;
case IH_TYPE_MULTI:
image_multi_getimg (hdr, 0, os_data, os_len);
break;
case IH_TYPE_STANDALONE:
if (argc >2) {
hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
}
*os_data = image_get_data (hdr);
*os_len = image_get_data_size (hdr);
break;
default:
printf ("Wrong Image Type for %s command\n", cmdtp->name);
show_boot_progress (-5);
return NULL;
}
/*
* copy image header to allow for image overwrites during kernel
* decompression.
*/
memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t));
/* save pointer to image header */
images->legacy_hdr_os = hdr;
images->legacy_hdr_valid = 1;
show_boot_progress (6);
break;
#if defined(CONFIG_FIT)
case IMAGE_FORMAT_FIT:
fit_hdr = (void *)img_addr;
printf ("## Booting kernel from FIT Image at %08lx ...\n",
img_addr);
if (!fit_check_format (fit_hdr)) {
puts ("Bad FIT kernel image format!\n");
show_boot_progress (-100);
return NULL;
}
show_boot_progress (100);
if (!fit_uname_kernel) {
/*
* no kernel image node unit name, try to get config
* node first. If config unit node name is NULL
* fit_conf_get_node() will try to find default config node
*/
show_boot_progress (101);
cfg_noffset = fit_conf_get_node (fit_hdr, fit_uname_config);
if (cfg_noffset < 0) {
show_boot_progress (-101);
return NULL;
}
/* save configuration uname provided in the first
* bootm argument
*/
images->fit_uname_cfg = fdt_get_name (fit_hdr, cfg_noffset, NULL);
printf (" Using '%s' configuration\n", images->fit_uname_cfg);
show_boot_progress (103);
os_noffset = fit_conf_get_kernel_node (fit_hdr, cfg_noffset);
fit_uname_kernel = fit_get_name (fit_hdr, os_noffset, NULL);
} else {
/* get kernel component image node offset */
show_boot_progress (102);
os_noffset = fit_image_get_node (fit_hdr, fit_uname_kernel);
}
if (os_noffset < 0) {
show_boot_progress (-103);
return NULL;
}
printf (" Trying '%s' kernel subimage\n", fit_uname_kernel);
show_boot_progress (104);
if (!fit_check_kernel (fit_hdr, os_noffset, images->verify))
return NULL;
/* get kernel image data address and length */
if (fit_image_get_data (fit_hdr, os_noffset, &data, &len)) {
puts ("Could not find kernel subimage data!\n");
show_boot_progress (-107);
return NULL;
}
show_boot_progress (108);
*os_len = len;
*os_data = (ulong)data;
images->fit_hdr_os = fit_hdr;
images->fit_uname_os = fit_uname_kernel;
images->fit_noffset_os = os_noffset;
break;
#endif
default:
printf ("Wrong Image Format for %s command\n", cmdtp->name);
show_boot_progress (-108);
return NULL;
}
debug (" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n",
*os_data, *os_len, *os_len);
return (void *)img_addr;
}
在boot_get_kernel函数中,会涉及kernel image的校验,并对os_data,os_len进行初始化,并返回image header,用作后续操作。
boot_get_ramdisk
此函数与boot_get_kernel类似,校验并初始化rd_start,rd_end,最终这两个参数会被传递到linux内核,挂载root目录用。
因为在正常启动模式下,传进的参数是bootm (kernel_addr) (rd_addr) 因此,boot_get_kernel可以通过addr得到kernel和rd的image,并对他们的文件头部分进行分析,初始化相关变量,初始化rd_start,rd_end后,kernel会对这两个参数进行分析,并挂载相应的root目录,并执行相应的init进程。
当在recovery模式下,传递进的参数是bootm (kernel_addr)或者是bootm,没有kernel_addr模式下,会使用环境变量默认的值,进行image配置。但ramdisk的没有默认值,image也是找不到的,所以被初始化为0。此时root的挂载就会走到另外一种模式,通过传递的commandline中加入 root=/dev/mmcblk0px时,kernel会对这个cmd进行分析,并挂载,后续kernel会继续讨论。
这两种初始化rd的模式,是kernel两种常规配置的模式
3.boot_load_os
static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)
{
uint8_t comp = os.comp;
ulong load = os.load;
ulong blob_start = os.start;
ulong blob_end = os.end;
ulong image_start = os.image_start;
ulong image_len = os.image_len;
uint unc_len = CONFIG_SYS_BOOTM_LEN;
const char *type_name = genimg_get_type_name (os.type);
/*通过image头配置情况,查看压缩与否以及压缩的模式,并解压或者搬移至enter point,ep的初始化在boot_get_kernel中设置的*/
switch (comp) {
case IH_COMP_NONE:
if (load == blob_start) {
printf (" XIP %s ... ", type_name);
} else {
printf (" Loading %s ... ", type_name);
if (load != image_start) {
memmove_wd ((void *)load,
(void *)image_start, image_len, CHUNKSZ);
}
}
*load_end = load + image_len;
puts("OK\n");
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", type_name);
if (gunzip ((void *)load, unc_len,
(uchar *)image_start, &image_len) != 0) {
puts ("GUNZIP: uncompress, out-of-mem or overwrite error "
"- must RESET board to recover\n");
if (boot_progress)
show_boot_progress (-6);
return BOOTM_ERR_RESET;
}
*load_end = load + image_len;
break;
#ifdef CONFIG_BZIP2
case IH_COMP_BZIP2:
printf (" Uncompressing %s ... ", type_name);
/*
* If we've got less than 4 MB of malloc() space,
* use slower decompression algorithm which requires
* at most 2300 KB of memory.
*/
int i = BZ2_bzBuffToBuffDecompress ((char*)load,
&unc_len, (char *)image_start, image_len,
CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
if (i != BZ_OK) {
printf ("BUNZIP2: uncompress or overwrite error %d "
"- must RESET board to recover\n", i);
if (boot_progress)
show_boot_progress (-6);
return BOOTM_ERR_RESET;
}
*load_end = load + unc_len;
break;
#endif /* CONFIG_BZIP2 */
#ifdef CONFIG_LZMA
case IH_COMP_LZMA:
printf (" Uncompressing %s ... ", type_name);
int ret = lzmaBuffToBuffDecompress(
(unsigned char *)load, &unc_len,
(unsigned char *)image_start, image_len);
if (ret != SZ_OK) {
printf ("LZMA: uncompress or overwrite error %d "
"- must RESET board to recover\n", ret);
show_boot_progress (-6);
return BOOTM_ERR_RESET;
}
*load_end = load + unc_len;
break;
#endif /* CONFIG_LZMA */
default:
printf ("Unimplemented compression type %d\n", comp);
return BOOTM_ERR_UNIMPLEMENTED;
}
puts ("OK\n");
debug (" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);
if (boot_progress)
show_boot_progress (7);
if ((load < blob_end) && (*load_end > blob_start)) {
debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end);
debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end);
return BOOTM_ERR_OVERLAP;
}
return 0;
}
boot_load_os函数功能以及流程都比较清晰。
4.do_bootm_linux
在do_bootm中,函数指针被赋值为boot_fn = boot_os[images.os.os];
images.os.os赋值在bootm_get_kernel中,并被设置为IH_OS_LINUX,
boot_os结构体为:
boot_os_fn * boot_os[] = {
#ifdef CONFIG_BOOTM_LINUX
[IH_OS_LINUX] = do_bootm_linux, /*需要的函数*/
#endif
#ifdef CONFIG_BOOTM_NETBSD
[IH_OS_NETBSD] = do_bootm_netbsd,
#endif
#ifdef CONFIG_LYNXKDI
[IH_OS_LYNXOS] = do_bootm_lynxkdi,
#endif
#ifdef CONFIG_BOOTM_RTEMS
[IH_OS_RTEMS] = do_bootm_rtems,
#endif
#if defined(CONFIG_CMD_ELF)
[IH_OS_VXWORKS] = do_bootm_vxworks,
[IH_OS_QNX] = do_bootm_qnxelf,
#endif
#ifdef CONFIG_INTEGRITY
[IH_OS_INTEGRITY] = do_bootm_integrity,
#endif
};do_boot_linux函数为:
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
bd_t *bd = gd->bd;
char *s;
int machid = bd->bi_arch_number;
void (*theKernel)(int zero, int arch, uint params);
#ifdef CONFIG_CMDLINE_TAG
/* add core board serial number temp solution*/
char commandline[256] ;
memset(commandline , 0, sizeof(commandline));
strcat(commandline,getenv ("bootargs"));
strcat(commandline,get_inand_cid_psn());
printf("COMMANDLINE:%s\n",commandline);
#endif
if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
return 1;
/*将函数的指针指向kernel起始地址,这样,运行这个函数指针,就相当于执行kernel了*/
theKernel = (void (*)(int, int, uint))images->ep;
s = getenv ("machid");
if (s) {
machid = simple_strtoul (s, NULL, 16);
printf ("Using machid 0x%x from environment\n", machid);
}
show_boot_progress (15);
debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) theKernel);
/*下面是配置需要传递到kernel的参数,其中跟启动相关的主要有两个,一个command_line,一个就是刚才的rd_start,rd_end*/
#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); /*配置commandline*/
#endif
#ifdef CONFIG_INITRD_TAG
if (images->rd_start && images->rd_end)
setup_initrd_tag (bd, images->rd_start, images->rd_end); /*配置initrd相关的,但是当rd_start,rd_end为0时,值为0*/
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif
/* we assume that the kernel is in place */
printf ("\nStarting kernel ...\n\n");
#ifdef CONFIG_USB_DEVICE
{
extern void udc_disconnect (void);
udc_disconnect ();
}
#endif
cleanup_before_linux ();
/*运行kernel,像所说的,never return*/
theKernel (0, machid, bd->bi_boot_params);
/* does not return */
return 1;
}
一般配置参数的做法,可以参考上面的两种方法,在内核中解析,并在启动阶段做你想做的。。。。如上所述:
1. 在uboot阶段,通过对是否需要运行recovery的检测,来设置不同的环境变量
2. 通过对环境变量的执行,加载kernel(ramdisk),并进行校验,检测,解压等操作,搬移到相应的位置
3.对ramdisk的操作,初始化rd_start,rd_end,并传递进内核,这样,内核就可以通过这两个参数,来决定是否存在ramdisk,如存在,就挂载并运行,入不存在,则检测是否传递进commandline中有root=字样,分析并挂载运行。
这样,recovery以及android system启动的模式,相同点与不同点,大致如此。
kernel阶段另行研究
本文详细介绍了Android系统启动过程中uboot如何引导内核,并配置进入system或recovery模式的参数。uboot会检查是否需要recovery,通过挂载boot device或按键检测。在不同模式下,bootm传递给内核的参数不同,影响root目录的挂载。文中深入剖析了do_bootm函数、bootm_start和相关结构体,以及在recovery和system模式下ramdisk的处理方式,阐述了uboot如何为kernel提供启动信息。
425

被折叠的 条评论
为什么被折叠?



