uboot的啓動第一階段流程梳理

u-boot 代碼分爲兩個階段第一階段是彙編,入口是 arch/arm/cpu/armv7/start.S,第二階段是 C 語言, 入口是 board.c。
 


第一階段:

1. 異常向量表定義
2. 設置 SVC32 模式(ARM 七種工作模式)
3. 調用 cpu_init_crit 進行 cpu 相關初始化
1) 清 TLB(頁面緩存)、關 MMU 及 Cache 等
2) 轉入低級初始化 lowlevel_init 函數
主要是對系統時鐘、片外內存(DDR3)、串口、 nand(這裏初始化 nand 主要是爲第二階段搬 uboot 到內存而準備的)等進行初始化。
4. 判斷啓動開關進行自搬移
5. 跳轉到 C 入口 board_init_f( )


具體過程分析:


1. 異常向量表定義
.globl _start //把_start 聲明爲全局標號
_start: b reset //跳轉到 rest 標號
ldr pc, _undefined_instruction //未定義異常入口
ldr pc, _software_interrupt //軟中斷異常入口
ldr pc, _prefetch_abort //預取指異常入口
ldr pc, _data_abort //取數據異常入口
ldr pc, _not_used //保留
ldr pc, _irq //irq 中斷入口(普通中斷)
ldr pc, _fiq //快速中斷入口
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq:        .word irq

_fiq:      .word fiq

_pad:      .word 0x12345678 /*now 16*4 = 64*/

2.進入管理模式

reset:
/* 進入管理模式 ,ARM 芯片進入管理模式代碼固定
* set the cpu to SVC32 mode 因爲在管理模式下具有超級權限,可以對芯片上的所有功能進行配
置, 以及模式自由切換。
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
3. 調用 cpu_init_crit 函數進行 CPU 底層初始化:
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //CPU 初始化
#endif
4. cpu_init_crit 子函數具體源碼分析
進行了 cache 初始化(關 cache),關 MMU, 失效 TLB(頁面緩存),最後調用 lowlevel_init 函數對
CPU 進行時鐘配置,存儲器控制器配置等。
cpu_init_crit:
//cache_init 在 lowlevel_init.S board\samsung\Tiny4412
bl cache_init //初始化高速緩存,其實什麼都沒有做
/* 使用協處理器指令,使 L1 緩存無效
* Invalidate L1 I/D cache ,mmu,mpu
*/
mov r0, #0   @set up for MCR

mcr p15,0,r0,c8,c7,0  @invalidate TLBs

mcr p15,0,r0,c7,c5,0   @invalidate icache

/*

禁止MMU 及caches

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

/*
* lowlevel_init 在
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
mov ip, lr @ persevere link reg across call
bl lowlevel_init @ go setup pll,mux,memory
mov lr, ip @ restore link
mov pc, lr @ back to my caller
5. lowlevel_init 源碼具體分析
檢測 cpu 當前狀態,判斷是否是由於休眠被喚醒運行到這裏的,如果是直接跳轉到 wakeup_reset,
否則則要進行代碼,時鐘配置, ddr 配置,代碼複製, 代碼重新定位。
1) 檢測是否是睡眠被喚醒的。
lowlevel_init.S board\samsung\Tiny4412
.globl lowlevel_init
lowlevel_init:
/* use iROM stack in bl2 */
//設置棧指針指向內部 RAM 的最後地址,因爲棧是往下生長的
ldr sp, =0x02060000
push{lr} //把返回地址壓入棧中保存
/* check reset status 檢查復位狀態 */
ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET)
ldr r1, [r0]
/* Sleep wakeup reset 測試是否是睡眠狀態喚醒的 */
ldr r2, =S5P_CHECK_SLEEP
cmp r1, r2 //比較兩個值是否相同
beq wakeup_reset //如果是睡眠狀態下喚醒執行的,直接跳轉到
2) 讀取啓動方式
如果不是休眠喚醒,則繼續以下代碼:
bl read_om //檢測啓動方式
/* 檢測 u-boot 自己是否已經在 RAM 中運行
* 如果已經在 RAM 中則不需要搬運了。
* 否則要進行存儲器參數配置,讓 RAM 工作,然後複製 u-boot 到 RAM
* 有一種可能,是通過仿真器把代碼直接下載到 DDR 中,這樣也不需要 對 DDR 進行初始化和代
碼複製。
* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
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 sdram init and u-boot.bin loading */
_TEXT_BASE 是 u-boot 鏈接地址,也就是 u-boot 應該在 ddr 中的內存地址。 以上代碼判斷 u-boot 是
否已經在 ddr中,如果在,則不需要進行時鐘,存儲器配置,以及代碼複製工作,直接跳轉到 after_copy
函數執行。
如沒有在內存,則繼續執行以下代碼:
通過讀取芯片的 ID 寄存器判斷芯片型號,從而執行不同的代碼, 這一步不是必須的,如果你的 uboot 想支持多個型號的芯片,可以仿照以下代碼添加。 在 tiny4412 中不執行下面的代碼。
#ifndef CONFIG_SMDKC220
ldr r0, =CHIP_ID_BASE
ldr r1, [r0]
lsr r1, r1, #8
and r1, r1, #3
cmp r1, #2
bne v310_1 //三星公司的 s5pv310 芯片 A8 芯片
#endif
以上代碼 4412 沒有用。
以下開始進行系統時鐘,存儲器控制器初始化, 這部分代碼是必須的。任何芯片, 任何開發板都是
必須的, 屬於主幹代碼。
/* 初始化系統時鐘
* 在 clock_init_tiny4412.S board\samsung\Tiny4412
* 屬於芯片級別代碼,不同芯片配置方法不同,需要參考具體的芯片手冊。 一般根據芯片公司的
演示板代碼進行修改,如果時鐘按照官方板的時鐘運行,則不需要進行任何修改。
*/
bl system_clock_init //調用系統時鐘初始化函數。
/* 存儲器控制器初始化,目的讓開發板的 DDR3 內存可以正常工作起來
* mem_init_tiny4412.S board\samsung\Tiny4412
* 這部分代碼屬於板級代碼。不同開發板使用的內存芯片可能 不同。時序參數就不同,用戶需要
根據自己使用的內存芯片參數修改。
*/
bl mem_ctrl_asm_init //初始化存儲器控制器
/* 調試串口初始化 , 不是必須的。
* lowlevel_init.S board\samsung\Tiny4412
*/
bl uart_asm_init
由於是 4412 芯片,直接路過 s5pv310 芯片初始化代碼。
b 1f
以下的 s5pv310 芯片代碼,和 4412 芯片無關, 所以上面直接跳過這部分代碼。
v310_1:
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init
進行 Trustzone 初始化(這個不是必須的,不是所有的芯片都具有 Trustzone 功能), 從啓動設備中
加載 u-boot.bin 代碼到 DDR3 中。
1:
//初始 Trustzone
bl tzpc_init //個別芯片
//跳轉到 load_uboot ,這個函數負責加載了 u-boot.bin 到 DDR3 中。
//lowlevel_init.S board\samsung\Tiny4412
b load_uboot //主幹,加載 u-boot
6. load_uboot 源碼具體分析
這個函數具體實現在 lowlevel_init.S board\samsung\Tiny4412。 這個函數具體工作是根據啓動方式,
調用不同的函數來實現代碼複製,如下:
load_uboot:
ldr r0, =INF_REG_BASE
ldr r1, [r0, #INF_REG3_OFFSET]
//判斷是否是 NAND 啓動
cmp r1, #BOOT_NAND
beq nand_boot //如果是則跳轉
//判斷是否是 ONENAND 啓動
cmp r1, #BOOT_ONENAND
beq onenand_boot
//判斷是否是 MMCSD 啓動
cmp r1, #BOOT_MMCSD
beq mmcsd_boot //跳轉到 mmcsd 啓動處去複製代碼到內存
//判斷是否是 EMMC 啓動
cmp r1, #BOOT_EMMC
beq emmc_boot
//判斷是否是 EMMC 通道 4 啓動
cmp r1, #BOOT_EMMC_4_4
beq emmc_boot_4_4
//判斷是否是 NOR 啓動
cmp r1, #BOOT_NOR
beq nor_boot
//判斷是否是第二啓動設備
cmp r1, #BOOT_SEC_DEV
beq mmcsd_boot //跳轉到 mmcsd 啓動處去複製代碼到內存
7. mmcsd_boot 源碼具體分析
以下以 sd 卡啓動方式來例子說明 u-boot 代碼複製功能。 根據以上代碼,如果是 sd 卡啓動,則會調
用 mmcsd_boot 函數進行代碼複製, mmcsd_boot 子函數代碼如下所示。
mmcsd_boot:
//主幹代碼,真正的複製,
//把 sd 中的 u-boot 複製 ddr 中的起始位置 0x40000000
//Irom_copy.c arch\arm\cpu\armv7\Exynos
bl movi_uboot_copy //調用 bl1 的代碼複製 mmcsd 的位置到 ddr 中。
//複製完成後,程序跳轉到 after_copy 函數
b after_copy
先分析 movi_uboot_copy 函數實現,此函數是使用 c 語言實現的, 位 於
arch\arm\cpu\armv7\Exynos\ Irom_copy.c 中,具體源碼如下:
void movi_uboot_copy(void)
{
#ifdef CONFIG_RAM_TEST //可以 tiny4412.h 中定義宏開啓內存測試功能
uboot_mem_test(); //內存測試,複製前先確定內存是否已經可用
#endif
#ifdef CONFIG_CORTEXA5_ENABLE
SDMMC_ReadBlocks(MOVI_UBOOT_POS, MOVI_UBOOT_BLKCNT, 0x40000000);
#endif
// bl1 的讀函數複製 u-boot 到 ddr 中
SDMMC_ReadBlocks(MOVI_UBOOT_POS, MOVI_UBOOT_BLKCNT, CONFIG_PHY_UBOOT_BASE);
#ifdef CONFIG_SECURE_BOOT //檢測是否支持安全啓動, tiny4412 中沒有定義,不執行
if (Check_Signature((SB20_CONTEXT *)SECURE_CONTEXT_BASE,
(unsigned char*)CONFIG_PHY_UBOOT_BASE, PART_SIZE_UBOOT-256,
(unsigned char*)(CONFIG_PHY_UBOOT_BASE+PART_SIZE_UBOOT-256), 256) != 0)
{
while(1);
}
#endif
} 以
上代碼中,核心代碼是:
SDMMC_ReadBlocks(MOVI_UBOOT_POS, MOVI_UBOOT_BLKCNT, CONFIG_PHY_UBOOT_BASE);
SDMMC_ReadBlocks 是一個宏,定義如下:
#define SDMMC_ReadBlocks(uStartBlk, uNumOfBlks, uDstAddr) \
(((void(*)(u32, u32, u32*))(*((u32 *)EXTERNAL_FUNC_ADDRESS)))(uStartBlk, uNumOfBlks, uDstAddr))
(((void(*)(u32, u32, u32*))(*((u32 *)EXTERNAL_FUNC_ADDRESS)))(uStartBlk, uNumOfBlks, uDstAddr)) 是
一個函數指針, 函數所在地址是 EXTERNAL_FUNC_ADDRESS, 其定義如下:
#define ISRAM_ADDRESS 0x02020000
#define SECURE_CONTEXT_BASE 0x02023000
#define EXTERNAL_FUNC_ADDRESS (ISRAM_ADDRESS + 0x0030)
這個地址就是 0x02020030 ,位於內部 RAM 地址空間中。 這個指針中的值是由 iROM 程序進行設
置的,指向了 iROM 代碼中實現的真正 SD 卡讀函數代碼函數首地址。
所以 SDMMC_ReadBlocks 可以看成是一個 SD 卡讀函數, 有 3 個參數,分別是起始扇區號 uStartBlk,
要讀取的扇區數量 uNumOfBlks,讀取數據後存放到內存的哪個起始地址 uDstAddr。
調用傳遞的實際參數:
SDMMC_ReadBlocks(MOVI_UBOOT_POS, MOVI_UBOOT_BLKCNT, CONFIG_PHY_UBOOT_BASE);
關於參數定義:
MOVI_UBOOT_POS:
Movi_partition.h arch\arm\include\asm\Arch-exynos
#define eFUSE_SIZE (1 * 512) // 512 Byte eFuse, 512 Byte reserved
#define MOVI_BLKSIZE (1<<9) /* 512 bytes */
#define PART_SIZE_FWBL1 (8 * 1024)
#define MOVI_FWBL1_BLKCNT (PART_SIZE_FWBL1 / MOVI_BLKSIZE)
由此可以計算出 MOVI_FWBL1_BLKCNT 大小是 (8 * 1024)/521 --> 16 個 blk

//由此可以計算出 MOVI_FWBL1_BLKCNT 大小是(16 * 1024) / 512 -->32 個 blk
#define PART_SIZE_BL1 (16 * 1024)
#define MOVI_BL1_BLKCNT (PART_SIZE_BL1 / MOVI_BLKSIZE)
//由此可以計算出 MOVI_UBOOT_POS 值爲 512/512 +16 + 32 = 49 -->第 49 個 blk 。 這個值剛剛好是 SD 卡燒寫 u-boot.bin 的扇區位置。
#define MOVI_UBOOT_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_FWBL1_BLKCNT + MOVI_BL1_BLKCNT)
//由此可以計算出 MOVI_UBOOT_BLKCNT 值爲 (328 * 1024)/512 或(512 * 1024)/512
MOVI_UBOOT_BLKCNT:
#ifdef CONFIG_TRUSTZONE
#define PART_SIZE_UBOOT (328 * 1024)
#else
#define PART_SIZE_UBOOT (512 * 1024)
#endif
#define MOVI_UBOOT_BLKCNT (PART_SIZE_UBOOT / MOVI_BLKSIZE) /* 328KB */
CONFIG_PHY_UBOOT_BASE:
#define CONFIG_PHY_UBOOT_BASE CONFIG_SYS_SDRAM_BASE + 0x3e00000
//CONFIG_SYS_SDRAM_BASE 是外擴展的 DDR 的起始地址。

經過這一步,SD卡的u-boot.bin代碼被完整的複製到DDR中。接下來函數返回,程序執行:

b  after_copy


8.after_copy源碼具體分析

這個子函數進入主要工作內容是開啓 MMU,設置第二階段運行標誌,進入 C 語言階段,調用 Board.c arch\arm\Lib\Board.c 文件中 board_init_f,這個函數對板子外設進行初始化。

after_copy: //複製好後指示燈亮
#ifdef CONFIG_SMDKC220 // 有定義
/* set up C2C */
ldr r0, =S5PV310_SYSREG_BASE
ldr r2, =GENERAL_CTRL_C2C_OFFSET
ldr r1, [r0, r2]
ldr r3, =0x4000
orr r1, r1, r3
str r1, [r0, r2]
#endif
//幹,使能 mmu,
#ifdef CONFIG_ENABLE_MMU
bl enable_mmu
#endif
/* store second boot information in u-boot C level variable 設置第二階段運行標誌*/
ldr r0, =CONFIG_PHY_UBOOT_BASE //#define CONFIG_SYS_SDRAM_BASE 0x40000000
sub r0, r0, #8
ldr r1, [r0]
ldr r0, _second_boot_info
str r1, [r0]
/* Print 'K' */
ldr r0, =S5PV310_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
//開始進入 C 語言階段
ldr r0, _board_init_f //C 語言入口
mov pc, r0 //程序執行 board_init_f 函數
_board_init_f:
//Board.c arch\arm\Lib ,這個函數屬於架構級別的,一般不需要修改
.word board_init_f //C 語言入口
_second_boot_info:
// 在 tiny4412/tiny4412.c
// 定義的一個變量, unsigned int second_boot_info = 0xffffffff;
.word second_boot_info

 

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