u-boot中SPL源代碼分析

[Author: Bo Shen <[email protected]>

[u-boot: 2014.01-rc1]

本文將使用sama5d3xek SPL實現做爲例子,具體代碼可查看:https://github.com/voiceshen/u-boot/tree/sama5d3xek_spl_spi_nand


u-boot SPL (second program loader), 對許多人來說也說很陌生。下面對此進行一個簡單介紹。

1. ARM SoC的啓動過程:

RomBoot --> SPL --> u-boot --> Linux kernel --> file system --> start application

(RomBoot是固化在SoC內部的。)

u-boot實現了一個新功能,能在編譯u-boot的同時生成SPL二進制文件。

2. SPL運行代碼go through

從u-boot-spl.lds鏈接文件可知,啓動代碼也是start.S。

(reset) <arch/arm/cpu/armv7/start.S> (b lowlevel_init: arch/arm/cpu/armv7/lowlevel_init.S)  (b _main) --> <arch/arm/lib/crt0.S> (bl board_init_f) --> <arch/arm/lib/spl.c> (board_init_r) --> <common/spl/spl.c> (jump_to_image_no_args去啓動u-boot) 到此SPL的生命週期結束。

簡單來講:SPL所做工作,一些硬件的初始化,然後讀取u-boot,最後調轉至u-boot。

3. 下面具體分析SPL的相關代碼。

<arch/arm/cpu/armv7/start.S>

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 110 reset:  
  2. 111         bl      save_boot_params  
  3. 112         /* 
  4. 113          * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, 
  5. 114          * except if in HYP mode already 
  6. 115          */  
  7. 116         mrs     r0, cpsr  
  8. 117         and     r1, r0, #0x1f           @ mask mode bits  
  9. 118         teq     r1, #0x1a               @ test for HYP mode  
  10. 119         bicne   r0, r0, #0x1f           @ clear all mode bits  
  11. 120         orrne   r0, r0, #0x13           @ set SVC mode  
  12. 121         orr     r0, r0, #0xc0           @ disable FIQ and IRQ  
  13. 122         msr     cpsr,r0  
  14. 123  
  15. 124 /* 
  16. 125  * Setup vector: 
  17. 126  * (OMAP4 spl TEXT_BASE is not 32 byte aligned. 
  18. 127  * Continue to use ROM code vector only in OMAP4 spl) 
  19. 128  */  
  20. 129 #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))  
  21. 130         /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */  
  22. 131         mrc     p15, 0, r0, c1, c0, 0   @ Read CP15 SCTRL Register  
  23. 132         bic     r0, #CR_V               @ V = 0  
  24. 133         mcr     p15, 0, r0, c1, c0, 0   @ Write CP15 SCTRL Register  
  25. 134  
  26. 135         /* Set vector address in CP15 VBAR register */  
  27. 136         ldr     r0, =_start  
  28. 137         mcr     p15, 0, r0, c12, c0, 0  @Set VBAR  
  29. 138 #endif  
  30. 139  
  31. 140         /* the mask ROM code should have PLL and others stable */  
  32. 141 #ifndef CONFIG_SKIP_LOWLEVEL_INIT  
  33. 142         bl      cpu_init_cp15  
  34. 143         bl      cpu_init_crit  
  35. 144 #endif  
  36. 145  
  37. 146         bl      _main  
111:如果沒有重新定義save_boot_params,則使用<arch/arm/cpu/armv7/start.S>中的save_boot_params。其不做任何事情,直接返回。

116~138: 看註釋即可明白。

141: 因爲SPL主要是對SoC進行初始化,所以不會定義CONFIG_SKIP_LOWLEVE_INIT, 即142,143行得以執行。

142: cpu_init_cpu15, 主要作用invalidate L1 I/D cache, disable MMU. 檢查是否需要workaround。

143: cpu_init_crit直接跳轉到lowlevel_init

下面看看lowlevel_init的實現:
<arch/arm/cpu/armv7/lowlevel_init.S>

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 18 ENTRY(lowlevel_init)  
  2.  19         /* 
  3.  20          * Setup a temporary stack 
  4.  21          */       
  5.  22         ldr     sp, =CONFIG_SYS_INIT_SP_ADDR  
  6.  23         bic     sp, sp, #7 /* 8-byte alignment for ABI compliance */  
  7.  24 #ifdef CONFIG_SPL_BUILD                   
  8.  25         ldr     r9, =gdata  
  9.  26 #else  
  10.  27         sub     sp, #GD_SIZE  
  11.  28         bic     sp, sp, #7  
  12.  29         mov     r9, sp  
  13.  30 #endif  
  14.  31         /* 
  15.  32          * Save the old lr(passed in ip) and the current lr to stack 
  16.  33          */  
  17.  34         push    {ip, lr}  
  18.  35   
  19.  36         /* 
  20.  37          * go setup pll, mux, memory 
  21.  38          */  
  22.  39         bl      s_init  
  23.  40         pop     {ip, pc}  
  24.  41 ENDPROC(lowlevel_init)  
22: 對stack pointer賦值成CONFIG_SYS_INIT_SP_ADDR

23: 確保sp是8字節對齊。

25:將gdata的地址存入到r9寄存器中。

39:跳轉到s_init。對Atmel sama5d3xek board, s_init定義在:<arch/arm/cpu/at91-common/spl.c> 此處暫時不分析。

然後返回到start.S處,接下來調用:bl _main到<arch/arm/lib/crt0.S>

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 58 ENTRY(_main)  
  2. 59   
  3. 60 /* 
  4. 61  * Set up initial C runtime environment and call board_init_f(0). 
  5. 62  */  
  6. 63   
  7. 64 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)  
  8. 65         ldr     sp, =(CONFIG_SPL_STACK)  
  9. 66 #else  
  10. 67         ldr     sp, =(CONFIG_SYS_INIT_SP_ADDR)  
  11. 68 #endif  
  12. 69         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */  
  13. 70         sub     sp, #GD_SIZE    /* allocate one GD above SP */  
  14. 71         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */  
  15. 72         mov     r9, sp          /* GD is above SP */  
  16. 73         mov     r0, #0  
  17. 74         bl      board_init_f  
65: 重新對SP賦值

69: 確認sp是8字對齊

70:相當於保留一個global_data的大小。

71: 確認更新後的sp是8字對齊

72:r9指向global_data

73:r0賦值0

74:跳轉到board_init_f中運行。

board_init_f在<arch/arm/lib/spl.c>定義:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 20 /* 
  2. 21  * In the context of SPL, board_init_f must ensure that any clocks/etc for 
  3. 22  * DDR are enabled, ensure that the stack pointer is valid, clear the BSS 
  4. 23  * and call board_init_f.  We provide this version by default but mark it 
  5. 24  * as __weak to allow for platforms to do this in their own way if needed. 
  6. 25  */  
  7. 26 void __weak board_init_f(ulong dummy)  
  8. 27 {  
  9. 28         /* Clear the BSS. */  
  10. 29         memset(__bss_start, 0, __bss_end - __bss_start);  
  11. 30   
  12. 31         /* Set global data pointer. */  
  13. 32         gd = &gdata;  
  14. 33   
  15. 34         board_init_r(NULL, 0);  
  16. 35 }  
26: board_init_f是一個弱函數,是可以被重新定義的。

29:對BSS段進行清零操作。

34: 跳轉到board_init_r

board_init_r在<common/spl/spl.c>中定義:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 132 void board_init_r(gd_t *dummy1, ulong dummy2)  
  2. 133 {  
  3. 134         u32 boot_device;  
  4. 135         debug(">>spl:board_init_r()\n");  
  5. 136   
  6. 137 #ifdef CONFIG_SYS_SPL_MALLOC_START  
  7. 138         mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,  
  8. 139                         CONFIG_SYS_SPL_MALLOC_SIZE);  
  9. 140 #endif  
  10. 141   
  11. 142 #ifndef CONFIG_PPC  
  12. 143         /* 
  13. 144          * timer_init() does not exist on PPC systems. The timer is initialized 
  14. 145          * and enabled (decrementer) in interrupt_init() here. 
  15. 146          */  
  16. 147         timer_init();  
  17. 148 #endif  
  18. 149   
  19. 150 #ifdef CONFIG_SPL_BOARD_INIT  
  20. 151         spl_board_init();  
  21. 152 #endif  

135: 輸出debug信息:>>spl:board_init_r();

137~140: 如果定義了:CONFIG_SYS_SPL_MALLOC_START, 則進行memory的malloc池初始化。以後調用malloc就在這個池子裏面分配內存。

142~148: 如果沒有定義:CONFIG_PPC, 則進行timer的初始化:timer_init() <arm/arm/cpu/armv7/at91/time.c>

150~150: CONFIG_SPL_BOARD_INIT, 則調用spl_board_init(). 這是board相關的定義,<board/atmel/sama5d3xek/sama5d3xek.c>

一切就緒後,就要檢查從什麼設備來啓動了。這裏就貼出RAM,MMC, NAND相關代碼

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 154         boot_device = spl_boot_device();  
  2. 155         debug("boot device - %d\n", boot_device);  
  3. 156         switch (boot_device) {  
  4. 157 #ifdef CONFIG_SPL_RAM_DEVICE  
  5. 158         case BOOT_DEVICE_RAM:  
  6. 159                 spl_ram_load_image();  
  7. 160                 break;  
  8. 161 #endif  
  9. 162 #ifdef CONFIG_SPL_MMC_SUPPORT  
  10. 163         case BOOT_DEVICE_MMC1:  
  11. 164         case BOOT_DEVICE_MMC2:  
  12. 165         case BOOT_DEVICE_MMC2_2:  
  13. 166                 spl_mmc_load_image();  
  14. 167                 break;  
  15. 168 #endif  
  16. 169 #ifdef CONFIG_SPL_NAND_SUPPORT  
  17. 170         case BOOT_DEVICE_NAND:  
  18. 171                 spl_nand_load_image();  
  19. 172                 break;  
  20. 173 #endif  
154: 獲取spl_boot_device,即從什麼設備啓動。

157~161:如果定義了CONFIG_SPL_RAM_DEVICE, 則執行spl_ram_load_image(),其就是將image下載到ram中。

162~168:如果定義了CONFIG_SPL_MMC_SUPPORT, 則執行spl_mmc_load_image(),其就是將image從mmc/sd裏面讀取到ram中。

169~173:如果定義了CONFIG_SPL_NAND_SUPPORT, 則執行spl_nand_load_image(), 其就是將image從nand flash中讀取到ram中。

當要啓動的image位於RAM中後,我們就可以啓動之。

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 213         switch (spl_image.os) {  
  2. 214         case IH_OS_U_BOOT:  
  3. 215                 debug("Jumping to U-Boot\n");  
  4. 216                 break;  
  5. 217 #ifdef CONFIG_SPL_OS_BOOT  
  6. 218         case IH_OS_LINUX:  
  7. 219                 debug("Jumping to Linux\n");  
  8. 220                 spl_board_prepare_for_linux();  
  9. 221                 jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);  
  10. 222 #endif  
  11. 223         default:  
  12. 224                 debug("Unsupported OS image.. Jumping nevertheless..\n");  
  13. 225         }  
  14. 226         jump_to_image_no_args(&spl_image);  
  15. 227 }  
213: 判斷image的類型。

214:如果是u-boot,則直接到227去運行u-boot。

218:如果是Linux,則到221去啓動Linux.

至此,SPL結束它的生命,控制權交於u-boot或Linux。

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