u-boot启动内核文件

在uboot命令行print查看系统的启动命令结果是:bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0。这条命令的意思是,从nandflash上面的kernel分区把内核读到SDRAM的ox30007FC0地址,然后从这个地址启动。

在我们的PC上,每个硬盘前面会有一个分区表,但是在嵌入式Linux里面,Flash是没有分区表的,那我们的flash里面的boot env kernel 跟文件系统这些分区只能在源码中写死,所以我们不关心falsh里面这些分区的名字,而是这些分区的地址,那么看一下在哪里写死的。

这里面定义了mtd分区,这个分区位于nandflash上面,前面从0开始的256K是bootloader,接下来的128k存放的是环境变量参数,接下来的2m是kernel,剩下的东西是跟文件系统。对于这些分区,名字不重要,重要的是他们的起始地址和大小,这些东西是在代码里面写死的。

OpenJTAG> mtd

device nand0 <nandflash0>, # parts = 4
 #: name                        size            offset          mask_flags
 0: bootloader          0x00040000      0x00000000      0
 1: params              0x00020000      0x00040000      0
 2: kernel              0x00200000      0x00060000      0
 3: root                0x0fda0000      0x00260000      0

active partition: nand0,0 - (bootloader) 0x00040000 @ 0x00000000

defaults:
mtdids  : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)

bootm 0x300007FC0地址,这个地址放uImage文件。文件中包含头信息+内核文件。其中头文件格式:

/*
 * 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;

内核启动打印信息:

## Booting image at 30007fc0 ...
   Image Name:   Linux-2.6.22.6
   Created:      2022-03-23   8:02:24 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    1848720 Bytes =  1.8 MB
   Load Address: 30008000
   Entry Point:  30008000
   Verifying Checksum ... OK
   XIP Kernel Image ... OK

Starting kernel ...

Uncompressing Linux...................................................................................................................... done, booting the kernel.
Linux version 2.6.22.6 (book@book-virtual-machine) (gcc version 3.4.5) #1 Wed Mar 23 16:02:20 CST 2022
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
Machine: SMDK2440
Memory policy: ECC disabled, Data cache writeback
CPU S3C2440A (id 0x32440001)
S3C244X: core 400.000 MHz, memory 100.000 MHz, peripheral 50.000 MHz
S3C24XX Clocks, (c) 2004 Simtec Electronics
CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on
CPU0: D VIVT write-back cache
CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets
CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets
Built 1 zonelists.  Total pages: 16256
Kernel command line: noinitrd root=/dev/mtdblock3 rw init=/linuxrc console=ttySAC0,115200 nfsroot=2.2.2.102:/work/nfs_root ip=2.2.2.112:2.2.2.102:2.2.2.1022:255.255.255.0::eth0:off
irq: clearing subpending status 00000002
PID hash table entries: 256 (order: 8, 1024 bytes)
timer tcon=00500000, tcnt a2c1, tcfg 00000200,00000000, usec 00001eb8

跳转地址是0x30008000,加载地址是0x30007FC0,中间相差64字节,这64字节就是头部的大小。

bootm的功能是:

  1. 根据头部,移动内核到合适的地方;
  2. 启动内核,这是通过do_bootm_linux函数完成的。

在启动kernel之前uboot需要告诉内核一些启动参数才能启动, 通过do_bootm_linux传递参数给内核, 但是当跳到内核运行时uboot已经不起作用了怎么传参数,
uboot是按照某种约定数据格式(称为TAG)把要传给kernel的参数写在某个约定地址, 当kernel启动后就去约定的地址读取参数.这个地址是0x30000100,这个格式称为TAG,设置TAG的代码在这里

一些关于uboot传参给kernel的概念:
    uboot向kernel传参的实现就是uboot和kernel规定一种传参的数据格式(tag),
    然后uboot将需要传递给kernel的参数存放到SDRAM的某段内存中,
    然后将这段内存的起始地址传递给kernel,
    然后kernel通过这个地址来读取传递的参数。

kernel如何知道tag的起始地址???
uboot启动内核的最后通过
    theKernel (0, machid, bd->bi_boot_params);来启动内核,
    这三个参数,第一个固定是0,;第二个是机器码;第三个就是tag的存放的起始地址,
    也就是setup_start_tag。这三个参数分别存放在寄存器r0 r1 r2中。

我们看setup_start_tag函数里面,params = (struct tag*) bd->bi_boot_params,bi_boot_params我们再代码搜一下可以看到

bi_boot_params是0x30000100,那么这些参数就是放到0x30000100这里,然后首先存放的是size,

从上面的代码中可以看到,size保存的是tag_size(tag_core),而tag_size是一个宏,

那么这里size就是头部的长度加上tag_core的长度。 

然后保存的是ATAG_CORE,

然后存放的是分别是联合体中的flags,pagesize,rootdev这三个值,一共是存放了五个变量。

执行完setup_start_tag之后得到如下内容,其中size是5,但是单位是4个字节,也就是5*4=20个字节,

 5.1.2 setup_memory_tag


 一样头部也是size,

然后是tag,

 然后是size和start,表示内存,

 这里的size和start在最开始的开机启动时的初始化代码已经设置好了。

 执行完setup_memory_tag之后,

 5.1.3 setup_commandline_tag
setup_commandline_tag输入了另外一个额外的参数commandline,这个参数来源于环境变量。

 这个环境变量的意思是,root也就是跟文件系统位于第3(从0开始)个flash分区,init表示第一个应用程序是linuxc,console表示内核大打印信息从串口0打印出来。

然后看一下setup_commandline_tag函数

执行完 setup_commandline_tag之后,得到如下内容

 5.1.4 setup_end_tag
这个比较简单,size和tag都是零,

5.2 启动内核

kernel如何知道tag的结束地址???
在tag传参的过程中,有一个setup_start_tag(ATAG_CORE类型)和setup_end_tag(AATAG_NONE类型),
setup_start_tag表示tag开始传参,
setup_end_tag 表示传参的结束,
这两个tag之间的tag就是实际的向内核传递的tag。


设置参数到某个约定的地址, 这个约定的地址就是启动参数的起始地址,
在board/100ask24x0/100ask24x0.c文件的board_init函数中设置
int board_init (void)
{
    ...
    /* adress of boot parameters */
    gd->bd->bi_boot_params = 0x30000100; // 这个就是TAG的起始地址, 也就是启动参数的起始地址
}

/***********************************************************************************/
以下是一些关于tag的结构:
struct tag_header {
    u32 size;     // tag参数个数
    u32 tag;    // tag地址
};

/* The list must start with an ATAG_CORE node */
#define ATAG_CORE    0x54410001

struct tag_core {
    u32 flags;        /* bit 0 = read-only */
    u32 pagesize;
    u32 rootdev;
};

/* it is allowed to have multiple ATAG_MEM nodes */
#define ATAG_MEM    0x54410002

struct tag_mem32 {
    u32    size;
    u32    start;    /* physical start address */
};

#define ATAG_CMDLINE    0x54410009

struct tag_cmdline {
    char    cmdline[1];    /* this is the minimum size */
};

#define ATAG_NONE    0x00000000

#define tag_next(t)    ((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)    ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
/***********************************************************************************/

下面分析do_bootm_linux函数时如何设置内核启动参数的:
    setup_start_tag (bd);
        -> params = (struct tag *) bd->bi_boot_params;     // params = 0x30000100
        -> params->hdr.tag = ATAG_CORE;                    // params->hdr.tag = 0x54410001
        -> params->hdr.size = tag_size (tag_core);         // params->hdr.size = (8 + 12) >> 2 = 5 //tag参数个数
        -> params->u.core.flags = 0;
        -> params->u.core.pagesize = 0;
        -> params->u.core.rootdev = 0;
        -> params = tag_next (params);                    // params指向下一个tag起始地址
    ...
#ifdef CONFIG_SETUP_MEMORY_TAGS
    setup_memory_tags (bd);
        -> params->hdr.tag = ATAG_MEM;                    // params->hdr.tag = 0x54410002
        -> params->hdr.size = tag_size (tag_mem32);     // params->hdr.size = (8 + 8) >> 2 = 4 //tag参数个数
        -> params->u.mem.start = bd->bi_dram[i].start;    // sdram起始地址, params->u.mem.start = 0x30000000;
        -> params->u.mem.size = bd->bi_dram[i].size;    // sdarm大小64M, params->u.mem.size = 64*1024*1024;
        -> params = tag_next (params);                    // params指向下一个tag起始地址
#endif
#ifdef CONFIG_CMDLINE_TAG
    setup_commandline_tag (bd, commandline);      //
        -> pparams->hdr.tag = ATAG_CMDLINE;     // params->hdr.tag = 0x54410009
        -> pparams->hdr.size = (sizeof (struct tag_header) + strlen (commandline) + 1 + 4) >> 2; // params->hdr.size = (8+X+1+4)/4
            -> strlen (commandline)得到命令命令行的长度
        -> strcpy (params->u.cmdline.cmdline, commandline); //将命令行拷贝进结构体存储
#endif    -> params = tag_next (params);                        // params指向下一个tag起始地址

#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);
        -> params->hdr.tag = ATAG_NONE;                 // params->hdr.tag = 0x00000000  tag结束标记
        -> params->hdr.size = 0;
#endif


可以得到启动参数存储结构:
                             -------------------------
                             |             内核               |
                             |                                  |
                             -------------------------0x300080000
                             |        .                         |
                             |        .                         |
                             |        .                         |
                             |        .                         |
                             -------------------------
    hdr.tag             |    0x00000000       |
                             -------------------------
    hdr.size             |             0                  |
                             -------------------------     params = tag_next (params); // end_tag
    cmdline            |         命令行数据         |
                             -------------------------
    hdr.tag             |         0x54410009    |
                             -------------------------
    hdr.size             |   (8+strlen+1+4)/4   | 命令行长度
                             -------------------------         params = tag_next (params); // commandline_tag
                             |     其他memory_tag  |
                             -------------------------
    u.mem.start     |         0x30000000   |
                             -------------------------
    u.mem.size      |         64*1024*1024   |
                             -------------------------
    hdr.tag             |         0x54410002    |
                             -------------------------
    hdr.size            |                 4              |
                             -------------------------     params = tag_next (params); // memory_tags
                             |            0                  |
                             -------------------------
                             |            0                  |
                             -------------------------
                             |             0                 |
                             -------------------------
    hdr.tag             |         0x54410001   |     tag
                             -------------------------
    hdr.size             |             5                |     tag参数个数
                             -------------------------     0x30000100 tag起始地址  start_tag
                             |        .                        |
                             |        .                        |
                             -------------------------     0x30000000  sdram起始地址

uboot: 最终目的启动内核
    1. 从Flash读出内核
    2. 启动
    而启动之前需要设置参数给kernel然后跳到kernel入口地址执行
    

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值