/******************************************************************************************/ 在主函数 main_loop中下面两行是启动内核的过程 # ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { s = getenv("menucmd"); 1 if (s) { # ifndef CFG_HUSH_PARSER run_command (s, 0); 2 #endif } } #endif
其中 getenv函数获取的 run_command要执行的指令就是bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0中的指令
下面这条指令是从nand读取内核:从哪里读?―从kernel分区中读取 == 0x00060000
读到哪里去?―0x30007FC0
分区的名字不重要,分的名字仅仅代表的是,起始地址和长度而已
nand read.jffs2 0x30007FC0 0x00060000 == nand read.jffs2 0x30007FC0 0x00060000 0x00200000;
读取的大小是 0x00200000 2M
使用 nand read.jffs2 可以不用页对其就能够进行读取的操作
bootm 0x30007FC0
0x30007FC0改地址只要不破,对咋堆栈等一些其他的信息就行,可以在随意放入改动,其值会
赋值给uimage的内核地址成员,因为uimage的头部中有加载地址和入口地址,所以地址可以随便放
内核的加载地址是: 30008000
0x30008000 - 0x30007FC0 = 0x40 = 64字节
将环境变量中下载内核的地址设置为 0x30007FC0 之后,下载的内核就可以不用移动直接使用了
因为uimage的头部信息刚好是64字节:
#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; 总共 4*7+4+32 = 64字节 可以使用 mtd 命令查看; 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)
注:
因为在嵌入式linux中没有PC机中那种非常庞大的文件管理系统,因此采用在源码中将分区写死的形式
为嵌入式linux中的文件进行分区
文件中一般会有此种定义:
```bash
#define MTDPARTS_DEFAULT “mtdparts=nandflash0:256k@0(bootloader),”
“128k(params),”
“2m(kernel),”
“-(root)”
嵌入式内核: uimage=头部+真正的内核 头部是一个结构体: ```c 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;
1.然后会按照头部信息移动内核到合适的地址
2.启动内核
do_bootm_linux
u-boot 设置内核启动参数
然后跳到内核启动地址启动内核
u-boot和内核的参数交互使用的是将参数放置在和内核约定好的地址(按照双方约定好的格式进行)
定义的格式是TAG,地址是30000100
定义的TAG如下:
#ifdef CONFIG_INITRD_TAG static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end) { /* an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found. ATAG_RDIMG is a better name, actually. */ params->hdr.tag = ATAG_INITRD2; params->hdr.size = tag_size (tag_initrd); params->u.initrd.start = initrd_start; params->u.initrd.size = initrd_end - initrd_start; params = tag_next (params); } #endif /* CONFIG_INITRD_TAG */ #if defined (CONFIG_VFD) || defined (CONFIG_LCD) extern ulong calc_fbsize (void); static void setup_videolfb_tag (gd_t *gd) { /* An ATAG_VIDEOLFB node tells the kernel where and how large * the framebuffer for video was allocated (among other things). * Note that a _physical_ address is passed ! * * We only use it to pass the address and size, the other entries * in the tag_videolfb are not of interest. */ params->hdr.tag = ATAG_VIDEOLFB; params->hdr.size = tag_size (tag_videolfb); params->u.videolfb.lfb_base = (u32) gd->fb_base; /* Fb size is calculated according to parameters for our panel */ params->u.videolfb.lfb_size = calc_fbsize(); params = tag_next (params); } #endif /* CONFIG_VFD || CONFIG_LCD */ #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ defined (CONFIG_CMDLINE_TAG) || \ defined (CONFIG_INITRD_TAG) || \ defined (CONFIG_SERIAL_TAG) || \ defined (CONFIG_REVISION_TAG) || \ defined (CONFIG_VFD) || \ defined (CONFIG_LCD) static void setup_start_tag (bd_t *bd); # ifdef CONFIG_SETUP_MEMORY_TAGS static void setup_memory_tags (bd_t *bd); # endif static void setup_commandline_tag (bd_t *bd, char *commandline); #if 0 static void setup_ramdisk_tag (bd_t *bd); #endif # ifdef CONFIG_INITRD_TAG static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end); # endif static void setup_end_tag (bd_t *bd); # if defined (CONFIG_VFD) || defined (CONFIG_LCD) static void setup_videolfb_tag (gd_t *gd); # endif
===============================================================================
主要是下面四个函数:
setup_memory_tags (bd_t *bd); setup_memory_tags (bd_t *bd); setup_commandline_tag (bd_t *bd, char *commandline); setup_end_tag (bd_t *bd); 函数体如下: setup_start_tag 函数的作用是将 地址执行的前五个字节放置上相关信息 static void setup_start_tag (bd_t *bd) { params = (struct tag *) bd->bi_boot_params; // bd->bi_boot_params = 30000100 //gd->bd->bi_boot_params = 0x30000100; 参数放置在 30000100的地址处 params->hdr.tag = ATAG_CORE; //#define ATAG_CORE 0x54410001 params->hdr.size = tag_size (tag_core); // 等于 5 //#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) // >> 2 右移 2 相当于除以4 struct tag_header { u32 size; u32 tag; }; + struct tag_core { u32 flags; /* bit 0 = read-only */ u32 pagesize; u32 rootdev; }; = 20字节 除以4之后刚好是5 struct tag_core { u32 flags; /* bit 0 = read-only */ u32 pagesize; u32 rootdev; }; 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; }; 接下来就是将下面的参数按照顺序装进内存中去 params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next (params); 做了一件事就是讲 先后移动 5 个字节 //#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)) }========================================================================================================== setup_memory_tags 函数体如下: static void setup_memory_tags (bd_t *bd) { int i; =========================================================== 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; } ============================================================== 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); } }
===================================================================================
setup_commandline_tag函数:
char *commandline = getenv (“bootargs”);
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
init=/linuxrc 第一个应用程序是 linuxrc
root=/dev/mtdblock3 根文件系统位于第四个flash分区
console=ttySAC0 指定打印信息输出的地方 ttySAC0 --> 串口 0
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; //54410009 params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); } ======================================================================================= setup_end_tag (bd_t *bd);函数: static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; }
=========================================================================================================
在这里设置好之后内核会到这个地址来读取这份参数:
注:使用sourceinsight搜索变量的时候,将 search method设置成 --> Regular expression
内核启动;
//在这里启动内核
//bi_arch_number 机器 ID
//运行该函数之后 控制权就交给了 内核
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);