目录
title: u-boot(五)内核启动
tags: linux date: 2018-09-26 19:58:05 ---u-boot(五)内核启动
概述
启动命令:bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
,具体代码实现的重点是以下:
s=getenv ("bootcmd")
获取环境变量run_command (s, 0);
启动内核,这个s=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
s
就是先读出内核,再启动内核了
备注 jffs2是一种文件格式,在这里并不需要文件格式,但是使用这个jffs2 可以不使用页对齐,如果使用nandread,需要考虑页对齐或者块对齐,最终会使用nand_read_opts
- 我们也可以在u-boot 命令行直接输入
boot
来启动内核,但是实际的命令是一样的,是在cmd_bootm.c
中调用do_bootd
>run_command (getenv ("bootcmd"), flag)
分区空间
常见的内部空间布局如下:
Bootloader | Boot parameters | Kernel | Root filesystem |
---|---|---|---|
u-boot,它会在内存的某个地方存放着内核启动的一些参数,也称为tag | u-boot 参数,包含传递给内核的一些东西 | 内核 | 根文件系统 |
嵌入式的FLASH没有实际的分区,所谓分区只是一个名称,具体的地址是写死的. 在include/configs/100ask24x0.h
#define MTDIDS_DEFAULT "nand0=nandflash0"#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \ "128k(params)," \ "2m(kernel)," \ "-(root)"
这里定义了mtdparts
分区,位于nandflash0
,bootloader
大小是256k,从0开始,然后是128k大小的params,接下去是2M的kernel内核,剩余的都是root文件系统.
内核文件格式
Flash上存储的内核格式为uImage,包含了一个头部加真正的内核.
/* * all data in network byte order (aka natural aka bigendian) */#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; /* 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;
- ih_load表示加载地址,表示内核应该放在哪里
- ih_ep表示入口地址,表示跳转的地址,也就是内核代码段的入口,广义上的main入口
内核复制跳转
bootm会先判断内核是否在加载地址,否则先移动内核到指定的加载地址,然后跳转。
命令中0x30007FC0 地址可以随便放,只要不破坏已经用到的信息就好, bootm
命令如果发现当前内核并不在加载地址,需要移动内核到加载地址。do_bootm
函数中memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
移动内核。
所以如果ih_load=我们内核的地址的时候,就不需要move,节省时间.jz2440 的加载地址是0x30008000,头部是64字节,所以,0x30008000-64=0x30007FC0,所以我们copy内核到0x30007FC0 .
内核启动
//在 bootm命令中有linux内核跳转,//lib_arm/armlinux.c-->do_bootm_linuxdo_bootm_linux (cmdtp, flag, argc, argv,addr, len_ptr, verify); //theKernel 就是uimage的头部中的入口地址 -theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); // 设置一些参数 setup_start_tag (bd); setup_memory_tags (bd); setup_commandline_tag (bd, commandline); setup_end_tag (bd); // 所以内核的入口参数 -theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
机器ID
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
中的第二个参数是机器ID,内核通过比对机器ID判断是否支持启动.gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
启动参数
内核跳转之前,同样需要设置内核的启动参数.内核的参数是按照tag
组织的.也就是在某个地址(0x30000100,在100ask24x0.c中定义),按照某种格式存储,这种格式具体为【size....tagid....tag值】
在do_bootm_linux
中有设置内存,命令行参数等,代码片段如下
bd_t *bd = gd->bd;//设置起始的头setup_start_tag (bd);//设置内存setup_memory_tags (bd);setup_commandline_tag (bd, commandline);//....// 设置结束的idsetup_end_tag (bd);
具体有以下几种tag,代码中以联合体定义,这样方便使用同一个指针指向它,方便之处见setup_start_tag
分析.
//这个tag 就是一个包含了所有类型tag的一个联合体,是实际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; /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u;};
(起始tag)setup_start_tag
static void setup_start_tag (bd_t *bd){ // 这个tag 就是一个包含了所有类型tag的一个联合体 // 使用联合体之后,下面就可以使用 params->具体的tag类型 params = (struct tag *) bd->bi_boot_params; params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core); //tag_core 也就是接下去这三个参数了 //tag_size =zise + tag + 实际的值 params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; //指向下一个参数 params = tag_next (params);}#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)// 这里 tag_header就是 size+tag , type 就是实际的tag的内容// tag_size就是包含 id 和 size 和 内容的大小了
因为bd_t *bd = gd->bd;
,所以搜索下gd->bd->bi_boot_params
,也就是在board/100ask24x0/100ask24x0.c
中定义,也就是说参数是放在0x30000100
.
gd->bd->bi_boot_params = 0x30000100;
内存设置
static void setup_memory_tags (bd_t *bd){ int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); params->u.mem.start = bd->bi_dram[i].start; params->u.mem.size = bd->bi_dram[i].size; params = tag_next (params); }}
搜索下gd->bd->bi_dram[0]
,同样在board/100ask24x0/100ask24x0.c
定义
int dram_init (void){ gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0;}#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
这个函数是在lib_arm/board.c
中的init_sequence
调用,也就是start_armboot
中调用,也就是在u-boot(三)第一阶段的C中使用的
根文件系统,启动程序,串口设备
char *commandline = getenv ("bootargs");setup_commandline_tag (bd, commandline);static void setup_commandline_tag (bd_t *bd, char *commandline){ char *p; if (!commandline) return; /* eat leading white space */ for (p = commandline; *p == ' '; p++); /* skip non-existent command lines so the kernel will still * use its default command line. */ if (*p == '\0') return; params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params);}
设置命令tag,多了参数commandline
,源自环境变量bootargs
查看下环境变量bootargs
,使用print
查看,也可搜索下代码
"bootargs=" CONFIG_BOOTARGS "\0"//include/configs/100ask24x0.h#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"
root=/dev/mtdblock3
表示根文件系统从第四个FLASH分区开始(从0开始计数)可以往上看分区空间init=/linuxrc
指示第一个应用程序console=ttySAC0
,内核打印信息从串口0 打印
(结束)setup_end_tag
设置结束标志