精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

U-Boot啟動過程概述 U-Boot啟動代碼具體分析

技術讓夢想更偉大 ? 來源:技術讓夢想更偉大 ? 2023-11-27 10:24 ? 次閱讀

1. U-Boot啟動過程概述

U-Boot的啟動過程分為兩個階段。

第一階段:主要是SOC內部的初始化,板級的初始化比較少,所以移植的修改量比較小。此階段由匯編語言編寫,代碼主體分布在/uboot/cpu/s5pc11x/start.S和/uboot/board/samsung/x210/lowlevel_init.S中。

第二階段:主要是板級的初始化,SOC內部的初始化比較少,移植的修改量主要在此。此階段由c語言編寫,代碼主體分布在/uboot/lib_arm/board.c中。

紅色字體部分和板級關系較大,是移植的重點修改部分。

6cfe8b4e-8c66-11ee-939d-92fbcf53809c.png

2. U-Boot啟動代碼具體分析

2.1 第一階段(/ubootcpu/s5pc11x/start.S)

(0)頭文件包含

/uboot/include/config.h文件是在配置過程中中生成的(具體/uboot/mkconfig文件中實現),其中的內容就是“#include ”,即包含開發板的配置頭文件。

version.h文件是uboot的版本信息文件,是在編譯過程中生成的。具體的U-Boot的version信息可以在/uboot/Makefile中更改

U-Boot中的頭文件包含其實都不是真正被包含的文件,它們大多是在配置編譯階段產生的符號鏈接或者是具有符號鏈接功能的頭文件(/uboot/mkconfig中創建符號鏈接)。總之,它們的功能都類似于符號鏈接,目的是讓uboot的源碼更具靈活性和移植性。

 #include 
#include 
#if defined(CONFIG_ENABLE_MMU)
#include 
#endif
#include 

(1)填充16字節的校驗位

uboot.bin鏡像在開頭需要加16字節的檢驗頭(詳見 irom application note文件)。這一部分主要功能是在代碼段最開始處放置16字節的填充位,即通過偽.word指令定義4個word(32位)的空間來在代碼段最開始處占16字節。

在此處只是占據16字節的空間,校驗頭的正確值在編譯階段通過mkv210image.c中計算并填充的。

#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif

(2)設置異常向量表

.globl是globl是把_start這個標號全局化,是編譯器的操作,并不是匯編指令。_start代表程序start.S的入口。

這段代碼的功能是設置異常向量表。b reset 所處的位置是與異常向量表基地址偏移量為的0的地方,所以當復位異常發生時(開機也屬于復位異常),CPU會自動跳轉入異常表基址偏移量為0處執行復位異常程序,即跳轉執行reset部分的代碼。

這部分代碼只是構建了異常向量表,當每個異常向量指向的內容為空(除reset異常外)。因為U-Boot比較簡單,只是作為引導程序,所以不需要很細致的處理各種異常。

.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

(3)禁能IRQ和FIQ,使能SVC模式

開機后程序從此時正式開始運行。

程序上電之初,異常向量表未初始化,故先禁能IRQ(普通中斷)和FIQ(快速中斷)。

使能SVC模式,即超級用戶模式。SVC 模式,主要用于 SWI(軟件中斷)和 OS(操作系統)。這個模式有額外的特權,允許你進一步控制計算機。例如,你必須進入超級用戶模式來讀取一個插件(podule)。這不能在用戶模式下完成。

/*
 * the actual reset code
 */
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

(4)初始化相關全局變量

TEXT_BASE是U-Boot代碼的鏈接地址,在/uboot/board/samsung/config.mk文件中定義,該文件在/uboot/Makefile->x210_sd_config段中生成。TEXT_BASE = 0xc3e00000

_TEXT_BASE:
.word    TEXT_BASE

設置U-Boot在DDR中的物理地址,即運行地址,U-Boot重定位將整個U-Boot拷貝至DDR中的_TEXT_PHY_BASE。CFG_PHY_UBOOT_BASE在開發板配置文件(/uboot/include/configs/x210_sd_config)中定義,為0x33e00000

_TEXT_PHY_BASE:
.word    CFG_PHY_UBOOT_BASE

在開啟MMU之后,虛擬地址段0xc0000000-0xd0000000將被映射到物理地址段0x30000000-0x40000000。所以TEXT_BASE = 0xc3e00000對應的物理地址為TEXT_BASE =0x33e00000,即U-Boot的鏈接地址與運行地址一致。

(5)禁用Cache和MMU

caches是CPU的緩沖區,它的作用是存放常用的數據和指令,提高cpu與內存之間數據與指令的傳輸速率。

MMU是CPU的內存管理單元,它的作用是轉換虛擬地址與物理地址。

關閉caches的原因:上電初始,DDR未初始化,當CPU從cache中取數據時,可能導致數據預取異常。另一方面,當匯編指令讀取緩存數據,而實際物理地址對應的數據發生變化,導致CPU不能獲取最新的數據。在C語言中無需關閉caches,因為C語言中可以使用volatile關鍵字避免上述情況。

關閉MMU的原因:U-Boot的作用是硬件初始化和引導操作系統,純粹的初始化階段,開啟MMU會導致這個過程更復雜。

cpu_init_crit:
    .................................................
     /*
    * disable MMU stuff and caches
    */
    mrc    p15, 0, r0, c1, c0, 0
    bic    r0, r0, #0x00002000 @ clear bits 13 (--V-)
    bic    r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
    orr    r0, r0, #0x00000002 @ set bit 1 (--A-) Align
    orr    r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
    mcr    p15, 0, r0, c1, c0, 0

(6)判斷啟動介質

先從(PRO_ID_BASE+OMR_OFFSET)地址的寄存器讀取啟動介質信息,經過數據處理之后放入r2寄存器中。

然后通過比較r2的值來判定啟動介質,經過判斷得到當前的啟動介質為SD/MMC,在把BOOT_MMCSD宏寫入r3中。

最后將啟動介質信息從寄存器r3中寫入(INF_REG_BASE+INF_REG3_OFFSET)地址的寄存器中

/* Read booting information */  
      ldr    r0, =PRO_ID_BASE
      ldr    r1, [r0,#OMR_OFFSET]
      bic    r2, r1, #0xffffffc1
............................................

     /* SD/MMC BOOT */
  cmp     r2, #0xc
  moveq   r3, #BOOT_MMCSD
    .............................................
     ldr    r0, =INF_REG_BASE
  str    r3, [r0, #INF_REG3_OFFSET]

(7)第一次初始化棧(SRAM中)

第一次設置棧,由于不設置棧的話無法使用嵌套bl跳轉指令,即雙層函數調用,因為只有一個LR寄存器,而后面的lowlevel_init就有雙層跳轉,故這里開始設置SRAM中用的棧。

這里棧設置的地址并沒有按照s5pv210的推薦地址,將0xd0036000地址下12Bytes內存空間設為棧內存,只要保證該段內存不會被其他程序占用即可。

ldr    sp, =0xd0036000 /* end of sram dedicated to u-boot */
sub    sp, sp, #12    /* set stack */
mov    fp, #0

(8)跳轉執行lowlevel_init部分

/uboot/board/x210/lowlevel_init

1)判斷復位的類型

復位分為多種情況,如冷啟動、休眠喚醒等。

判斷復位類型的意義在于:冷啟動需要重新初始化DDR,而休眠喚醒不需要再次初始化DDR。

/* check reset status  */
ldr    r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr    r1, [r0]
bic    r1, r1, #0xfff6ffff
cmp    r1, #0x10000
beq    wakeup_reset_pre
cmp    r1, #0x80000
beq    wakeup_reset_from_didle

2)關看門狗

U-Boot主要功能是初始化硬件資源和引導OS,基本不會出現程序跑飛的情況。因此,為避免喂狗的麻煩,等U-Boot一切就緒正常運行后,再打開看門狗。

/* Disable Watchdog */
ldr    r0, =ELFIN_WATCHDOG_BASE    /* 0xE2700000 */
mov    r1, #0
str    r1, [r0]

3)開發板供電鎖存

設置開發板的供電按鍵鎖存功能。注意是哪個gpio,移植過程可能需要修改。

/* 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]

4)檢測程序當前的執行位置(SRAM或DDR)

本段的功能是檢測當前代碼的執行位置。判斷是在SRAM中還是DDR中,即CPU是冷啟動還是休眠喚醒復位,從而來決定是否要跳過后面的時鐘、DDR的初始化代碼。

bic是位清除指令,其功能是:將pc中的某些位清零(r0中為1的位清零),剩下一些特殊的bit位賦值給r1。

比較鏈接地址和當前地址的特定位,即比較r1和r2。若比較鏈接地址和當前地址的特定位相等,說明當前代碼處于DDR中(休眠喚醒),則跳轉到標號1處執行后面的代碼(即跳過時鐘、DDR的初始化代碼);否則,繼續執行后續的時鐘、DDR的初始化代碼。

ldr    r0, =0xff000fff
bic    r1, pc, r0    /* r0 <- current base addr of code */
ldr    r2, _TEXT_BASE    /* r1 <- original base addr in ram */
bic    r2, r2, r0    /* r0 <- current base addr of code */
cmp     r1, r2          /* compare r0, r1            */
beq     1f    /* r0 == r1 then skip sdram init  */

5)初始化時鐘、DDR、串口

跳轉執行時鐘初始化函數system_clock_init,然后返回。時鐘的相關參數可以在開發板配置文件(/uboot/include/configs/x210_sd.h)中修改,移植過程基本不需要需改該部分代碼。

跳轉執行DDR初始化函數mem_ctrl_asm_init,然后返回。其中,DMC0,DMC1等設置很重要,直接和板子上內存的大小和分布有關,需要注意。DDR的相關參數可以在開發板配置文件(/uboot/include/configs/x210_sd.h)中修改。

跳轉執行串口初始化函數uart_asm_init,成功執行后打印‘O’,然后返回。‘O’可作為調試的幫助。

若定義了ONFIG_ONENAND,則初始化ONENAND;若定義了CONFIG_NAND,則初始化NAND。

在返回start.S前打印了‘K’,與之前的‘O’組成“OK”,這是uboot的第一條打印信息,可以用來判斷lowlevel_init是否正常運行。

把之前保存在棧中的lr值彈出到pc中,來返回到start.S。

/* init system clock */
bl system_clock_init

/* Memory initialize */
bl mem_ctrl_asm_init

1:
/* for UART */
bl uart_asm_init   //初始化完成打印'O'

bl tzpc_init           //基本沒用

#if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif

#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif
    ..............................................
        /* Print 'K' */
ldr    r0, =ELFIN_UART_CONSOLE_BASE
ldr    r1, =0x4b4b4b4b
str    r1, [r0, #UTXH_OFFSET]

pop    {pc}

(9)再次供電鎖存

從lowlevel_init中跳轉回start.S中,再次進行開發板供電鎖存設置(在lowlevel_init中已經設置過一次),可能為代碼的冗余。注意引腳號。

ldr    r0, =0xE010E81C       /* PS_HOLD_CONTROL register */
ldr    r1, =0x00005301     /* PS_HOLD output high    */
str    r1, [r0]

(10)第二次初始化棧(DDR中)

為了即將執行的c程序做準備,這里開始第二次設置棧,設置于DDR中。這里將棧設置在_TEXT_PHY_BASE,即U-Boot在DDR中的真正物理地址(U-Boot運行地址)。由于棧是滿減棧,所以緊挨著uboot放置也不會沖突。

/* get ready to call C functions */
ldr    sp, _TEXT_PHY_BASE    /* setup temp stack pointer */
sub    sp, sp, #12
mov    fp, #0    /* no previous frame, so fp=0 */

(11)再次檢測當前程序執行地址(SRAM或DDR)

在lowlevel_init中,檢測當前代碼的執行位置。判斷是在SRAM中還是DDR中,即cpu是冷啟動還是休眠喚醒復位,從而來決定是否要跳過后面的時鐘、DDR的初始化代碼。

在此處再次檢測當前代碼的執行位置,從而來決定是否要跳過重定位代碼。

ldr    r0, =0xff000fff
bic    r1, pc, r0         /* r0 <- current base addr of code */
ldr    r2, _TEXT_BASE       /* r1 <- original base addr in ram */
bic    r2, r2, r0         /* r0 <- current base addr of code */
cmp     r1, r2          /* compare r0, r1            */
beq     after_copy    /* r0 == r1 then skip flash copy  */

(12)通過引腳來判斷啟動介質

這一段區別于之前的啟動介質判斷,本段是通過引腳來判斷啟動介質的

將0xD0037488地址的寄存器的值放入r0,該寄存器里的值可以判斷BL1是從SD/MMC哪個通道啟動的,將寄存器的值放入r1。這個寄存器的信息在irom applicationnote里有記載。

將0xEB200000這個值放入r2,該值的表示是2號方式啟動,并將寄存器的值和r2(0xEB200000)進行對比。如果相同則說明BL1是從SD卡通道2啟動,跳轉入標號mmcsd_boot處執行SD/MMC重定位相關代碼(/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy)。

若不是從SD卡通道2啟動,則讀取之前存儲的啟動介質信息(INF_REG_BASE+INF_REG3_OFFSET地址的寄存器),該信息有上一次啟動介質檢測所得(見(6))。隨后,跳轉至啟動介質相應的xxx_boot代碼中執行重定位相關的代碼(/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy)。

#if defined(CONFIG_EVT1)

/* If BL1 was copied from SD/MMC CH2 */

ldr    r0, =0xD0037488
ldr    r1, [r0]
ldr    r2, =0xEB200000
cmp    r1, r2
beq     mmcsd_boot

#endif

ldr    r0, =INF_REG_BASE
ldr    r1, [r0, #INF_REG3_OFFSET]
cmp    r1, #BOOT_NAND    /* 0x0 => boot device is nand */
beq    nand_boot
cmp    r1, #BOOT_ONENAND    /* 0x1 => boot device is onenand */
beq    onenand_boot
cmp     r1, #BOOT_MMCSD
beq     mmcsd_boot
cmp     r1, #BOOT_NOR
beq     nor_boot
cmp     r1, #BOOT_SEC_DEV
beq     mmcsd_boot

nand_boot:
mov    r0, #0x1000
bl    copy_from_nand
b    after_copy

onenand_boot:
bl    onenand_bl2_copy
b    after_copy

mmcsd_boot:
#if DELETE
ldr     sp, _TEXT_PHY_BASE      
sub     sp, sp, #12
mov     fp, #0
#endif
bl      movi_bl2_copy
b       after_copy

nor_boot:
bl      read_hword
b       after_copy

(13)進行U-Boot重定位

具體代碼在/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy中。

定義一個函數指針類型copy_sd_mmc_to_mem。

typedef u32(*copy_sd_mmc_to_mem) (u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);

函數返回值 u32代表函數執行成功與否
u32 channel 代表SD/MMC啟動通道號(0-4)
u32 start_block 起始塊地址
u16 block_size 復制的塊的個數
u32 *trg 復制操作的目的地址,一般是DDR內的地址
u32 init init一般給0,不用多管

首先讀取位于0xD0037488的寄存器值至變量ch中,再一次檢查啟動介質(SD卡的通道號)。

然后0xD0037F98地址中的指針變量強制轉換為copy_sd_mmc_to_mem類型,并賦值給copy_bl2。該指針變量指向一個函數,這個函數就是CPU的IROM中固化用來復制SD/mmc中的內容至任意地址的函數。

最后,根據ch變量的值判斷SD的通道號,并將整個U-Boot從相應的SD通道號中拷貝至DDR的指定地址(CFG_PHY_UBOOT_BASE =_TEXT_PHY_BASE =0x33e00000)處。

MOVI_BL2_POS是燒錄uboot時的扇區,MOVI_BL2_BLKCNT是uboot占的扇區數,具體的定義和計算都在/uboot/include/movi.h中。CFG_PHY_UBOOT_BASE為U-Boot的在DDR中的物理地址(運行地址),具體的定義和計算都在/uboot/include/configs/x210_sd.h中。

void movi_bl2_copy(void){
ulong ch;

    ch = *(volatile u32 *)(0xD003A508);

copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem) (*(u32 *) (0xD003E008));

    u32 ret;

 if (ch == 0xEB000000) //SD卡通道0

    {
ret = copy_bl2(0, MOVI_BL2_POS, MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE, 0);    
     }
else if (ch == 0xEB200000) //SD卡通道2
    {
       .................
    }
}

(14)建立虛擬地址映射表并開啟MMU

從movi_bl2_copy返回之后,將跳轉至start.S->after_copy,建立虛擬地址映射表并開啟MMU。

bl      movi_bl2_copy
b       after_copy

TTB(TranslationTableBase),即轉換表的基地址。轉換表是MMU將虛擬地址映射為物理地址的憑據,建立整個虛擬地址映射的關鍵就是建立轉換表,此表存放在內存中,工作時不需要軟件干涉。

只要將轉換表TTB的基地址(_mmu_table_base)放入cp15的c2寄存器,MMU就能自動使用虛擬地址映射。_mmu_table_base定義在start.S其值為標號mmu_table,mmu_table在lowlevel_init中定義。

最后通過設置cp15的c1寄存器來開啟mmu,以實現虛擬地址映射和內存訪問權限管理等功能。

#if defined(CONFIG_ENABLE_MMU)

/*使能域訪問*/
enable_mmu:    
ldr    r5, =0x0000ffff    
mcr    p15, 0, r5, c3, c0, 0    @load domain access register    

/*將轉換表ttb的基地址放入cp15的c2寄存器,mmu就能自動使用虛擬地址映射*/    
ldr    r0, _mmu_table_base    ldr    r1, =CFG_PHY_UBOOT_BASE    
ldr    r2, =0xfff00000    
bic    r0, r0, r2    
orr    r1, r0, r1    
mcr    p15, 0, r1, c2, c0, 0    

/*通過設置cp15的c1寄存器來開啟mmu,以實現虛擬地址映射和內存訪問權限管理等功能*/    
mmu_on:    
mrc    p15, 0, r0, c1, c0, 0    
orr    r0, r0, #1    
mcr    p15, 0, r0, c1, c0, 0    
nop    
nop    
nop    
nop

#endif

詳細的建表步驟在/uboot/board/x210/lowlevel_init。

映射表中規定了內存映射和管理是以塊為單位的,在ARM中支持3種塊大小,細表1KB、粗表4KB、段1MB。真正的轉換表就是由若干個轉換表單元構成的,每個單元負責1個內存塊,總體的轉換表負責整個內存空間(0-4G,32位CPU)的映射。

帶參宏將FL_SECTION_ENTRY base,ap,d,c,b定義成一個word大小的特定值,這個特定值就是轉換表的填充量。

參數分析:參數base是映射出來的段地址的基地址,從第20位開始。20位的大小正好是1MB,故此映射表采用的是段式映射;ap是訪問控制位,從第10位開始。d、c、b都是一些權限位。

#ifdef CONFIG_ENABLE_MMU    
#ifdef CONFIG_MCP_SINGLE    
   
.macro FL_SECTION_ENTRY base,ap,d,c,b //macro指令是匯編中宏定義
.word (base << 20) | (ap << 10) | 
          (d << 5) | (1<<4) | (c << 3) | ( << 2) | (1<<1)
    .endm                  //即結束宏定義


.section .mmudata, "a"
.align 14
.globl mmu_table

mmu_table:    
.set __base,0   //設置變量__base值為
/*.rept 0x100相當于for循環,一共循環0x100次,所以這一塊代碼創建了0x100(256)個轉換表單元*/          
.rept 0x100               
/*利用剛才定義的帶參宏創建轉換表的內容,變量__base和3,0,0,0作為參數*/
FL_SECTION_ENTRY __base,3,0,0,0                                 
.set __base,__base+1      //相當于base=base+1
.endr                //結束循環

.........................................//后續繼續建表

由以上代碼分析,得出整張轉換表的設定如下。

輸入虛擬地址 輸出的物理地址 長度
0-10000000 0-10000000 256MB
10000000-20000000 0 256MB
20000000-60000000 20000000-60000000 1GB
60000000-80000000 0 512MB
80000000-b0000000 80000000-b0000000 768MB
b0000000-c0000000 b0000000-c0000000 256MB
c0000000-d0000000 30000000-40000000 256MB
d-完 d-完 768MB

由此可知,此表僅僅將c0000000開頭的256MB映射到了DMC0的30000000開頭的256MB物理內存,其他的虛擬地址空間是原樣映射的。所以uboot的鏈接地址(0xc3e00000)對應物理地址(0x33e00000),即U-Boot的鏈接地址和運行地址(CFG_PHY_UBOOT_BASE =0x33e00000)一致。

(15)第三次初始化棧(DDR中)

第三次設置棧,仍然設在DDR中。雖然上一次已經在DDR中設置過,但是是緊挨著uboot存放的,位置不合理。

所以本次將棧設置uboot鏈接地址上方2MB處,這個位置合理、緊湊、安全。

stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
    ldr    sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000

(16)清bss段

_bss_start和_bss_end是鏈接腳本中定義的。

利用循環清零。

clear_bss:
ldr    r0, _bss_start         /* find start of bss segment     */
ldr    r1, _bss_end    /* stop here                 */
mov     r2, #0x00000000    /* clear                   */


clbss_l:
str    r2, [r0]          /* clear loop...               */
add    r0, r0, #4
cmp    r0, r1
ble    clbss_l

(17)遠跳轉至start_armboot(U-Boot第二階段)

從SRAM中遠跳轉到DDR中函數start_armboot處,start_armboot定義在根目錄下/uboot/lib_arm/board.c中。

至此,uboot第一階段結束,進入第二階段,至DDR處執行。

ldr    pc, _start_armboot
_start_armboot:
    .word start_armboot

2.2 第二階段(/uboot/lib_arm/board.c->start_armboot)

(1)初始化全局變量

1)定義一個函數指針類型。

typedef int (init_fnc_t) (void);

2)定義并初始化init_sequence數組

定義并初始化一個全局數組init_sequence,其元素是指向init_fnc_t類型函數的指針,這些函數都是用來初始化各個功能的函數。這些函數的具體功能如下:

cpu_init:CPU初始化函數,但cpu初始化工作已在U-Boot的第一階段完成,所以該函數為空。

board_init:開始板級初始化函數,配置網卡用到的GPIO、機器碼、內存傳參地址,并填充至gd結構體。

interrupt_init:定時器的初始化函數,和中斷無關(應用,如bootdelay)。

env_init:環境變量的初始化函數,由于環境變量還沒從啟動介質中取到DDR中,故此處的初始化只是對DDR中的環境變量(U-Boot自帶的環境變量)進行一個簡單的判定,檢測其是否可用。真正的初始化在start_armboot里面。U-Boot支持多種不同的啟動介質(如norflash、nandflash、sd/mmc),而各種介質存取操作env的方法都是不同的,故U-Boot中包含在多個文件中包含了env_init函數。而此U-Boot的啟動介質為SD/MMC,故當前的env_init函數在/uboot/common/Env_movi.c文件中。

init_baudrate:串口波特率初始化函數,設置串口波特率,并填充至gd結構體。具體的串口初始化工作已在U-Boot的第一階段完成(lowlevel_init)。

serial_init:串口初始化函數,但具體的串口初始化工作已在U-Boot的第一階段完成(lowlevel_init),所以該函數為空。

console_init_f:控制臺初始化函數,名字中的_f表示這是第一階段的初始化,由于第二階段的初始化之前需要夾雜一些前提代碼,故將在start_armboot執行。

display_banner:用來通過串口控制臺顯示U-Boot的版本信息(logo)。

print_cpuinfo:打印CPU的信息。

checkboard:確認開發板信息。即,打印出當前開發板的名稱信息。

init_func_i2c:I2C初始化函數,可在開發板配置文件(/uboot/include/configs/x210_sd.h)中設置U-Boot是否使用I2C。

dram_init:DDR初始化函數,由于DDR硬件層面的初始化已在第一階段完成,故此函數只是通過宏來獲取DDR相關信息,并填充至gd結構體。DDR信息相關的宏在開發板配置文件(/uboot/include/configs/x210_sd.h)中修改。

display_dram_config:打印bd中的DDR配置信息(由dram_init獲取)

init_fnc_t *init_sequence[] = {    
cpu_init,    /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
reloc_init,   /* Set the relocation done flag, mustdo this AFTER cpu_init()*/
#endif    
board_init,    /* basic board dependent setup */    
interrupt_init,    /* set up exceptions */    
env_init,        /* initialize environment */    
init_baudrate,    /* initialze baudrate settings */    
serial_init,    /* serial communications setup */    
console_init_f,    /* stage 1 init of console */    
display_banner,    /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)    
print_cpuinfo,    /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)    
checkboard,    /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)    
init_func_i2c,
#endif    
dram_init,    /* configure available RAM banks */    
display_dram_config,    
NULL,
};

3)初始化全局變量結構體gd_t、bd_t

本段功能是初始化全局變量結構體。

gd_t是保存全集變量的結構體類型,在/uboot/include/asm-arm/Global_data.h文件中定義。

typedef    struct    global_data {   
bd_t *bd;//該指針指向bd_t結構體,其具體內容涉與板級硬件資源信息相關的全局變量   
unsigned long    flags;  
unsigned long    baudrate;    //串口波特率   
unsigned long    have_console;//標志位,是否使用控制臺console   
unsigned long    reloc_off;   //重定位有關偏移量   
unsigned long    env_addr;    //環境變量結構體的偏移量   
usigned long    env_valid;   //標志位,表示內存中的環境變量能否使用   
unsigned long    fb_base;     //幀緩存基地址,和顯示有關
#ifdef CONFIG_VFD   
unsigned char    vfd_type;    /* display type */
#endif   
void    **jt;    /* jump table *///基本無用
} gd_t;

gd_t結構體中的bd指向一個bd_t結構體,bd_t結構體存放的是和開發板硬件相關的全局變量,在/uboot/include/asm-arm/U-Boot.h文件中定義。

typedef struct bd_info {    
int    bi_baudrate;         //串口波特率    
unsigned long    bi_ip_addr;    //IP地址    
unsigned char    bi_enetaddr[6]; //MAC地址    
struct environment_s    *bi_env;    
ulong bi_arch_number;    //機器碼    
ulong    bi_boot_params;    //U-Boot給內核傳參的地址    
struct    //DDR相關信息    
{            
ulong start;        
ulong size;     
} 
bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1    
unsigned char bi_enet1addr[6];
#endif
} bd_t;

gd(globle data)是指向gd_t結構體的指針,在/uboot/include/asm-arm/Global_data.h文件中定義。

/*register表示盡量讓cpu放在寄存器中,以提高其讀寫速度;asm (“r8”)是指定放在寄存器的r8*/

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

給gd結構體指針分配內存地址,以后可通過gd指針訪問全局變量結構體gd_t。

#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */  

ulong gd_base;  

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);

...............  

gd = (gd_t*)gd_base;

#endif

為bd_t結構體分配內存空間,將gd下的一段內存空間分配給bd_t。

清空gd_t、bd_t結構體。

memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t))

(2)運行init_sequence數組中所有的初始化函數

利用for循環遍歷init_sequence數組,并執行初始化函數。若函數返回值為0,則說明初始化函數執行錯誤,掛起程序,進入死循環。

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) 
{
if ((*init_fnc_ptr)() != 0) 
{
hang ();
}
}

(3)初始化堆內存

配置堆內存的起止地址、終止地址。

#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
    mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif

(4)初始化外部存儲設備

若在開發板配置文件中配置過外部存儲設備,則進行相應的初始化。

#if defined(CONFIG_SMDKC110)

#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC:  ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB
");
}
#endif

#if defined(CONFIG_MTD_ONENAND)
puts("OneNAND: ");
onenand_init();
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
#else
//puts("OneNAND: (FSR layer enabled)
");
#endif

#if defined(CONFIG_CMD_NAND)
puts("NAND:    ");
nand_init();
#endif

#endif /* CONFIG_SMDKC110 */

(5)環境變量的重定位

環境變量的重定位,將環境變量從啟動介質中讀到DDR內,環境變量的位置是通過原始分區信息表中讀到的。

/* initialize environment */
env_relocate ();

(6)獲取IP地址和MAC地址

從重定位之后的環境變量中獲取IP地址和MAC地址(以太網地址),放到bd中的全局變量內,以供使用。

/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];

i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;


for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}

(7)其他函數

devices_init ();     /* get the devices list going. *///設備驅動初始化,從linux中移植而來

jumptable_init ();//跳轉表初始化,其實沒用

console_init_r (); //控制臺的第二部分的初始化,有實質性的功能
enable_interrupts ();//開啟中斷,實質是一個空函數,U-Boot中并沒有使用中斷

(8)進入main_loop循環

至此,U-Boot啟動第二階段結束。即整個U-Boot啟動完成,進入main_loop循環。若控制臺有任意輸入,則進入控制臺命令解析-執行的循環。若無,則U-Boot將啟動內核。

for (;;) 
{
    main_loop ();
}

至此,U-Boot啟動完成。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 嵌入式
    +關注

    關注

    5068

    文章

    19017

    瀏覽量

    303245
  • soc
    soc
    +關注

    關注

    38

    文章

    4120

    瀏覽量

    217933
  • C語言
    +關注

    關注

    180

    文章

    7598

    瀏覽量

    136184
  • u-boot
    +關注

    關注

    0

    文章

    120

    瀏覽量

    38200
  • 代碼
    +關注

    關注

    30

    文章

    4747

    瀏覽量

    68349

原文標題:u-boot 啟動內幕

文章出處:【微信號:技術讓夢想更偉大,微信公眾號:技術讓夢想更偉大】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    U-Boot介紹

    在移植 Linux之前我們需要先移植一個 bootloader 代碼,這個 bootloader 代碼用于啟動 Linux 內核, bootloader有很多,常用的就是 U-Boot
    的頭像 發表于 10-08 10:50 ?3945次閱讀

    u-boot啟動流程分析

    今天給大家全面的分析一下u-boot啟動流程。整理這篇文章花費時間較長,中間很長時間未更新,希望這篇文章對大家有所幫助。
    發表于 07-12 15:16 ?757次閱讀
    <b class='flag-5'>u-boot</b><b class='flag-5'>啟動</b>流程<b class='flag-5'>分析</b>

    U-Boot啟動過程--詳細版的完全分析

    Boot Record,主引導記錄)中的Bootloader(例如LILO或GRUB),并進一步引導操作系統的啟動。然而在嵌入式系統中通常沒有像BIOS那樣的固件程序,因此整個系統的加載啟動就完全由bootloader來完成。
    發表于 12-12 22:50

    基于S3C44B0的U-Boot啟動分析和移植實現

    U-Boot 是sourceforge網站上的一個開源項目,是當今比較流行、功能強大的BootLoader,能支持多種體系結構。本文在闡述U-Boot啟動過程之后,對其在S3C44B0上移植
    發表于 08-27 09:08 ?37次下載

    U-Boot啟動及移植分析

    bootloader 開發是嵌入式系統必不可少而且十分重要的部分,U-Boot 為功能強大的bootloader 開發軟件。本文詳細分析U-Boot啟動流程,并結合其源碼,闡述了
    發表于 09-01 16:34 ?27次下載

    u-boot的Makefile分析

    u-boot的Makefile分析 U-BOOT是一個LINUX下的工程,在編譯之前必須已經安裝對應體系結構的交叉編譯環境,這里只針對ARM,編譯器系列軟件為arm-linux-*。 U-
    發表于 05-17 09:16 ?2057次閱讀

    嵌入式U-BOOT啟動流程及移植

    摘要:嵌入式系統一般沒有通用的bootloader,u-boot是功能強大的bootloader開發軟件,但相對也比較復雜。文中對u-boot啟動流程作了介紹,詳細給出了u-boot
    發表于 02-25 16:00 ?59次下載

    u-boot學習指南

    到自己的開發板上,這個過程主要是修改主芯片相關代碼以及開發板硬件相關代碼,包括啟動文件 Start.s、NAND 讀寫程序、USB 通信程序、相應的 IO 口配置等開發板上的資源,這里
    發表于 11-17 15:54 ?2次下載

    詳解U-Boot引導內核分析

    bootm命令是用來引導經過U-Boot的工具mkimage打包后的kernel image的。U-Boot代碼的tools/目錄下有mkimage工具,這個工具可以用來制作不壓縮或者壓縮的多種可
    的頭像 發表于 04-13 15:22 ?5231次閱讀
    詳解<b class='flag-5'>U-Boot</b>引導內核<b class='flag-5'>分析</b>

    fireflyFace-RK3399主板U-Boot模式啟動

    RK U-Boot 基于開源的 U-Boot 進行開發,工作模式有啟動加載模式和下載模式。
    的頭像 發表于 12-04 08:52 ?4486次閱讀
    fireflyFace-RK3399主板<b class='flag-5'>U-Boot</b>模式<b class='flag-5'>啟動</b>

    fireflyAIO-3399C主板U-Boot介紹

    RK U-Boot 基于開源的 U-Boot 進行開發,工作模式有啟動加載模式和下載模式。
    的頭像 發表于 12-04 10:31 ?1412次閱讀

    fireflyAIO-3288C主板U-Boot介紹

    RK U-Boot 基于開源的 U-Boot 進行開發,工作模式有啟動加載模式和下載模式。
    的頭像 發表于 12-16 13:52 ?1130次閱讀
    fireflyAIO-3288C主板<b class='flag-5'>U-Boot</b>介紹

    fireflyAIO-3288J主板U-Boot使用簡介

    RK U-Boot 基于開源的 U-Boot 進行開發,工作模式有啟動加載模式和下載模式。
    的頭像 發表于 12-20 10:06 ?1536次閱讀
    fireflyAIO-3288J主板<b class='flag-5'>U-Boot</b>使用簡介

    fireflyAIO-3399J主板U-Boot使用介紹

    RK U-Boot 基于開源的 U-Boot 進行開發,工作模式有啟動加載模式和下載模式。
    的頭像 發表于 12-24 10:00 ?1611次閱讀
    fireflyAIO-3399J主板<b class='flag-5'>U-Boot</b>使用介紹

    u-boot在匯編啟動階段的相關操作介紹

    u-boot在匯編啟動階段對系統的一些初始化 當cpu交由u-boot接管進入u-boot后, 首先會到_start符號處開始執行初始化, 并在此期間完成一些必要的系統寄存器相關的初始
    的頭像 發表于 12-07 11:22 ?576次閱讀