u-boot分析(二)
由於這兩天家里有點事,所以耽誤了點時間,沒有按時更新,今天我首先要跟大家說說我對於u-boot分析的整體的思路,然后呢我以后的博客會按照這個內容更新,希望大家關注。
言歸正傳,我首先說一說我以后的思路,對於u-boot呢,我會結合2440、6410、210這三款主流的學習芯片進行分析,首先會結合u-boot的源碼以及我以前的arm啟動流程一文http://www.cnblogs.com/wrjvszq/p/4204703.html 總結出u-boot的工作流程,然后以后的博文會結合u-boot源碼、芯片手冊等內容去分析,u-boot為什么去這么做。
Ok我們下面進入我們今天的內容,今天我會以210為例來分析其u-boot的工作流程,因為2440,6410的啟動流程大家跟着u-boot源碼中的注釋就能解決。
我們根據上篇博文(http://www.cnblogs.com/wrjvszq/p/4206975.html)中提到的方法,可以得到其的入口為arch\arm\cpu\armv7\start.S中的_start我們可以輕松的找到其對應代碼,下面我們對其工作流程進行分析:
1. 設置中斷向量表。
1 .globl _start 2 3 _start: b reset 4 5 ldr pc, _undefined_instruction 6 7 ldr pc, _software_interrupt 8 9 ldr pc, _prefetch_abort 10 11 ldr pc, _data_abort 12 13 ldr pc, _not_used 14 15 ldr pc, _irq 16 17 ldr pc, _fiq
2. 設置處理器到svc的模式(因為我們的上電將會觸發reset)
1 reset: 2 bl save_boot_params//空函數 3 /* 4 * set the cpu to SVC32 mode 5 */ 6 mrs r0, cpsr 7 bic r0, r0, #0x1f 8 orr r0, r0, #0xd3 9 msr cpsr,r0
繼續跟隨代碼往下走我們會找到
1 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 2 3 bl cpu_init_crit//跳轉至cpu_init_crit 4 5 #endif 6 7 cpu_init_crit:
3. 讓L1的I/D caches失效
1 cpu_init_crit: 2 /* 3 * Invalidate L1 I/D 4 */ 5 mov r0, #0 @ set up for MCR 6 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs 7 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache 8 mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array 9 mcr p15, 0, r0, c7, c10, 4 @ DSB 10 mcr p15, 0, r0, c7, c5, 4 @ ISB
4. 關閉MMU和caches
1 /* 2 * disable MMU stuff and caches 3 */ 4 mrc p15, 0, r0, c1, c0, 0 5 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) 6 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) 7 orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align 8 orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB 9 #ifdef CONFIG_SYS_ICACHE_OFF 10 bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache 11 #else 12 orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache 13 #endif 14 mcr p15, 0, r0, c1, c0, 0
下面代碼我們將跳轉至lowlevel_init(我們在此以三星的smart210為例)我們可以在\board\samsung\smart210\lowlevel_init.S中找到
1 /* 2 * Jump to board specific initialization... 3 * The Mask ROM will have already initialized 4 * basic memory. Go here to bump up clock rate and handle 5 * wake up conditions. 6 */ 7 mov ip, lr @ persevere link reg across call 8 bl lowlevel_init @ go setup pll,mux,memory 9 mov lr, ip @ restore link 10 mov pc, lr @ back to my caller 11 #endif 12 13 #ifndef CONFIG_SPL_BUILD
5. 檢查reset狀態(reset分為兩種1、掉電的reset 2、從睡眠喚醒的reset)所以要對其進行區分,以便跳過部分代碼。
1 push {lr}//保存lr的值以便待會返回 2 3 /* check reset status */ 4 5 ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) 6 ldr r1, [r0] 7 bic r1, r1, #0xfff6ffff 8 cmp r1, #0x10000 9 beq wakeup_reset_pre 10 cmp r1, #0x80000 11 beq wakeup_reset_from_didle
6. 恢復IO引腳為默認值
1 /* IO Retention release */ 2 ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET) 3 ldr r1, [r0] 4 ldr r2, =IO_RET_REL 5 orr r1, r1, r2 6 str r1, [r0]
7. 關閉看門狗
1 /* Disable Watchdog */ 2 ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */ 3 mov r1, #0 4 str r1, [r0]
8. SRAM、SROM初始化
1 /* SRAM(2MB) init for SMDKC110 */ 2 /* GPJ1 SROM_ADDR_16to21 */ 3 ldr r0, =ELFIN_GPIO_BASE 4 5 ldr r1, [r0, #GPJ1CON_OFFSET] 6 bic r1, r1, #0xFFFFFF 7 ldr r2, =0x444444 8 orr r1, r1, r2 9 str r1, [r0, #GPJ1CON_OFFSET] 10 11 ldr r1, [r0, #GPJ1PUD_OFFSET] 12 ldr r2, =0x3ff 13 bic r1, r1, r2 14 str r1, [r0, #GPJ1PUD_OFFSET] 15 16 /* GPJ4 SROM_ADDR_16to21 */ 17 ldr r1, [r0, #GPJ4CON_OFFSET] 18 bic r1, r1, #(0xf<<16) 19 ldr r2, =(0x4<<16) 20 orr r1, r1, r2 21 str r1, [r0, #GPJ4CON_OFFSET] 22 23 ldr r1, [r0, #GPJ4PUD_OFFSET] 24 ldr r2, =(0x3<<8) 25 bic r1, r1, r2 26 str r1, [r0, #GPJ4PUD_OFFSET] 27 28 29 /* CS0 - 16bit sram, enable nBE, Byte base address */ 30 ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */ 31 mov r1, #0x1 32 str r1, [r0] 33 34 /* PS_HOLD pin(GPH0_0) set to high */ 35 ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET) 36 ldr r1, [r0] 37 orr r1, r1, #0x300 38 orr r1, r1, #0x1 39 str r1, [r0]
以下代碼是在判斷是不是在內存中運行,如果是在內存中運行則跳過部分代碼,我們這是第一次運行BL,並且從nand啟動的所以代碼不在內存中不用關心。
1 /* when we already run in ram, we don't need to relocate U-Boot. 2 * and actually, memory controller must be configured before U-Boot 3 * is running in ram. 4 */ 5 ldr r0, =0x00ffffff 6 bic r1, pc, r0 /* r0 <- current base addr of code */ 7 ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ 8 bic r2, r2, r0 /* r0 <- current base addr of code */ 9 cmp r1, r2 /* compare r0, r1 */ 10 beq 1f /* r0 == r1 then skip sdram init */
9. 初始化時鍾
1 /* init system clock */ 2 bl system_clock_init
10. 初始化內存
1 /* Memory initialize */ 2 bl mem_ctrl_asm_init
11. 串口簡單初始化
1 1: 2 /* for UART */ 3 bl uart_asm_init
12. 取消存儲保護區
1 bl tzpc_init 2 //我們沒有用onenand所以跳過 3 4 #if defined(CONFIG_ONENAND) 5 bl onenandcon_init 6 #endif
13. 簡單初始化nand
1 #if defined(CONFIG_NAND) 2 /* simple init for NAND */ 3 bl nand_asm_init 4 #endif
14. 關閉ABB
1 /* ABB disable */ 2 ldr r0, =0xE010C300 3 orr r1, r1, #(0x1<<23) 4 str r1, [r0] 5 6 /* Print 'K' */ 7 ldr r0, =ELFIN_UART_CONSOLE_BASE 8 ldr r1, =0x4b4b4b4b 9 str r1, [r0, #UTXH_OFFSET] 10 11 pop {pc}//函數返回到調用者
代碼將會返回到這里
1 /* 2 * Jump to board specific initialization... 3 * The Mask ROM will have already initialized 4 * basic memory. Go here to bump up clock rate and handle 5 * wake up conditions. 6 */ 7 mov ip, lr @ persevere link reg across call 8 bl lowlevel_init @ go setup pll,mux,memory 9 mov lr, ip @ restore link 10 mov pc, lr @ back to my caller
代碼繼續返回到
1 /* the mask ROM code should have PLL and others stable */ 2 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 3 bl cpu_init_crit 4 #endif
15. 設置堆棧
1 /* Set stackpointer in internal RAM to call board_init_f */ 2 call_board_init_f: 3 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) 4 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ 5 ldr r0,=0x00000000
下面代碼在判斷我們現在是不是在內存中運行,如果在內存將跳轉至board_init_in_ram
1 #if defined(CONFIG_TINY210) || defined(CONFIG_SMART210) 2 adr r4, _start 3 ldr r5,_TEXT_BASE 4 cmp r5,r4 5 beq board_init_in_ram
下面代碼在判斷從什么地方啟動的我們是從nand啟動的所以跳轉至nand_boot_210
1 ldr r0, =PRO_ID_BASE 2 ldr r1, [r0,#OMR_OFFSET] 3 bic r2, r1, #0xffffffc1 4 5 /* NAND BOOT */ 6 cmp r2, #0x0 @ 512B 4-cycle 7 moveq r3, #BOOT_NAND 8 9 cmp r2, #0x2 @ 2KB 5-cycle 10 moveq r3, #BOOT_NAND 11 12 cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC 13 moveq r3, #BOOT_NAND 14 15 cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC 16 moveq r3, #BOOT_NAND 17 18 cmp r2, #0x8 @ OneNAND Mux 19 moveq r3, #BOOT_ONENAND 20 21 /* SD/MMC BOOT */ 22 cmp r2, #0xc 23 moveq r3, #BOOT_MMCSD 24 25 /* NOR BOOT */ 26 cmp r2, #0x14 27 moveq r3, #BOOT_NOR 28 29 /* Uart BOOTONG failed */ 30 cmp r2, #(0x1<<4) 31 moveq r3, #BOOT_SEC_DEV 32 33 ldr r0, =INF_REG_BASE 34 str r3, [r0, #INF_REG3_OFFSET] 35 36 ldr r1, [r0, #INF_REG3_OFFSET] 37 cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */ 38 beq nand_boot_210 39 cmp r1, #BOOT_MMCSD 40 beq mmcsd_boot_210 41
下面代碼將會跳轉至board_init_f_nand
1 nand_boot_210: 2 bl board_init_f_nand 3 4 mmcsd_boot_210: 5 bl board_init_f 6 board_init_in_ram: 7 #endif 8 bl board_init_f
我們繼續可以看看board_init_f_nand
1 void board_init_f_nand(unsigned long bootflag) 2 { 3 __attribute__((noreturn)) void (*uboot)(void); 4 copy_uboot_to_ram_nand();//復制nand中的bl2到內存 5 6 /* Jump to U-Boot image */ 7 uboot = (void *)CONFIG_SYS_TEXT_BASE;//跳轉至內存執行uboot 8 (*uboot)(); 9 /* Never returns Here */ 10 }
16. 復制nand中的bl2到內存
l 我們可以到copy_uboot_to_ram_nand函數中看到其將我們的BL2復制到CONFIG_SYS_TEXT_BASE地址處,用SI(Source Insight)我們可以看到這個地址為0x23E00000,我在arm啟動流程一文中說到過210的地址布局可以知道其內存是從0x20000000開始的,所以上面這個地址在內存中
1 int copy_uboot_to_ram_nand (void) 2 { 3 int large_block = 0; 4 int i; 5 vu_char id; 6 7 NAND_CONTROL_ENABLE(); 8 NAND_ENABLE_CE(); 9 NFCMD_REG = NAND_CMD_READID; 10 NFADDR_REG = 0x00; 11 12 /* wait for a while */ 13 for (i=0; i<200; i++); 14 id = NFDATA8_REG; 15 id = NFDATA8_REG; 16 17 if (id > 0x80) 18 large_block = 1; 19 20 /* read NAND Block. 21 * 128KB ->240KB because of U-Boot size increase. by scsuh 22 * So, read 0x3c000 bytes not 0x20000(128KB). 23 */ 24 return nandll_read_blocks(CONFIG_SYS_TEXT_BASE, COPY_BL2_SIZE, large_block); 25 }
17. 跳轉至內存執行uboot
l 通運行代碼段的方式完成向BL2的跳轉
1 /* Jump to U-Boot image */ 2 uboot = (void *)CONFIG_SYS_TEXT_BASE;//跳轉至內存執行uboot 3 (*uboot)();
到此我們第一階段的代碼分析完成,接下來我們進行第二階段的分析,由於我們的210第二階段的代碼入口和第一階段相同,所以第一階段執行的代碼又會被執行一次,直到運行到下面代碼的時候才會發生變化
1 #if defined(CONFIG_TINY210) || defined(CONFIG_SMART210) 2 adr r4, _start 3 ldr r5,_TEXT_BASE 4 cmp r5,r4 5 beq board_init_in_ram
我們代碼將會跳到board_init_in_ram運行
1 board_init_in_ram: 2 #endif 3 bl board_init_f
我們繼續跳進board_init_f這個函數在arch\arm\lib\board.c中在這個函數中比較重要的一段代碼,下面的代碼是在遍歷一個函數指針數組,並且一一執行
1 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { 2 if ((*init_fnc_ptr)() != 0) { 3 hang (); 4 } 5 }
通過查找我們可以發現這個數組中的內容,是一大堆東西的初始化函數,也就是說上面的代碼進行了一系列的初始化
1 init_fnc_t *init_sequence[] = { 2 #if defined(CONFIG_ARCH_CPU_INIT) 3 arch_cpu_init, /* basic arch cpu dependent setup */ 4 #endif 5 #if defined(CONFIG_BOARD_EARLY_INIT_F) 6 board_early_init_f, 7 #endif 8 timer_init, /* initialize timer */ 9 #ifdef CONFIG_FSL_ESDHC 10 get_clocks, 11 #endif 12 env_init, /* initialize environment */ 13 init_baudrate, /* initialze baudrate settings */ 14 serial_init, /* serial communications setup */ 15 console_init_f, /* stage 1 init of console */ 16 display_banner, /* say that we are here */ 17 #if defined(CONFIG_DISPLAY_CPUINFO) 18 print_cpuinfo, /* display cpu info (and speed) */ 19 #endif 20 #if defined(CONFIG_DISPLAY_BOARDINFO) 21 checkboard, /* display board info */ 22 #endif 23 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) 24 init_func_i2c, 25 #endif 26 dram_init, /* configure available RAM banks */ 27 NULL, 28 };
經過上面的代碼后其會返回到start.s中繼續運行,中間又經過一系列東西,好多和第一階段重復,所以這里就不一一分析了,最終通過下面代碼進入board_init_r函數
1 ldr r0, _board_init_r_ofs 2 adr r1, _start 3 add lr, r0, r1 4 add lr, lr, r9 5 /* setup parameters for board_init_r */ 6 mov r0, r5 /* gd_t */ 7 mov r1, r6 /* dest_addr */ 8 /* jump to it ... */ 9 mov pc, lr 10 11 _board_init_r_ofs: 12 .word board_init_r - _start
在這個函數中又進行了硬件和軟件的初始化最后會落腳在
1 /* main_loop() can return to retry autoboot, if so just run it again. */ 2 for (;;) { 3 main_loop(); 4 }
通過上面的代碼對用戶命令進行解析。至此210的u-boot工作流程分析完畢,2440和6410比較簡單,基本按照注釋就可以總結出來。
下面我將自己總結的流程跟大家分享一下
l 2440
第一階段
1. 設置中斷向量表
2. 設置處理器工作模式為svc
3. 刷新I/Dcaches
4. 關閉MMU和caches
5. 關閉看門狗
6. 關所有中斷
7. 設置系統時鍾
8. 初始化串口
9. 初始化nand
10. 初始化內存
11. 復制BL到內存
12. 設置堆棧
13. 清楚BSS段
第二階段
1. 初始化串口
2. LCD初始化
3. 網卡初始化
4. Led初始化
5. 執行用戶命令
l 6410
第一階段
1. 設置中斷向量表
2. 設置處理器工作模式為svc
3. 刷新I/Dcaches
4. 關閉MMU和caches
5. 外設基地址初始化
6. 點亮led
7. 關看門狗
8. 初始化時鍾
9. 初始化串口
10. 初始化nand
11. 初始化內存
12. 復制BL到內存
13. 設置堆棧
14. 清楚BSS段
第二階段
1. 初始化串口
2. LCD初始化
3. 網卡初始化
4. Led初始化
5. 執行用戶命令
l 210
第一階段
1. 設置中斷向量表
2. 設置處理器工作模式為svc
3. 讓L1的I/D caches失效
4. 關閉MMU和caches
5. 檢查reset狀態
6. 回復io引腳為默認值
7. 關看門狗
8. Sram、srom初始化
9. 初始化時鍾
10. 初始化內存
11. 初始化串口
12. 取消存儲保護區
13. 初始化nand
14. 關閉ABB
15. 設置堆棧
16. 復制BL2到內存
17. 跳轉到內存執行
第二階段
1. 初始化串口
2. LCD初始化
3. 網卡初始化
4. Led初始化
5. 執行用戶命令