一.bootloader
1.了解系統的啟動過程
PC: BIOS(固件)------>bootloader(硬碟MBR)------>OS核心(拷貝到RAM)- ---->啟動系統
bootlloadre的啟動過程一般分為2段,BL1 BL2
嵌入式: bootloader(flash)------->OS核心(拷貝到記憶體)—>啟動系統
S5PV210的啟動過程:
IROM(固件)------>bootloader(flash,網絡,usb,SD,序列槽)加載到記憶體 ---->啟動OS核心---->系統
bootloader有兩種狀态,一種是自啟動模式(加載核心),另一種叫下載下傳更新模式(指令行)
bootloader的種類:e-boot u-boot r-boot vivi…
2.u-boot是bootloader的一種
Uboot官方下載下傳位址:
ftp://ftp.denx.de/pub/u-boot/
git://git.denx.de/u-boot.git
http://git.denx.de/u-boot.git
u-boot的通用性:
1)支援多種架構的CPU:PowerPC、ARM、X86、MIPS、NIOS、XScale
2)支援多種作業系統:Linux Vxworks QNX…
3.分析uboot的源代碼
注意:檢視uboot源代碼時在source Insight文檔選項–>檔案過濾器加上[*c,*h,*s,*S]。
uboot源碼目錄下檔案夾的介紹:
1)cpu
包含和CPU架構相關的代碼,u-boot支援的CPU在該目錄下都有一個子目錄 與之對應。
2)board
包含和開發闆相關的檔案,每一個開發闆都以一個目錄的形式出現在該目錄下。
3)drivers
本目錄下存放所有外圍晶片的驅動,網卡、USB、序列槽、LCD、Nand Flash等。
4)lib_arm
支援ARM架構下的通用檔案。
5)include/configs/smdkv210single.h
定義了大量的宏定義,配置u-boot的參數(如:晶振的頻率、序列槽的波特率、開發闆的IP位址)。
4.源代碼研究:
Makefile
1)确定編譯器(在Makefile檔案中加入下面兩行代碼确定編譯器)
126 ARCH = arm
127 CROSS_COMPILE = arm-linux-(根據實際工具添加)
2)uboot配置
make smdkv210single_config
Makefile:
2584 smdkv210single_config : unconfig
2585 @$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110
2586 @echo “TEXT_BASE = 0xc3e00000” > $(obj)board/samsung/smdkc110/config.mk
476 unconfig:
477 @rm -f $(obj)include/config.h $(obj)include/config.mk
478 $(obj)board//config.tmp $(obj)board/
138 bl nand_asm_init
//nand初始化
//做了非常重要的底層初始化
//回到cpu/s5pc11x/start.S
324 beq nand_boot
334 nand_boot:
335 mov r0, #0x1000
336 bl copy_from_nand
//将uboot從nand flash拷貝到外部DRAM
337 b after_copy
拷貝完成後
開啟MMU
初始化堆棧
清BSS段
//使用絕對跳轉跳轉到start_armboot執行(BL2)
411 ldr pc, _start_armboot
413 _start_armboot:
414 .word start_armboot
uboot的BL1的執行流程
1.關中斷,設定為管理模式
2.在lowlevel_init中進行底層初始化(看門狗,時鐘,記憶體,序列槽,nand)
3.将uboot搬運到外部DRAM—>0x23e00000
4.開啟MMU 0x23e00000---->0xc3e00000
5.初始化棧
6.清空BSS段
7.使用跳轉指令ldr pc, _start_armboot将代碼跳轉到BL2運作(外部DRAM中)
uboot的BL2
//lib_arm/board.c
291 typedef int (init_fnc_t) (void);
//定義了一個函數類型,傳回值為int,參數為void
start_armboot:
325 init_fnc_t **init_fnc_ptr;
362 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
363 if ((*init_fnc_ptr)() != 0) {
364 hang ();
365 }
366 }
//周遊init_sequence函數指針數組,執行其中的每一個函數
//init_sequence數組中儲存的是一系列初始化函數,執行失敗,報錯
gd_t bd_t
//include/asm-arm/global_data.h
typedef struct global_data {
bd_t *bd;
…
} gd_t;
//定義u-boot的全局資訊結構體
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”)
//定義一個寄存器變量儲存gd變量的位址,而且必須儲存在r8寄存器中
//include/asm-arm/u-boot.h
typedef struct bd_info {
…
ulong bi_arch_number; /機器碼(開發闆ID)/
ulong bi_boot_params; /uboot傳遞給核心的參數位址/
struct
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
//開發闆資訊結構體
//board/samsung/smdkc110.c
int board_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_DRIVER_SMC911X
smc9115_pre_init();
#endif
#ifdef CONFIG_DRIVER_DM9000
dm9000_pre_init();
#endif
gd->bd->bi_arch_number = MACH_TYPE;
//開發闆的機器碼,啟動核心時傳遞給核心(2456)
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
//uboot傳遞給核心的參數所在的位址(0x20000100)
return 0;
}
int dram_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
//第一塊記憶體的起始位址(0x20000000)
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
//第一塊記憶體的大小(0x20000000) 512M
#if defined(PHYS_SDRAM_2)
gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE;
#endif
return 0;
}
最後執行main_loop
uboot的兩種模式:
自啟動模式:執行bootcmd環境變量中的指令
下載下傳更新模式:進入指令行
//commom/main.c
main_loop:
擷取環境變量bootdelay bootcmd
在bootdelay秒内,判斷序列槽是否有輸入
1>有,通過敲擊空格鍵,進入下載下傳更新模式(指令行)
GEC210#
可以在該提示符下執行鍵盤輸入的指令
2>沒有,進入自啟動模式,啟動核心
如何啟動核心?
通過擷取一個環境變量,bootcmd環境變量,該環境變量中存放了啟動核心相關指令,接着執行這些指令,啟動核心
bootcmd=tftp 20008000 zImage;bootm 20008000
//将核心鏡像zImage加載到20008000;到20008000去運作核心鏡像zImage
uboot的指令:
1>print(printenv):檢視目前環境的環境變量
print bootdelay
2>set(setenv):設定環境變量
set bootdelay 5
3>save(saveenv):儲存環境變量
4>tftp:通過tftp服務下載下傳在tftp伺服器目錄下的程式到記憶體中
tftp 30008000 led.bin
5>go:執行記憶體中的二進制代碼
go 30008000
6>bootm:啟動記憶體中的核心鏡像檔案zImage
bootcmd=tftp 20008000 zImage;bootm 20008000
7>nand系列指令
nand erase:擦nand flash
nand erase 0 100000
nand write:寫nand flash
nand write 20008000 0 100000
nand read:讀nand flash
nand read 20008000 0 100000
uboot源代碼中如何定義指令?
//include/command.h
struct cmd_tbl_s {
char *name; /指令名/
int maxargs; /最大參數個數/
int repeatable; /重複次數/ //指令對應的處理函數
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage; /用法資訊/
#ifdef CFG_LONGHELP
char *help; /幫助資訊/
#endif
};
//uboot的指令結構體
typedef struct cmd_tbl_s cmd_tbl_t;
#define Struct_Section attribute ((unused,section (".u_boot_cmd")))
//__attribute__是GNU C對标準C的擴充,可以用來設定變量,函數,類型的屬性
//在這裡表示被該屬性修飾的變量,在連結時被該屬性修飾的變量必須放//到.u_boot_cmd段中
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
cmd_tbl_t _u_boot_cmd##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
//common/command.c
U_BOOT_CMD(
version, 1, 1, do_version,
“version - print monitor version\n”,
NULL
);
cmd_tbl_t __u_boot_cmd_version attribute ((unused,section (".u_boot_cmd"))) =
{version, 1, 1, do_version, “version - print monitor version\n”, NULL};
bootm指令:
//common/cmd_bootm.c
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
“bootm - boot application image from memory\n”,
“[addr [arg …]]\n - boot application image stored in memory\n”
“\tpassing arguments ‘arg …’; when booting a Linux kernel,\n”
“\t’arg’ can be the address of an initrd image\n”
#if defined(CONFIG_OF_LIBFDT)
“\tWhen booting a Linux kernel which requires a flat device-tree\n”
“\ta third argument is required which is the address of the\n”
“\tdevice-tree blob. To boot that kernel without an initrd image,\n”
“\tuse a ‘-’ for the second argument. If you do not pass a third\n”
“\ta bd_info struct will be passed instead\n”
#endif
#if defined(CONFIG_FIT)
“\t\nFor the new multi component uImage format (FIT) addresses\n”
“\tmust be extened to include component or configuration unit name:\n”
“\taddr:<subimg_uname> - direct component image specification\n”
“\taddr#<conf_uname> - configuration specification\n”
“\tUse iminfo command to get the list of existing component\n”
“\timages and configurations.\n”
#endif
);
bootm 20008000
do_dootm函數---->do_bootm_linux函數(lib_arm/bootm.c)
---->theKernel (0, machid, bd->bi_boot_params);
開發闆ID 傳遞給核心的啟動參數的位址
2456 0x20000100
啟動核心,uboot到此結束
uboot的整個啟動過程:
1>start.S->前8K代碼在CPU的IRAM中執行(管理模式,關中斷)
2>lowlevel_init.S->關看門狗,初始化時鐘,記憶體,nand,序列槽
3>start.S->将整個uboot拷貝到外部DRAM中,為其運作在0xc3e00000做準備
開mmu,建棧,清bss段,使用ldr pc, _start_armboot跳轉到uboot的BL2
4>start_armboot->執行初始化函數清單(board_init,dram_init…)
5>main_loop->擷取bootdelay,bootcmd環境變量
6>執行bootcmd環境變量中的啟動指令tftp/nand read bootm
7>bootm指令->do_bootm->do_bootm_linux->theKernel(0,2456,0x20000100)
bootargs環境變量很重要
-ffixed-r8:編譯時不使用r8寄存器
cp uboot.bin /tftpboot
tftp 30008000 u-boot.bin
nand erase 0 100000
nand write 30008000 0 100000