记录自己学习android系统启动以及 recovery过程(1)----------uboot

本文详细介绍了Android系统启动过程中uboot如何引导内核,并配置进入system或recovery模式的参数。uboot会检查是否需要recovery,通过挂载boot device或按键检测。在不同模式下,bootm传递给内核的参数不同,影响root目录的挂载。文中深入剖析了do_bootm函数、bootm_start和相关结构体,以及在recovery和system模式下ramdisk的处理方式,阐述了uboot如何为kernel提供启动信息。

记录自己学习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阶段另行研究

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值