005uboot启动内核

        进入uboot后,输入printenv可以看到一个环境变量,bootcmd,这个环境变量里面内容的作用就是把内核从NandFlash中读取到SDRAM中,然后从SDRAM的加载地址启动内核。

一、内核是什么

        内核实际上就是一个比较复杂庞大的裸板程序,只不过是内核无法自启动,必须借助boot来启动,内核运行起来后,在软件分层上分成了应用层和内核层,内核层可以随意的访问硬件,应用层无法随意的访问硬件资源,想要访问硬件资源必须open后产生软中断进入内核层。

         内核的镜像文件是uImage,uImage是一个信息头+一个内核文件

#define IH_NMLEN		32	/* Image Name Length		*/
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;	/* 加载地址		*/
	uint32_t	ih_ep;		/* 入口地址		*/
	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;

        这个结构体就是uImage的头部信息,一共64字节,所以启动内核时要偏移64字节

二、嵌入式linux分区

        一个完整的嵌入式系统,必须有bootloader、环境变量、kernel、rootfs等内容,他们都以镜像文件的形式存储在FLASH中,运行时重定位到SDRAM中。

        嵌入式系统的分区,并不像PC机的分区一样,他的分区是写死的,记录在include/configs中

#define MTDIDS_DEFAULT "nand0=nandflash0"
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \
                            "128k(params)," \
                            "2m(kernel)," \
                            "-(root)"

三、uboot内核启动过程

1.启动内核第一步:加载内核到SDRAM中

        内核镜像存储在NAND FLash的Kernel分区中,需要此时需要把真正的内核从FLASH读取到SDRAM的0x30008000这个地址。

        但是我们查看uboot的环境变量,

        加载内核到SDRAM中的指令是nand read.jffs2 0x30007FC0 kernel,这不对啊,地址不应该是0x30008000吗?因为内核镜像文件的头部占据的64字节,这样的话,真正的内核文件就读取到了0x30008000这个地址了。

2.启动内核

2.1校验内核的格式:uImage、Image、 zImage

        内核有三种格式:uImage、Image、 zImage

        Linux内核经过编译会生成一个elf格式的可重定位的文件,然后经过编译器的objcopy工具会生成一个可以烧录的镜像文件Image,这个制作烧录镜像主要目的就是缩减大小,节省磁盘。

        zImage就是在Image的基础上再进行压缩

        uImage是由zImage加工得到的,uboot中有一个工具,可以将zImage加上64字节的信息头生成uImage。

        注意:uImage不关linux内核的事,linux内核只管生成zImage即可

        在com_bootm.c的do_bootm函数中进行镜像文件的校验,校验成功才可以加载

	data = (ulong)&header;
	len  = sizeof(image_header_t);

	checksum = ntohl(hdr->ih_hcrc);
	hdr->ih_hcrc = 0;

	if (crc32 (0, (uchar *)data, len) != checksum) {
		puts ("Bad Header Checksum\n");
		SHOW_BOOT_PROGRESS (-2);
		return 1;
	}
	SHOW_BOOT_PROGRESS (3);

#ifdef CONFIG_HAS_DATAFLASH
	if (addr_dataflash(addr)){
		len  = ntohl(hdr->ih_size) + sizeof(image_header_t);
		read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
		addr = CFG_LOAD_ADDR;
	}
#endif


	/* for multi-file images we need the data part, too */
	print_image_hdr ((image_header_t *)addr);

	data = addr + sizeof(image_header_t);
	len  = ntohl(hdr->ih_size);

	if (verify) {
		puts ("   Verifying Checksum ... ");
		if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
			printf ("Bad Data CRC\n");
			SHOW_BOOT_PROGRESS (-3);
			return 1;
		}
		puts ("OK\n");
	}
	SHOW_BOOT_PROGRESS (4);

	len_ptr = (ulong *)data;

2.2启动内核

       bootm 0x30007FC0 启动内核 bootm这个指令会将内核移动到加载地址,然后跳到入口地址

                加载地址:内核镜像整体要放置的内存空间位置

                入口地址:从入口地址开始执行内核代码(一般是_start指示的tag位置)

         bootm命令可以分成两个部分

                1.移动内核到内核的加载地址(uImage头部定义了in_load)

                2.启动内核。调用do_boot_linux()函数。该函数会设置启动参数(RAM大小等),然后跳到入口地址。

                2.1设置启动参数,在armlinux.c的do_bootm_linux()中有如下代码

#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 (&params);
#endif
#ifdef CONFIG_REVISION_TAG
	setup_revision_tag (&params);
#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

	/* we assume that the kernel is in place */
	printf ("\nStarting kernel ...\n\n");

               其中的setup_xxxx_tag(db)就是用来设置各种启动参数,uboot会和内核约定一个位置用          来存放这些参数。

                 当启动参数设置后,在0x30000100的位置上会开始保存这些参数
               

               2.2启动内核

                 

void (*theKernel)(int zero, int arch, uint params);
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值