0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

U-Boot啟動(dòng)過(guò)程概述 U-Boot啟動(dòng)代碼具體分析

技術(shù)讓夢(mèng)想更偉大 ? 來(lái)源:技術(shù)讓夢(mèng)想更偉大 ? 2023-11-27 10:24 ? 次閱讀

1. U-Boot啟動(dòng)過(guò)程概述

U-Boot的啟動(dòng)過(guò)程分為兩個(gè)階段。

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

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

紅色字體部分和板級(jí)關(guān)系較大,是移植的重點(diǎn)修改部分。

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

2. U-Boot啟動(dòng)代碼具體分析

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

(0)頭文件包含

/uboot/include/config.h文件是在配置過(guò)程中中生成的(具體/uboot/mkconfig文件中實(shí)現(xiàn)),其中的內(nèi)容就是“#include ”,即包含開(kāi)發(fā)板的配置頭文件。

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

U-Boot中的頭文件包含其實(shí)都不是真正被包含的文件,它們大多是在配置編譯階段產(chǎn)生的符號(hào)鏈接或者是具有符號(hào)鏈接功能的頭文件(/uboot/mkconfig中創(chuàng)建符號(hào)鏈接)??傊鼈兊墓δ芏碱?lèi)似于符號(hào)鏈接,目的是讓uboot的源碼更具靈活性和移植性。

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

(1)填充16字節(jié)的校驗(yàn)位

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

在此處只是占據(jù)16字節(jié)的空間,校驗(yàn)頭的正確值在編譯階段通過(guò)mkv210image.c中計(jì)算并填充的。

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

(2)設(shè)置異常向量表

.globl是globl是把_start這個(gè)標(biāo)號(hào)全局化,是編譯器的操作,并不是匯編指令。_start代表程序start.S的入口。

這段代碼的功能是設(shè)置異常向量表。b reset 所處的位置是與異常向量表基地址偏移量為的0的地方,所以當(dāng)復(fù)位異常發(fā)生時(shí)(開(kāi)機(jī)也屬于復(fù)位異常),CPU會(huì)自動(dòng)跳轉(zhuǎn)入異常表基址偏移量為0處執(zhí)行復(fù)位異常程序,即跳轉(zhuǎn)執(zhí)行reset部分的代碼。

這部分代碼只是構(gòu)建了異常向量表,當(dāng)每個(gè)異常向量指向的內(nèi)容為空(除reset異常外)。因?yàn)閁-Boot比較簡(jiǎn)單,只是作為引導(dǎo)程序,所以不需要很細(xì)致的處理各種異常。

.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模式

開(kāi)機(jī)后程序從此時(shí)正式開(kāi)始運(yùn)行。

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

使能SVC模式,即超級(jí)用戶(hù)模式。SVC 模式,主要用于 SWI(軟件中斷)和 OS(操作系統(tǒng))。這個(gè)模式有額外的特權(quán),允許你進(jìn)一步控制計(jì)算機(jī)。例如,你必須進(jìn)入超級(jí)用戶(hù)模式來(lái)讀取一個(gè)插件(podule)。這不能在用戶(hù)模式下完成。

/*
 * 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)初始化相關(guān)全局變量

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

_TEXT_BASE:
.word    TEXT_BASE

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

_TEXT_PHY_BASE:
.word    CFG_PHY_UBOOT_BASE

在開(kāi)啟MMU之后,虛擬地址段0xc0000000-0xd0000000將被映射到物理地址段0x30000000-0x40000000。所以TEXT_BASE = 0xc3e00000對(duì)應(yīng)的物理地址為T(mén)EXT_BASE =0x33e00000,即U-Boot的鏈接地址與運(yùn)行地址一致。

(5)禁用Cache和MMU

caches是CPU的緩沖區(qū),它的作用是存放常用的數(shù)據(jù)和指令,提高cpu與內(nèi)存之間數(shù)據(jù)與指令的傳輸速率。

MMU是CPU的內(nèi)存管理單元,它的作用是轉(zhuǎn)換虛擬地址與物理地址。

關(guān)閉caches的原因:上電初始,DDR未初始化,當(dāng)CPU從cache中取數(shù)據(jù)時(shí),可能導(dǎo)致數(shù)據(jù)預(yù)取異常。另一方面,當(dāng)匯編指令讀取緩存數(shù)據(jù),而實(shí)際物理地址對(duì)應(yīng)的數(shù)據(jù)發(fā)生變化,導(dǎo)致CPU不能獲取最新的數(shù)據(jù)。在C語(yǔ)言中無(wú)需關(guān)閉caches,因?yàn)镃語(yǔ)言中可以使用volatile關(guān)鍵字避免上述情況。

關(guān)閉MMU的原因:U-Boot的作用是硬件初始化和引導(dǎo)操作系統(tǒng),純粹的初始化階段,開(kāi)啟MMU會(huì)導(dǎo)致這個(gè)過(guò)程更復(fù)雜。

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)判斷啟動(dòng)介質(zhì)

先從(PRO_ID_BASE+OMR_OFFSET)地址的寄存器讀取啟動(dòng)介質(zhì)信息,經(jīng)過(guò)數(shù)據(jù)處理之后放入r2寄存器中。

然后通過(guò)比較r2的值來(lái)判定啟動(dòng)介質(zhì),經(jīng)過(guò)判斷得到當(dāng)前的啟動(dòng)介質(zhì)為SD/MMC,在把BOOT_MMCSD宏寫(xiě)入r3中。

最后將啟動(dòng)介質(zhì)信息從寄存器r3中寫(xiě)入(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中)

第一次設(shè)置棧,由于不設(shè)置棧的話(huà)無(wú)法使用嵌套bl跳轉(zhuǎn)指令,即雙層函數(shù)調(diào)用,因?yàn)橹挥幸粋€(gè)LR寄存器,而后面的lowlevel_init就有雙層跳轉(zhuǎn),故這里開(kāi)始設(shè)置SRAM中用的棧。

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

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

(8)跳轉(zhuǎn)執(zhí)行l(wèi)owlevel_init部分

/uboot/board/x210/lowlevel_init

1)判斷復(fù)位的類(lèi)型

復(fù)位分為多種情況,如冷啟動(dòng)、休眠喚醒等。

判斷復(fù)位類(lèi)型的意義在于:冷啟動(dòng)需要重新初始化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)關(guān)看門(mén)狗

U-Boot主要功能是初始化硬件資源和引導(dǎo)OS,基本不會(huì)出現(xiàn)程序跑飛的情況。因此,為避免喂狗的麻煩,等U-Boot一切就緒正常運(yùn)行后,再打開(kāi)看門(mén)狗。

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

3)開(kāi)發(fā)板供電鎖存

設(shè)置開(kāi)發(fā)板的供電按鍵鎖存功能。注意是哪個(gè)gpio,移植過(guò)程可能需要修改。

/* PS_HOLD pin(GPH0_0) set to high 開(kāi)發(fā)板供電上鎖*/
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)檢測(cè)程序當(dāng)前的執(zhí)行位置(SRAM或DDR)

本段的功能是檢測(cè)當(dāng)前代碼的執(zhí)行位置。判斷是在SRAM中還是DDR中,即CPU是冷啟動(dòng)還是休眠喚醒復(fù)位,從而來(lái)決定是否要跳過(guò)后面的時(shí)鐘、DDR的初始化代碼。

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

比較鏈接地址和當(dāng)前地址的特定位,即比較r1和r2。若比較鏈接地址和當(dāng)前地址的特定位相等,說(shuō)明當(dāng)前代碼處于DDR中(休眠喚醒),則跳轉(zhuǎn)到標(biāo)號(hào)1處執(zhí)行后面的代碼(即跳過(guò)時(shí)鐘、DDR的初始化代碼);否則,繼續(xù)執(zhí)行后續(xù)的時(shí)鐘、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)初始化時(shí)鐘、DDR、串口

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

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

跳轉(zhuǎn)執(zhí)行串口初始化函數(shù)uart_asm_init,成功執(zhí)行后打印‘O’,然后返回?!甇’可作為調(diào)試的幫助。

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

在返回start.S前打印了‘K’,與之前的‘O’組成“OK”,這是uboot的第一條打印信息,可以用來(lái)判斷l(xiāng)owlevel_init是否正常運(yùn)行。

把之前保存在棧中的lr值彈出到pc中,來(lái)返回到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           //基本沒(méi)用

#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中跳轉(zhuǎn)回start.S中,再次進(jìn)行開(kāi)發(fā)板供電鎖存設(shè)置(在lowlevel_init中已經(jīng)設(shè)置過(guò)一次),可能為代碼的冗余。注意引腳號(hào)。

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

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

為了即將執(zhí)行的c程序做準(zhǔn)備,這里開(kāi)始第二次設(shè)置棧,設(shè)置于DDR中。這里將棧設(shè)置在_TEXT_PHY_BASE,即U-Boot在DDR中的真正物理地址(U-Boot運(yùn)行地址)。由于棧是滿(mǎn)減棧,所以緊挨著uboot放置也不會(huì)沖突。

/* 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)再次檢測(cè)當(dāng)前程序執(zhí)行地址(SRAM或DDR)

在lowlevel_init中,檢測(cè)當(dāng)前代碼的執(zhí)行位置。判斷是在SRAM中還是DDR中,即cpu是冷啟動(dòng)還是休眠喚醒復(fù)位,從而來(lái)決定是否要跳過(guò)后面的時(shí)鐘、DDR的初始化代碼。

在此處再次檢測(cè)當(dāng)前代碼的執(zhí)行位置,從而來(lái)決定是否要跳過(guò)重定位代碼。

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)通過(guò)引腳來(lái)判斷啟動(dòng)介質(zhì)

這一段區(qū)別于之前的啟動(dòng)介質(zhì)判斷,本段是通過(guò)引腳來(lái)判斷啟動(dòng)介質(zhì)的

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

將0xEB200000這個(gè)值放入r2,該值的表示是2號(hào)方式啟動(dòng),并將寄存器的值和r2(0xEB200000)進(jìn)行對(duì)比。如果相同則說(shuō)明BL1是從SD卡通道2啟動(dòng),跳轉(zhuǎn)入標(biāo)號(hào)mmcsd_boot處執(zhí)行SD/MMC重定位相關(guān)代碼(/uboot/cpu/s5pc11x/movi.c->movi_bl2_copy)。

若不是從SD卡通道2啟動(dòng),則讀取之前存儲(chǔ)的啟動(dòng)介質(zhì)信息(INF_REG_BASE+INF_REG3_OFFSET地址的寄存器),該信息有上一次啟動(dòng)介質(zhì)檢測(cè)所得(見(jiàn)(6))。隨后,跳轉(zhuǎn)至啟動(dòng)介質(zhì)相應(yīng)的xxx_boot代碼中執(zhí)行重定位相關(guān)的代碼(/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)進(jìn)行U-Boot重定位

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

定義一個(gè)函數(shù)指針類(lèi)型copy_sd_mmc_to_mem。

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

函數(shù)返回值 u32代表函數(shù)執(zhí)行成功與否
u32 channel 代表SD/MMC啟動(dòng)通道號(hào)(0-4)
u32 start_block 起始?jí)K地址
u16 block_size 復(fù)制的塊的個(gè)數(shù)
u32 *trg 復(fù)制操作的目的地址,一般是DDR內(nèi)的地址
u32 init init一般給0,不用多管

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

然后0xD0037F98地址中的指針變量強(qiáng)制轉(zhuǎn)換為copy_sd_mmc_to_mem類(lèi)型,并賦值給copy_bl2。該指針變量指向一個(gè)函數(shù),這個(gè)函數(shù)就是CPU的IROM中固化用來(lái)復(fù)制SD/mmc中的內(nèi)容至任意地址的函數(shù)。

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

MOVI_BL2_POS是燒錄uboot時(shí)的扇區(qū),MOVI_BL2_BLKCNT是uboot占的扇區(qū)數(shù),具體的定義和計(jì)算都在/uboot/include/movi.h中。CFG_PHY_UBOOT_BASE為U-Boot的在DDR中的物理地址(運(yùn)行地址),具體的定義和計(jì)算都在/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)建立虛擬地址映射表并開(kāi)啟MMU

從movi_bl2_copy返回之后,將跳轉(zhuǎn)至start.S->after_copy,建立虛擬地址映射表并開(kāi)啟MMU。

bl      movi_bl2_copy
b       after_copy

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

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

最后通過(guò)設(shè)置cp15的c1寄存器來(lái)開(kāi)啟mmu,以實(shí)現(xiàn)虛擬地址映射和內(nèi)存訪(fǎng)問(wèn)權(quán)限管理等功能。

#if defined(CONFIG_ENABLE_MMU)

/*使能域訪(fǎng)問(wèn)*/
enable_mmu:    
ldr    r5, =0x0000ffff    
mcr    p15, 0, r5, c3, c0, 0    @load domain access register    

/*將轉(zhuǎn)換表ttb的基地址放入cp15的c2寄存器,mmu就能自動(dòng)使用虛擬地址映射*/    
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    

/*通過(guò)設(shè)置cp15的c1寄存器來(lái)開(kāi)啟mmu,以實(shí)現(xiàn)虛擬地址映射和內(nèi)存訪(fǎng)問(wèn)權(quán)限管理等功能*/    
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

詳細(xì)的建表步驟在/uboot/board/x210/lowlevel_init。

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

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

參數(shù)分析:參數(shù)base是映射出來(lái)的段地址的基地址,從第20位開(kāi)始。20位的大小正好是1MB,故此映射表采用的是段式映射;ap是訪(fǎng)問(wèn)控制位,從第10位開(kāi)始。d、c、b都是一些權(quán)限位。

#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                  //即結(jié)束宏定義


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

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

.........................................//后續(xù)繼續(xù)建表

由以上代碼分析,得出整張轉(zhuǎn)換表的設(shè)定如下。

輸入虛擬地址 輸出的物理地址 長(zhǎng)度
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開(kāi)頭的256MB映射到了DMC0的30000000開(kāi)頭的256MB物理內(nèi)存,其他的虛擬地址空間是原樣映射的。所以u(píng)boot的鏈接地址(0xc3e00000)對(duì)應(yīng)物理地址(0x33e00000),即U-Boot的鏈接地址和運(yùn)行地址(CFG_PHY_UBOOT_BASE =0x33e00000)一致。

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

第三次設(shè)置棧,仍然設(shè)在DDR中。雖然上一次已經(jīng)在DDR中設(shè)置過(guò),但是是緊挨著uboot存放的,位置不合理。

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

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

(16)清bss段

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

利用循環(huán)清零。

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)遠(yuǎn)跳轉(zhuǎn)至start_armboot(U-Boot第二階段)

從SRAM中遠(yuǎn)跳轉(zhuǎn)到DDR中函數(shù)start_armboot處,start_armboot定義在根目錄下/uboot/lib_arm/board.c中。

至此,uboot第一階段結(jié)束,進(jìn)入第二階段,至DDR處執(zhí)行。

ldr    pc, _start_armboot
_start_armboot:
    .word start_armboot

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

(1)初始化全局變量

1)定義一個(gè)函數(shù)指針類(lèi)型。

typedef int (init_fnc_t) (void);

2)定義并初始化init_sequence數(shù)組

定義并初始化一個(gè)全局?jǐn)?shù)組init_sequence,其元素是指向init_fnc_t類(lèi)型函數(shù)的指針,這些函數(shù)都是用來(lái)初始化各個(gè)功能的函數(shù)。這些函數(shù)的具體功能如下:

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

board_init:開(kāi)始板級(jí)初始化函數(shù),配置網(wǎng)卡用到的GPIO、機(jī)器碼、內(nèi)存?zhèn)鲄⒌刂?,并填充至gd結(jié)構(gòu)體。

interrupt_init:定時(shí)器的初始化函數(shù),和中斷無(wú)關(guān)(應(yīng)用,如bootdelay)。

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

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

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

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

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

print_cpuinfo:打印CPU的信息。

checkboard:確認(rèn)開(kāi)發(fā)板信息。即,打印出當(dāng)前開(kāi)發(fā)板的名稱(chēng)信息。

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

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

display_dram_config:打印bd中的DDR配置信息(由dram_init獲?。?/p>

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)初始化全局變量結(jié)構(gòu)體gd_t、bd_t

本段功能是初始化全局變量結(jié)構(gòu)體。

gd_t是保存全集變量的結(jié)構(gòu)體類(lèi)型,在/uboot/include/asm-arm/Global_data.h文件中定義。

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

gd_t結(jié)構(gòu)體中的bd指向一個(gè)bd_t結(jié)構(gòu)體,bd_t結(jié)構(gòu)體存放的是和開(kāi)發(fā)板硬件相關(guān)的全局變量,在/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;    //機(jī)器碼    
ulong    bi_boot_params;    //U-Boot給內(nèi)核傳參的地址    
struct    //DDR相關(guān)信息    
{            
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結(jié)構(gòu)體的指針,在/uboot/include/asm-arm/Global_data.h文件中定義。

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

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

給gd結(jié)構(gòu)體指針?lè)峙鋬?nèi)存地址,以后可通過(guò)gd指針訪(fǎng)問(wèn)全局變量結(jié)構(gòu)體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結(jié)構(gòu)體分配內(nèi)存空間,將gd下的一段內(nèi)存空間分配給bd_t。

清空gd_t、bd_t結(jié)構(gòu)體。

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

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

利用for循環(huán)遍歷init_sequence數(shù)組,并執(zhí)行初始化函數(shù)。若函數(shù)返回值為0,則說(shuō)明初始化函數(shù)執(zhí)行錯(cuò)誤,掛起程序,進(jìn)入死循環(huán)。

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

(3)初始化堆內(nèi)存

配置堆內(nèi)存的起止地址、終止地址。

#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)初始化外部存儲(chǔ)設(shè)備

若在開(kāi)發(fā)板配置文件中配置過(guò)外部存儲(chǔ)設(shè)備,則進(jìn)行相應(yīng)的初始化。

#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)環(huán)境變量的重定位

環(huán)境變量的重定位,將環(huán)境變量從啟動(dòng)介質(zhì)中讀到DDR內(nèi),環(huán)境變量的位置是通過(guò)原始分區(qū)信息表中讀到的。

/* initialize environment */
env_relocate ();

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

從重定位之后的環(huán)境變量中獲取IP地址和MAC地址(以太網(wǎng)地址),放到bd中的全局變量?jī)?nèi),以供使用。

/* 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)其他函數(shù)

devices_init ();     /* get the devices list going. *///設(shè)備驅(qū)動(dòng)初始化,從linux中移植而來(lái)

jumptable_init ();//跳轉(zhuǎn)表初始化,其實(shí)沒(méi)用

console_init_r (); //控制臺(tái)的第二部分的初始化,有實(shí)質(zhì)性的功能
enable_interrupts ();//開(kāi)啟中斷,實(shí)質(zhì)是一個(gè)空函數(shù),U-Boot中并沒(méi)有使用中斷

(8)進(jìn)入main_loop循環(huán)

至此,U-Boot啟動(dòng)第二階段結(jié)束。即整個(gè)U-Boot啟動(dòng)完成,進(jìn)入main_loop循環(huán)。若控制臺(tái)有任意輸入,則進(jìn)入控制臺(tái)命令解析-執(zhí)行的循環(huán)。若無(wú),則U-Boot將啟動(dòng)內(nèi)核。

for (;;) 
{
    main_loop ();
}

至此,U-Boot啟動(dòng)完成。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5083

    文章

    19131

    瀏覽量

    305543
  • soc
    soc
    +關(guān)注

    關(guān)注

    38

    文章

    4169

    瀏覽量

    218350
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7605

    瀏覽量

    136934
  • u-boot
    +關(guān)注

    關(guān)注

    0

    文章

    121

    瀏覽量

    38229
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4790

    瀏覽量

    68654

原文標(biāo)題:u-boot 啟動(dòng)內(nèi)幕

文章出處:【微信號(hào):技術(shù)讓夢(mèng)想更偉大,微信公眾號(hào):技術(shù)讓夢(mèng)想更偉大】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    U-Boot介紹

    在移植 Linux之前我們需要先移植一個(gè) bootloader 代碼,這個(gè) bootloader 代碼用于啟動(dòng) Linux 內(nèi)核, bootloader有很多,常用的就是 U-Boot
    的頭像 發(fā)表于 10-08 10:50 ?4080次閱讀

    u-boot啟動(dòng)流程分析

    今天給大家全面的分析一下u-boot啟動(dòng)流程。整理這篇文章花費(fèi)時(shí)間較長(zhǎng),中間很長(zhǎng)時(shí)間未更新,希望這篇文章對(duì)大家有所幫助。
    發(fā)表于 07-12 15:16 ?820次閱讀
    <b class='flag-5'>u-boot</b><b class='flag-5'>啟動(dòng)</b>流程<b class='flag-5'>分析</b>

    U-Boot啟動(dòng)過(guò)程--詳細(xì)版的完全分析

    Boot Record,主引導(dǎo)記錄)中的Bootloader(例如LILO或GRUB),并進(jìn)一步引導(dǎo)操作系統(tǒng)的啟動(dòng)。然而在嵌入式系統(tǒng)中通常沒(méi)有像BIOS那樣的固件程序,因此整個(gè)系統(tǒng)的加載啟動(dòng)就完全由bootloader來(lái)完成。
    發(fā)表于 12-12 22:50

    基于S3C44B0的U-Boot啟動(dòng)分析和移植實(shí)現(xiàn)

    U-Boot 是sourceforge網(wǎng)站上的一個(gè)開(kāi)源項(xiàng)目,是當(dāng)今比較流行、功能強(qiáng)大的BootLoader,能支持多種體系結(jié)構(gòu)。本文在闡述U-Boot啟動(dòng)過(guò)程之后,對(duì)其在S3C44B0上移植過(guò)
    發(fā)表于 08-27 09:08 ?37次下載

    U-Boot啟動(dòng)及移植分析

    bootloader 開(kāi)發(fā)是嵌入式系統(tǒng)必不可少而且十分重要的部分,U-Boot 為功能強(qiáng)大的bootloader 開(kāi)發(fā)軟件。本文詳細(xì)分析U-Boot啟動(dòng)流程,并結(jié)合其源碼,闡述了
    發(fā)表于 09-01 16:34 ?27次下載

    u-boot的Makefile分析

    u-boot的Makefile分析 U-BOOT是一個(gè)LINUX下的工程,在編譯之前必須已經(jīng)安裝對(duì)應(yīng)體系結(jié)構(gòu)的交叉編譯環(huán)境,這里只針對(duì)ARM,編譯器系列軟件為arm-linux-*。 U-
    發(fā)表于 05-17 09:16 ?2071次閱讀

    嵌入式U-BOOT啟動(dòng)流程及移植

    摘要:嵌入式系統(tǒng)一般沒(méi)有通用的bootloader,u-boot是功能強(qiáng)大的bootloader開(kāi)發(fā)軟件,但相對(duì)也比較復(fù)雜。文中對(duì)u-boot啟動(dòng)流程作了介紹,詳細(xì)給出了u-boot
    發(fā)表于 02-25 16:00 ?59次下載

    u-boot學(xué)習(xí)指南

    到自己的開(kāi)發(fā)板上,這個(gè)過(guò)程主要是修改主芯片相關(guān)代碼以及開(kāi)發(fā)板硬件相關(guān)代碼,包括啟動(dòng)文件 Start.s、NAND 讀寫(xiě)程序、USB 通信程序、相應(yīng)的 IO 口配置等開(kāi)發(fā)板上的資源,這里
    發(fā)表于 11-17 15:54 ?2次下載

    詳解U-Boot引導(dǎo)內(nèi)核分析

    bootm命令是用來(lái)引導(dǎo)經(jīng)過(guò)U-Boot的工具mkimage打包后的kernel image的。U-Boot代碼的tools/目錄下有mkimage工具,這個(gè)工具可以用來(lái)制作不壓縮或者壓縮的多種可
    的頭像 發(fā)表于 04-13 15:22 ?5267次閱讀
    詳解<b class='flag-5'>U-Boot</b>引導(dǎo)內(nèi)核<b class='flag-5'>分析</b>

    fireflyFace-RK3399主板U-Boot模式啟動(dòng)

    RK U-Boot 基于開(kāi)源的 U-Boot 進(jìn)行開(kāi)發(fā),工作模式有啟動(dòng)加載模式和下載模式。
    的頭像 發(fā)表于 12-04 08:52 ?4561次閱讀
    fireflyFace-RK3399主板<b class='flag-5'>U-Boot</b>模式<b class='flag-5'>啟動(dòng)</b>

    fireflyAIO-3399C主板U-Boot介紹

    RK U-Boot 基于開(kāi)源的 U-Boot 進(jìn)行開(kāi)發(fā),工作模式有啟動(dòng)加載模式和下載模式。
    的頭像 發(fā)表于 12-04 10:31 ?1455次閱讀

    fireflyAIO-3288C主板U-Boot介紹

    RK U-Boot 基于開(kāi)源的 U-Boot 進(jìn)行開(kāi)發(fā),工作模式有啟動(dòng)加載模式和下載模式。
    的頭像 發(fā)表于 12-16 13:52 ?1159次閱讀
    fireflyAIO-3288C主板<b class='flag-5'>U-Boot</b>介紹

    fireflyAIO-3288J主板U-Boot使用簡(jiǎn)介

    RK U-Boot 基于開(kāi)源的 U-Boot 進(jìn)行開(kāi)發(fā),工作模式有啟動(dòng)加載模式和下載模式。
    的頭像 發(fā)表于 12-20 10:06 ?1574次閱讀
    fireflyAIO-3288J主板<b class='flag-5'>U-Boot</b>使用簡(jiǎn)介

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

    RK U-Boot 基于開(kāi)源的 U-Boot 進(jìn)行開(kāi)發(fā),工作模式有啟動(dòng)加載模式和下載模式。
    的頭像 發(fā)表于 12-24 10:00 ?1658次閱讀
    fireflyAIO-3399J主板<b class='flag-5'>U-Boot</b>使用介紹

    u-boot在匯編啟動(dòng)階段的相關(guān)操作介紹

    u-boot在匯編啟動(dòng)階段對(duì)系統(tǒng)的一些初始化 當(dāng)cpu交由u-boot接管進(jìn)入u-boot后, 首先會(huì)到_start符號(hào)處開(kāi)始執(zhí)行初始化, 并在此期間完成一些必要的系統(tǒng)寄存器相關(guān)的初始
    的頭像 發(fā)表于 12-07 11:22 ?641次閱讀