Uboot到底如何啟動核心
1.uboot啟動核心的程式碼縮減如下:
Uboot 1.16/lib_arm/board.c中start_armboot()函式呼叫/common/main.c中main_loop()函式,在main_loop()中有uboot啟動核心的程式碼:
s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s :"<UNDEFINED>");
if (bootdelay >= 0 && s && !abortboot (bootdelay))
{
run_command(s, 0);
}
2.假設bootcmd = nandread.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
<1> nandread.jffs2 0x30007FC0 kernel
從nand讀出核心:
從哪裡讀? :kernel分割槽
讀到哪裡去?:0x30007FC0
何為分割槽?
簡單的說就是將nand劃分為幾個區域,一般如下:
bootloader->params->kernel->root
這些分區劃分在/include/configs/100ask24x0.h中寫死的:
#define MTDPARTS_DEFAULT"mtdparts=nandflash0:[email protected](bootloader)," \
"128k(params)," \
"2m(kernel)," \
"-(root)"
進入uboot執行mtd ,可以檢視已有分割槽:
# name 大小 在nand上的起始地址
0 bootloader 0x00040000 0x00000000
1 params 0x00020000 0x00040000
2 kernel 0x00200000 0x00060000
3 root 0xfda00000 0x00260000
上面的nand read.jffs2 0x30007FC0 kernel等價於:
nand read.jffs20x30007FC0 0x00060000 0x00200000
注:read.jffs2並不是指定特定的格式,僅表示不需要塊/頁對齊,所以kernel的分割槽大小可以隨意定。
<2> bootm0x30007FC0
關鍵函式do_bootm()
flash上存的核心:uImage
uImage =頭部+真正的核心
頭部的定義如下:
typedef struct image_header {
uint32_t ih_magic;
uint32_t ih_hcrc;
uint32_t ih_time;
uint32_t ih_size;
uint32_t ih_load;
uint32_t ih_ep;
uint32_t ih_dcrc;
uint8_t ih_os;
uint8_t ih_arch;
uint8_t ih_type;
uint8_t ih_comp;
uint8_t ih_name[IH_NMLEN];
} image_header_t;
我們需要關心:
uint32_t ih_load;
uint32_t ih_ep;
ih_load是載入地址,即核心執行是應該位於的地方
ih_ep是入口地址,即核心的入口地址
這與uboot類似,uboot的載入地址是TEXT_BASE = 0x33F80000;入口地址是start.S中的_start。
從nand讀出來的核心可以放在ram中的任意地方,如0x31000000,0x32000000等等,只要它不破壞uboot所佔用的記憶體空間就可以
既然設定好了載入地址和入口地址,為什麼核心還能隨意放?
因為uImage有一個頭部!頭部裡有載入地址和入口地址,當我們用bootm xxx時,
do_bootm先去讀uImage的頭部以獲取該uImage的載入地址和入口地址,當發現該uImage目前所處的記憶體地址不等於它的載入地址時,會將uImage移動到它的載入地址上,程式碼中體現如下:
uboot 1.16/common/cmd_bootm.c中的bootm_load_os()函式
case IH_COMP_NONE::
if (load != image_start)
{
memmove_wd((void *)load, (void *)image_start, image_len, CHUNKSZ);
}
另外,當核心正好處於頭部指定的載入地址,便不用uboot的do_bootm函式來幫我們搬運核心了,可以縮短啟動時間。這就是為什麼我們一般都下載uImage到0x30007FC0的原因。
核心載入地址是0x30008000,而頭部的大小64個位元組,將核心拷貝到0x30007FC0,加上頭部的64個位元組,核心正好位於0x30008000處。
總結bootm做了什麼:
1.讀取頭部
2.將核心移動到載入地址
3.啟動核心
具體如何啟動核心?
使用在/lib_arm/bootm.c定義的do_bootm_linux(),我們已經知道入口地址,只需跳到入口地址就可以啟動linux核心了,在這之前需要做一件事———— uboot傳遞引數(啟動引數)給核心。
啟動程式碼在do_bootm_linux()函式:
void (*theKernel)(int zero, int arch,uint params); //定義函式指標theKernel
theKernel = (void (*)(int, int, uint))images->ep; //先是將入口地址賦值給theKernel
theKernel (0, bd->bi_arch_number, bd->bi_boot_params); //然後是呼叫thekernel,以0,bd->bi_arch_number,bd->bi_boot_params為引數
下面分析這三個引數:
1. 0—相當於mov,ro #0
2.bd->bi_arch_number:uboot機器碼,這個在/board/100ask24x0.c設定:gd->bd->bi_arch_number = MACH_TYPE_S3C2440,MACH_TYPE_S3C2440在/arch/arm/asm/mach-types.h定義:362,核心機器碼和uboot機器碼必須一致才能啟動核心
2. bd->bi_boot_parmas--- 啟動引數地址
也是在在/board/100ask24x0.c設定:gd->bd->bi_boot_params = 0x30000100;
啟動引數(tag)在哪裡設定?
在lib_arm/armlinux.c設定:
setup_start_tag (bd);
setup_revision_tag (parmas);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
setup_initrd_tag (bd, images->rd_start, images->rd_end);
setup_videolfb_tag ((gd_t *) gd);
setup_end_tag (bd);
每一個啟動引數對應一個tag結構體,所謂的設定傳遞引數其實就是初始化這些tag的值,想了解這個結構體以及這些tag的值是如何設定的請看嵌入式Linux應用開發完全手冊的uboot章節
我們來看setup_start_tag(bd)函式:
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size(tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
再看setup_commandline_tag (bd , commandline):
static void setup_commandline_tag (bd_t *bd, char*commandline)
{
// commandline就是我們的bootargs
char *p;
if (!commandline)
return;
for (p = commandline; *p == ' '; p++);
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(引數)並跳轉啟動。
更多uboot啟動核心的細節觀看畢業班視訊自己寫uboot。
---------------------
作者:韋東山
來源:CSDN
原文:https://blog.csdn.net/thisway_diy/article/details/76064244
版權宣告:本文為博主原創文章,轉載請附上博文連結!