0nism

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

u-boot kernel启动

  目前uboot已经可以在开发板上成功使用了,下一步,就是最关键的一步了——加载Linux内核到DDR上并启动内核。

一、加载内核并启动

  uboot加载内核的方法一般分为两种。

1.SD卡加载

  如果kernel在SD卡中,那么需要使用movi read kernel 30008000将内核加载到DDR上0x30008000的位置。
  uboot如何知道kernel在SD卡的那个扇区呢?根据iNand分区表得到的。

2.tftp加载

  确保网络联通后,可以在虚拟机下构建tftp客户端实现烧录,加载时采用命令tftp 0x30008000 zImage-qt就可以完成内核到DDR的加载。

3.启动

  可以使用bootm 0x30008000启动内核。

二、uImage与zImage

  比较u-boot以及u-boot.bin

u-boot
u-boot

u-boot.bin
u-boot.bin

  可以看到直接连接编译得到的u-boot文件为ELF格式,是Linux下的可执行文件。值得注意的是该文件属于not stripped状态。该状态说明这个程序没有剔除符号表信息。用objcopy剔除之后生成文件u-boot.bin,可以烧录。
  剔除符号表信息前,文件有956KB,而剔除后,仅剩下384K。很明显,小体积的文件更适合烧录。
  Linux内核经过编译也会生成elf可执行程序,一般叫做vmlinuxvmlinuz。但是这个内核文件太大了,经过strip之后,就产生了Image文件。其实这个Image文件已经可以被烧录执行了。但是为了进一步压缩成本,所以Linux参与者再对这个Image进行了压缩,并且在压缩后的文件前端,加入了一部分解压缩代码。构成了压缩格式的zImage。
  uboot为了启动内核,就又发明了一种内核格式——uImage。加工的过程其实就是用uboot中的mkimage工具再zImage前加上64Bytes的头信息即可。


  在kernel目录下,执行make x210ii_qt_defconfig即可完成内核编译第一步配置,接下来使用make menuconfig进行内核模块化删改,最后make,即可得到zImage。

三、do_bootm分析

  do_bootm是执行bootm的主要函数。

关键结构体image_header_tbootm_headers_t
  • image_header_t
/*
 * Legacy format image header,
 * all data in network byte order (aka natural aka bigendian).
 */
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_headers_t
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;

	int		verify;		/* getenv("verify")[0] != 'n' */
	struct lmb	*lmb;		/* for memory mgmt */
} bootm_headers_t;
流程图

  从这个流程图中可以看出,do_bootm函数至少经历了三次大改。第一次是初始的情况下,使用的是uImage。后来因为内核的更新,用条件编译在判断uImage原本的函数里加入设备树的相关配置。最后一次,则是直接独立出来判断zImage,用goto语句跳转。
  所以目前uboot支持三种镜像加载方式,分别为zImageuImage设备树

zImage
	if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
		printf("Boot with zImage\n");
		addr = virt_to_phys(addr);
		hdr = (image_header_t *)addr;
		hdr->ih_os = IH_OS_LINUX;
		hdr->ih_ep = ntohl(addr);

		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;

zImage加载
zImage加载

uImage
  • boot_get_kernel
      检查输入参数,然后获取image的format type,并打印信息。经过image_check_magic,检测37到40字节的数是否是0x27051956,打印出检测到的Image格式,1代表uImage。
      因为是uImage格式,所以输出一些信息。
      接着使用image_get_type获得OS相关信息。
      image_get_kernel,先打印image的信息,然后确认内核的crc校验。并输出。
      设置bootm_headers结构体中的legacy_hdr_oslegacy_hdr_valid
    format
    加载地址
    image信息以及crc校验

  • genimg_get_format
      判断image的格式,然后获取OS相关信息。例如image的类型,压缩方式,OS名称,image_start,image_end,image_size,load_start,load_end。
    format

  • 判断压缩格式
      判断未使用压缩,所以做相应输出。
    压缩格式判断

  • 检查加载地址与Image地址

	if ((load_start < image_end) && (load_end > image_start)) {
		debug ("image_start = 0x%lX, image_end = 0x%lx\n", image_start, image_end);
		debug ("load_start = 0x%lx, load_end = 0x%lx\n", load_start, load_end);

		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);
		}
	}
四、do_bootm_linux分析

  do_bootm函数最终执行do_bootm_linux来完成内核加载的最后一步。

tag结构体
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;
                struct tag_mtdpart      mtdpart_info;
        } u;
};

  tag_header中有这个tag的size和类型编码,kernel拿到一个tag后先分析tag_header得到tag的类型和大小,然后将tag中剩余部分当作一个tag_xxx来处理。
  内核传参是依靠上述结构体实现的。传参时,实际为一个结构体序列,以tag_start开始,以tag_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) || \
    defined (CONFIG_MTDPARTITION)
	setup_start_tag (bd);
#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
#ifdef CONFIG_MTDPARTITION
	setup_mtdpartition_tag();
#endif
	setup_end_tag (bd);
#endif

  CONFIG_SETUP_MEMORY_TAGS,tag_mem,传参内容是内存配置信息。
  CONFIG_CMDLINE_TAG,tag_cmdline,传参内容是启动命令行参数,也就是uboot环境变量的bootargs.
(  CONFIG_INITRD_TAG
  CONFIG_MTDPARTITION,传参内容是iNand/SD卡的分区表。
  起始tag是ATAG_CORE、结束tag是ATAG_NONE,其他的ATAG_XXX都是有效信息tag。

如何传参

  先打印Starting kernel ...,然后开始准备传参。

//	函数指针
void	(*theKernel)(int zero, int arch, uint params);

//	entry_point
theKernel = (void (*)(int, int, uint))ep;

//	开始执行内核,u-boot结束
theKernel (0, machid, bd->bi_boot_params);
/* does not return */
return;

  uboot最终是调用theKernel函数来执行linux内核的,uboot调用这个函数(其实就是linux内核)时传递了3个参数。这3个参数就是uboot直接传递给linux内核的3个参数,通过寄存器来实现传参的。(第1个参数就放在r0中,第二个参数放在r1中,第3个参数放在r2中)第1个参数固定为0,第2个参数是机器码,第3个参数传递的就是大片传参tag的首地址。

posted on 2020-02-28 22:05  0nism  阅读(674)  评论(0编辑  收藏  举报