u-boot分析之内核启动(五)
目錄
u-boot(五)內核啟動
- 概述
- 分區空間
- 內核文件格式
- 內核復制跳轉
- 內核啟動
- 機器ID
- 啟動參數
- 內存設置
- 根文件系統,啟動程序,串口設備
- (結束)setup_end_tag
u-boot(五)內核啟動
概述
啟動命令:bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0,具體代碼實現的重點是以下:
備注?jffs2是一種文件格式,在這里并不需要文件格式,但是使用這個jffs2 可以不使用頁對齊,如果使用nand read,需要考慮頁對齊或者塊對齊,最終會使用nand_read_opts
- 我們也可以在u-boot 命令行直接輸入boot來啟動內核,但是實際的命令是一樣的,是在cmd_bootm.c中調用do_bootd>run_command (getenv ("bootcmd"), flag)
分區空間
常見的內部空間布局如下:
| 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,包含了一個64字節頭部加真正的內核Image.
/** 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表示加載地址,表示內核應該放在哪里,加載地址為0x30008000
- 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_linux do_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); //.... // 設置結束的id setup_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
設置結束標志
轉載:https://www.cnblogs.com/zongzi10010/p/10023681.html
總結
以上是生活随笔為你收集整理的u-boot分析之内核启动(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: u-boot分析之小结(六)
- 下一篇: u-boot分析之命令实现(四)