u-boot的啓動、編譯過程和命令添加

u-boot的啓動、編譯過程和命令添加


MCU:s5pv210

開發板:unsp210

u-boot:1.3.4

一、簡介

U-Boot是一種支持多架構,多操作系統的Bootloader(啓動引導程序)

u-boot目前最新版本是:http://ftp.denx.de/pub/u-boot/


二、啓動過程

嵌入式Bootloader的啓動過程可以分爲單階段(Single-Stage)和多階段(Multi-Stage)

通常多階段的Bootloader能夠提供更復雜的功能,及更好的可讀性和移植性。

從外部存儲設備上啓動的Bootloader大多是兩階段的啓動過程

第一階段通常用匯編實現,完成一些依賴於CPU體系結構的初始化,並調用第二階段代碼

第二階段通常用C語言實現,完成體系結構之外的功能,主要提供多種複雜命令並引導操作系統

u-boot兩階段入口代碼位置爲:

第一階段位於:cpu/s5pc11x/start.S

第二階段位於:lib_arm/board.c


三、啓動流程分析

以s5pv210爲例分析一下u-boot的啓動流程:

上電之後,從iROM(BL0)裏面執行出廠時固化的代碼,獲取6位OM狀態,檢測相應的控制器(controller),選擇啓動方式(ps:有的芯片也可以通過設置優先級的方式省去撥碼開關,如s5p6818設置啓動方式SD->USB->nand)。

通過控制器選擇對應的啓動設備(SD/USB/Nand/MMC/NOR),從中讀取數據,將其中的BL1拷貝到芯片內部iRAM(BL1),開始從BL1運行,BL1運行結束後將BL2拷貝到SDRAM,在SDRAM裏面運行BL2,BL2運行結束後將內核拷貝到SDRAM,運行內核,掛載根文件系統...

BL0:獲取OM狀態,選擇控制器,拷貝外部BL1到內部BL1(iRAM)

BL1;把BL2拷貝到SDRAM,初始化SDRAM Controller

BL2;把OS拷貝到SDRAM


爲什麼把BL1拷貝到iRAM,BL2拷貝到SDRAM?

因爲第一階段要初始化SDRAM Controller才能使用外存SDRAM,所以第一階段必須在內部進行,第二階段如果芯片內部存儲空間夠大的話也可以在內部運行,但一般都是在外部運行,BL2運行結束後啓動內核,內核直接從Bootloader的地址啓動,覆蓋Bootloader的地址空間,Bootloader被擦除


內核傳參過程:

傳遞給內核的參數由多個結構體組成(面向對象),各結構體放在一段連續的內存空間,起始地址爲0x3000_0100每一個結構體代表一條信息並首尾相連,內核引導起來以後,將從指定內存按照同樣的數據結構將數據取出。


四、兩個階段的任務

u-boot第一階段完成任務:

涉及兩個文件:

u-boot/cpu/s5pc11x/start.S

u-boot/board/sumsung/unsp210/lowlevel_init.S


1、禁用看門狗

lowlevel_init.S(line:61)

/* Disable Watchdog */

ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */

mov r1, #0

str r1, [r0]


2、關中斷

start.S(line:141)

reset:

/*

* set the cpu to SVC32 mode and IRQ & FIQ disable

*/

@;mrs r0,cpsr

@;bic r0,r0,#0x1f

@;orr r0,r0,#0xd3

@;msr cpsr,r0

msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC


3、初始化系統時鐘

lowlevel_init.S(line:122)

/* init system clock */

bl system_clock_init

lowlevel_init.S(line:209)

/*

 * system_clock_init: Initialize core clock and bus clock.

 * void system_clock_init(void)

 */

system_clock_init:

ldr r0, =ELFIN_CLOCK_POWER_BASE @0xe0100000

/* Set Mux to FIN */

ldr r1, =0x0

str r1, [r0, #CLK_SRC0_OFFSET]

ldr r1, =APLL_LOCKTIME_VAL

str r1, [r0, #APLL_LOCK_OFFSET]


4、設置異常向量表(用到中斷的情況下設置)

start.S(line:56)

.globl _start

_start: b reset

ldr pc, _undefined_instruction

ldr pc, _software_interrupt

ldr pc, _prefetch_abort

ldr pc, _data_abort

ldr pc, _not_used

ldr pc, _irq

ldr pc, _fiq


5、內存控制器配置

lowlevel_init.S(line:125)

/* Memory initialize */

bl mem_ctrl_asm_init

/cpu/s5pc11x/s5pc110/cpu_init.S(line:5)

.globl mem_ctrl_asm_init

mem_ctrl_asm_init:

......


6、初始化調試指示燈(可選)

lowlevel_init.S(line:99)

/* PS_HOLD pin(GPH0_0) set to high */

ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)

ldr r1, [r0]

orr r1, r1, #0x300

orr r1, r1, #0x1

str r1, [r0]


7、初始化UART,用於開發調試(可選)

lowlevel_init.S(line:129)

/* for UART */

bl uart_asm_init

lowlevel_init.S(line:355)

/*

 * uart_asm_init: Initialize UART in asm mode, 115200bps fixed.

 * void uart_asm_init(void)

 */

uart_asm_init:

/* set GPIO(GPA) to enable UART */

@ GPIO setting for UART

ldr r0, =ELFIN_GPIO_BASE

ldr r1, =0x22222222

str   r1, [r0, #GPA0CON_OFFSET]


ldr     r1, =0x2222

str     r1, [r0, #GPA1CON_OFFSET]


8、從NAND、NOR或SD卡中複製代碼到SDRAM

NAND:

start.S(line:337)

nand_boot:

mov r0, #0x1000

bl copy_from_nand

b after_copy

start.S(line:435)

/*

 * copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND)

 * r0: size to be compared

 * Load 1'st 2blocks to RAM because U-boot's size is larger than 1block(128k) size

 */

.globl copy_from_nand

copy_from_nand:

push {lr} /* save return address */


mov r9, r0

mov r9, #0x100 /* Compare about 8KB */

bl copy_uboot_to_ram

tst r0, #0x0

bne copy_failed


NOR:

start.S(line:355, 356)

nor_boot:

bl      read_hword

b       after_copy

/board/sumsung/unsp210/flash.c(line:590)

/*-----------------------------------------------------------------------

 * Copy flash to memory

 */

void read_hword (void)

{

volatile u32 *buf = CFG_PHY_UBOOT_BASE;

volatile u32 *nor_base = CFG_FLASH_BASE;

memcpy ((void *)buf, (void *)nor_base, COPY_BL2_SIZE);

}


SD:

start.S(line:346~353)

mmcsd_boot:

#if DELETE

ldr     sp, _TEXT_PHY_BASE      

sub     sp, sp, #12

mov     fp, #0

#endif

bl      movi_bl2_copy

b       after_copy

/cpu/s5pc11x/movi.c(line:19)

void movi_bl2_copy(void)

{

......

......

}

9、 初始化堆棧:start.S(line:389)

stack_setup:

#if defined(CONFIG_MEMORY_UPPER_CODE)

ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)

#else

ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot   */

sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */

sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */

#if defined(CONFIG_USE_IRQ)

sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

sub sp, r0, #12 /* leave 3 words for abort-stack    */

清除bss段:start.S(line:408)

clear_bss:

ldr r0, _bss_start /* find start of bss segment        */

ldr r1, _bss_end /* stop here                        */

mov r2, #0x00000000 /* clear                            */

10、跳轉到start_armboot,進入Bootloader第二階段

start.S(line:419, 421, 422)

ldr pc, _start_armboot

_start_armboot:

.word start_armboot


u-boot第二階段完成任務:

涉及一個文件:/u-boot/lib_arm/board.c


1.開發板相關的初始化:

u-boot/lib_arm/board.c(483行)

#if defined(CONFIG_SMDKC110)

2.加載環境變量:

u-boot/lib_arm/board.c(650行)

/* initialize environment */

env_relocate ();

3.使能中斷:

u-boot/lib_arm/board.c(705行)

/* enable exceptions */

enable_interrupts ();

4.主循環:                

main_loop(756行)

/* main_loop() can return to retry autoboot, if so just run it again. */

for (;;) {

main_loop ();

}

->     s = getenv ("bootcmd");

->     parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);

->     rcode = parse_stream_outer(&input, flag);

->     code = run_list(ctx.list_head);

->     rcode = run_list_real(pi);

->     rcode = run_pipe_real(pi);

->     if ((cmdtp = find_cmd(child->argv[i])) == NULL)                                  

(common/hush.c  1679行)

->     rcode = (cmdtp->cmd)(cmdtp, flag,child->argc-i,&child->argv[i]);          

(common/hush.c  1710行)


五、配置編譯過程

以s5pv210開發板爲例

u-boot的配置編譯需要經過以下步驟:

1、在u-boot的根目錄下執行:#make unsp210_config  //對應開發板配置

Makefile 會構建編譯結構,如:架構、cpu、開發板、廠商、芯片、目錄等,爲下一步真正編譯鏈接做準備。

2、修改include/configs/unsp210.h配置文件

3、在根目錄下執行:make

根據以上兩步產生編譯和連接所需文件的信息

最終make完成,在根目錄下將生成:

u-boot.bin   u-boot.dis   u-boot.map...


編譯過程如下:

u-boot的根目錄下執行:#make unsp210_config 打開Makefile在2581行找到

unsp210_config : unconfig

@$(MKCONFIG) $(@:_config=) arm s5pc11x unsp210 samsung s5pc110

@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/unsp210/config.mk

可以去掉@符號讓信息輸出到屏幕

其中第一句的$(MKCONFIG)表示引用第101行指定的mkconfig文件路徑

$(@:_config=)表示目標去掉_config

後面的是編譯參數

第二句表示將信息輸出到文件board/samsung/unsp210/config.mk


打開mkconfig看到第23行如下:

[ "${BOARD_NAME}" ] || BOARD_NAME="$1" 給BOARD_NAME賦值$1

繼續往下執行mkconfig發現都是一些mkdir, rm, cd, ln命令,刪除舊的鏈接建立新的文件鏈接

mkconfig第123行

echo "ARCH   = $2" >  config.mk

echo "CPU    = $3" >> config.mk

echo "BOARD  = $4" >> config.mk

將信息輸出到include/config.mk文件中,在config.mk文件114行引用環境變量

mkconfig第134行

if [ "$APPEND" = "yes" ] # Append to existing config file

then

echo >> config.h

else

> config.h # Create new config file

fi

echo "/* Automatically generated - do not edit */" >>config.h

echo "#include <configs/$1.h>" >>config.h

添加平臺頭文件(unsp210.h)創建config.h加入"#include <configs/$1.h>"

鏈接過程:

鏈接地址定義在board/samsung/unsp210/config.mk中

鏈接腳本board/samsung/unsp210/u-boot.lds

/board/samsung/unsp210/config.mk被頂層config.mk包含並設置鏈接選項LDFLAGS(arm-linux-ld)

config.mk第198行

LDFLAGS += -Text $(TEXT_BASE)

這個TEXT_BASE就是Makefile第2583行的這句裏

@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/unsp210/config.mk

LDFLAGS在makefile鏈接時發揮作用

最終用OBJCOPY去掉ELF格式信息得到可以直接在裸機上執行的u-boot.bin

$(OBJCOPY)${OBJCFLAGS} -O binary $< $@

六、命令添加

u-boot的每一個命令都是通過U_BOOT_CMD宏定義來實現的,這個宏在include/command.h頭文件中

U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)

每一個命令定義了一個cmd_tbl_t結構體,結構體包含的成員變量有:命令名稱、最大參數個數、重複次數、命令執行函數、用法、幫助。

從控制檯輸入的命令都被送到common/command.c中的find_cmd()函數解釋執行,根據匹配輸入的命令,從列表中找出對應的命令結構體,並調用其回調處理函數完成命令處理,命令響應的過程,就是命令的查找與回調函數處理的過程

find_cmd()部分代碼如下:

for(cmdtp=&__u_boot_cmd_start;cmdtp != &__u_boot_cmd_end;cmdtp++)

{

if(strncmp(cmd, cmdtp->name, len)==0)

{

if(len == strlen(cmdtp->name))

return cmdtp;

cmdtp_temp=cmdtp;

n_found++;

}

}

if(n_found==0){

return cmdtp_temp;

}

實現了輸入部分命令可以代替全部命令的效果,如輸入print和輸入pri結果一樣

原理:輸入時用len保存命令長度,比較前len個長度的輸入與命令,如果全部匹配直接返回命令結構體,如果部分匹配,存入臨時結構體cmdtp_temp,並且n_found++,最後判斷如果n_found==1則返回cmdtp_temp


怎樣添加一個u-boot命令(三步)

假如要添加一個helloworld命令

1、在include/configs/unsp210.h中增加一項宏定義 這樣通過改變宏定義的值(1打開或0關閉)進行選擇性的編譯

   #define CONFIG_CMD_HELLOWORLD  1

2、在common/文件夾下建立cmd_helloworld.c文件

#include<common.h>

#include<command.h>

#ifdef CONFIG_CMD_HELLOWORLD

void helloworld(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

int i=0;

for(i=0;i<argc;i++)

{

printf("Hello World!\n");

printf("argv[%d]=$s\n", i, argv[i]);

}

}

//命令名稱、最大參數個數、重複次數、命令執行函數、用法、幫助

U_BOOT_CMD(hello,3,2,helloworld,"hello command","add u-boot command!\n");

#endif

3、在common/Makefile中增加一項

COBJS-y += cmd_helloworld.o

make重新下載u-boot.bin在命令行輸入help和hello命令查看結果


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章