u-boot(五)核心啟動
目錄
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:[email protected](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_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
設定結束標誌