上篇博客:http://www.cnblogs.com/yeqluofwupheng/p/7347925.html
講到uboot-spl的工作流程,接下來簡述一下uboot.bin的工作流程,這對應BL2的流程。
BL2的主要文件和任務流程如下:
arch/arm/cpu/armv7/start.S
1. 設置CPU為SVC模式
2. 關閉MMU
3. 關閉Cache
4. 跳轉到lowlevel_init.S low_level_init
board/samsung/origen/lowlevel_init.S
5. 初始化時鍾
6. 初始化內存
7. 初始化串口
8. 關閉看門狗
9. 跳轉到crt0.S _main
arch/arm/lib/crt0.S
10. 設置棧
11. 初始化C運行環境
12. 調用board_init_f()
arch/arm/lib/board.c
13. board_init_f對全局信息GD結構體進行填充
arch/arm/lib/crt0.S
14. 代碼重定位------------BL2的最后的工作, 執行完就進入DRAM執行BL2
1.首先從board_init_f函數開始,它是定義在/u-boot/arch/arm/lib/board.c文件中。
它的作用是初始化開發板。需要注意的是,此時程序是在flash中運行的。
1 void board_init_f(ulong bootflag) 2 { 3 bd_t *bd; 4 init_fnc_t **init_fnc_ptr; 5 gd_t *id; 6 ulong addr, addr_sp; 7 8 #if defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN9IW1P1) 9 memset((void*)0x00000000, 0, 4*1024); 10 #endif 11 /* Pointer is writable since we allocated a register for it */ 12 gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); 13 /* compiler optimization barrier needed for GCC >= 3.4 */ 14 __asm__ __volatile__("": : :"memory"); 15 16 memset((void *)gd, 0, sizeof(gd_t)); 17 gd->mon_len = _bss_end_ofs + sizeof(struct spare_boot_head_t); 18 gd->debug_mode = 1;
gd是一個保存在ARM的r8寄存器中的gd_t結構體的指針,該結構體包括了u-boot中所有重要的全局變量,它是在arch/arm/include/asm目錄下的global_data.h文件內被定義的。上述代碼的作用是為gd分配地址,並清零,最后得到整個u-boot的長度。gd_t結構體的定義如下:

typedef struct global_data { bd_t *bd; unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long fb_base; /* base address of frame buffer */ #ifdef CONFIG_FSL_ESDHC unsigned long sdhc_clk; #endif #ifdef CONFIG_AT91FAMILY /* "static data" needed by at91's clock.c */ unsigned long cpu_clk_rate_hz; unsigned long main_clk_rate_hz; unsigned long mck_rate_hz; unsigned long plla_rate_hz; unsigned long pllb_rate_hz; unsigned long at91_pllb_usb_init; #endif #ifdef CONFIG_ARM /* "static data" needed by most of timer.c on ARM platforms */ unsigned long timer_rate_hz; unsigned long tbl; unsigned long tbu; unsigned long long timer_reset_value; unsigned long lastinc; #endif #ifdef CONFIG_IXP425 unsigned long timestamp; #endif unsigned long relocaddr; /* Start address of U-Boot in RAM */ phys_size_t ram_size; /* RAM size */ unsigned long ram_size_mb; /* RAM size MB*/ unsigned long mon_len; /* monitor len */ unsigned long irq_sp; /* irq stack pointer */ unsigned long start_addr_sp; /* start_addr_stackpointer */ unsigned long reloc_off; #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) unsigned long tlb_addr; #endif #if defined(CONFIG_ALLWINNER) int uart_console; int boot_card_num; unsigned int layer_para; unsigned int layer_hd; int key_pressd_value; int axp_power_soft_id; int power_step_level; int pmu_suspend_chgcur; int pmu_runtime_chgcur; int limit_vol; int limit_cur; int limit_pcvol; int limit_pccur; int power_main_id; int power_slave_id; char *script_mod_buf; int script_main_key_count; int force_shell; uint malloc_noncache_start; int lockflag; uint chargemode; uint force_download_uboot; int securemode; uint vbus_status; //0: 未知;1:存在;2:不存在 uint debug_mode; #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ } gd_t;
19 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { 20 if ((*init_fnc_ptr)() != 0) { 21 hang (); 22 } 23 }
上述代碼的作用是循環調用init_sequence函數指針數組中的成員,該數組成員函數主要完成一些初始化的工作。
其中init_sequence的定義如下:
init_fnc_t *init_sequence[] = { //#if defined(CONFIG_ARCH_CPU_INIT) arch_cpu_init, /* basic arch cpu dependent setup */ //#endif sunxi_probe_securemode, #if defined(CONFIG_USE_NEON_SIMD) arm_neon_init, #endif #if defined(CONFIG_BOARD_EARLY_INIT_F) board_early_init_f, #endif timer_init, /* initialize timer */ #ifdef CONFIG_FSL_ESDHC get_clocks, #endif 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 */ display_inner, /* show the inner version */ print_commit_log, script_init, #if defined(SUNXI_OTA_TEST) display_ota_test, #endif get_debugmode_flag, #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif smc_init, init_func_pmubus, power_source_init, check_update_key, check_uart_input, dram_init, /* configure available RAM banks */ sunxi_set_secure_mode, NULL, };
board_early_init_f函數(在board/samsung/smdk2410目錄下的smdk2410.c文件內)完成ARM的時鍾頻率和IO的設置;
timer_init函數(在arch/arm/cpu/arm920t/s3c24x0目錄下的timer.c文件內)完成定時器4的設置;
env_init函數(在common目錄下的env_flash.c文件內,因為include/configs/smdk2410.h中定義了CONFIG_ENV_IS_IN_FLASH)完成環境變量的設置;
init_baudrate函數(在arch/arm/lib目錄下的board.c文件內)完成波特率的設置;
serial_init函數(在drivers/serial目錄下的serial_s3c24x0.c文件內,因為include/configs/smdk2410.h中定義了CONFIG_S3C24X0_SERIAL)完成串口通訊的設置;
console_init_f函數(在common目錄下的console.c文件內)完成第一階段的控制台初始化;
display_banner函數(在arch/arm/lib目錄下的board.c文件內)用來打印輸出一些信息;
dram_init函數(在board/samsung/smdk2410目錄下的smdk2410.c文件內)用來配置SDRAM的大小。
24 #if defined(CONFIG_SYS_MEM_TOP_HIDE) 25 /* 26 * Subtract specified amount of memory to hide so that it won't 27 * get "touched" at all by U-Boot. By fixing up gd->ram_size 28 * the Linux kernel should now get passed the now "corrected" 29 * memory size and won't touch it either. This should work 30 * for arch/ppc and arch/powerpc. Only Linux board ports in 31 * arch/powerpc with bootwrapper support, that recalculate the 32 * memory size from the SDRAM controller setup will have to 33 * get fixed. 34 */ 35 gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE; 36 #endif 37 if(gd->ram_size) 38 addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 39 else 40 addr = CONFIG_SYS_SDRAM_BASE + (1U<<30);
得到SDRAM的末位物理地址,即SDRAM的空間分布。
65 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) 66 /* reserve TLB table */ 67 addr -= (4096 * 4); 68 69 /* round down to next 64 kB limit */ 70 addr &= ~(0x10000 - 1); 71 72 gd->tlb_addr = addr; 73 debug("TLB table at: %08lx\n", addr); 74 #endif 75 76 /* round down to next 4 kB limit */ 77 addr &= ~(4096 - 1); 78 debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
分配SDRAM的高64kB區域作為TLB,並且該區域也被用於U-Boot。16KB保存TLB表,
88 /* 89 * reserve memory for U-Boot code, data & bss 90 * round down to next 4 kB limit 91 */ 92 addr -= gd->mon_len; 93 addr &= ~(4096 - 1); 94 95 debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
分配SDRAM的下一個單元為U-Boot代碼段、數據段及BSS段。
96 #ifndef CONFIG_SPL_BUILD 97 /* 98 * reserve memory for malloc() arena 99 */ 100 addr_sp = addr - TOTAL_MALLOC_LEN; 101 debug("Reserving %dk for malloc() at: %08lx\n", 102 TOTAL_MALLOC_LEN >> 10, addr_sp); 103 #ifdef CONFIG_NONCACHE_MEMORY 104 addr_sp &= (~(0x00100000 -1)); 105 addr_sp -= CONFIG_NONCACHE_MEMORY_SIZE; 107 #endif 108 /* 109 * (permanently) allocate a Board Info struct 110 * and a permanent copy of the "global" data 111 */ 112 addr_sp -= sizeof (bd_t); 113 bd = (bd_t *) addr_sp; 114 gd->bd = bd; 115 debug("Reserving %zu Bytes for Board Info at: %08lx\n", 116 sizeof (bd_t), addr_sp); 117 118 #ifdef CONFIG_MACH_TYPE 119 gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ 120 #endif 121 122 addr_sp -= sizeof (gd_t); 123 id = (gd_t *) addr_sp; 124 debug("Reserving %zu Bytes for Global Data at: %08lx\n", 125 sizeof (gd_t), addr_sp); 126 127 /* setup stackpointer for exeptions */ 128 gd->irq_sp = addr_sp; 129 #ifdef CONFIG_USE_IRQ 130 addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); 131 debug("Reserving %zu Bytes for IRQ stack at: %08lx\n", 132 CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); 133 #endif 134 /* leave 3 words for abort-stack */ 135 addr_sp -= 12; 136 137 /* 8-byte alignment for ABI compliance */ 138 addr_sp &= ~0x07; 139 #else 140 addr_sp += 128; /* leave 32 words for abort-stack */ 141 gd->irq_sp = addr_sp; 142 #endif
第100行的意思為在SDRAM中又開辟了一塊malloc空間,該區域是緊挨着上面定義的U-Boot區域的下面。然后在SDRAM中又分別依次定義了bd結構體空間、gd結構體空間和3個字大小的異常中斷堆空間。其中bd結構體的數據原型為bd_t數據結構,它表示的是“板級信息”結構體,這些信息包括開發板的波特率、IP地址、ID、以及DRAM等信息,它是在arch/arm/include/asm目錄下的u-boot.h文件中定義的。下圖詳細描述了SDRAM的空間分配情況(地址從上到下遞減):
64KB的TLB |
4KB的RAM空間 |
4KB的U-Boot代碼段、數據段及BSS段 |
malloc空間 |
bd空間 |
gd空間 |
3字異常中斷堆空間 |
棧空間 |
143 gd->bd->bi_baudrate = gd->baudrate; 144 /* Ram ist board specific, so move it to board code ... */ 145 dram_init_banksize(); 146 display_dram_config(); /* and display it */ 147 148 gd->relocaddr = addr + sizeof(struct spare_boot_head_t) + sizeof(uboot_hash_value); 149 gd->start_addr_sp = addr_sp; 150 gd->reloc_off = addr - _TEXT_BASE;
上述代碼主要的作用是為gd結構體賦值,其中display_dram_config函數的作用是計算SDRAM的大小,並把它通過串口顯示在控制台上。
151 memcpy(id, (void *)gd, sizeof(gd_t)); 152 153 relocate_code(addr_sp, id, addr + sizeof(struct spare_boot_head_t)+sizeof(uboot_hash_value)); 154 155 /* NOTREACHED - relocate_code() does not return */ 156 }
在board_init_f函數的最后是跳轉到relocate_code函數體內,這個函數是在arch/arm/cpu/arm920t目錄下的start.s文件內,也就是說從最開始的start.s跳到board.c,又從board.c跳回到了start.s中,這是因為此時程序需要重定向,即把代碼從flash中搬運到ram中,這個過程是需要匯編這個低級語言來完成的。傳遞給relocate_code函數的三個參數分別棧頂地址、數據ID(即全局結構gd)在SDRAM中的起始地址和在SDRAM中存儲U-Boot的起始地址。需要注意的是relocate_code函數執行完后,並不會返回到relocate_code (addr_sp, id, addr);的下一條語句繼續執行。而是繼續運行start.S的程序。
下面繼續看start.S的程序:
/* * void relocate_code (addr_sp, gd, addr_moni) * * This "function" does not return, instead it continues in RAM * after relocating the monitor code. * */ .globl relocate_code relocate_code: mov r4, r0 /* save addr_sp */ mov r5, r1 /* save addr of gd */ mov r6, r2 /* save addr of destination */
取得三個參數,分別放入寄存器r4、r5和r6。
/* Set up the stack */ stack_setup: mov sp, r4
設置堆棧地址。
/* Set up irq stack */ add r4, r4, #12 add r4, r4, #0x2000 mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x12 msr cpsr_c, r0 mov sp, r4
設置IRQ 棧。
/* Set up svc stack */ sub r4, r4, #0x2000 sub r4, r4, #12 mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x13 msr cpsr_c, r0
設置SVN 棧。
adr r0, _start cmp r0, r6 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq clear_bss /* skip relocation */ @ mov r9, #0 @ b clear_bss mov r1, r6 /* r1 <- scratch for copy_loop */ ldr r3, _image_copy_end_ofs add r2, r0, r3 /* r2 <- source end address */ copy_loop: ldmia r0!, {r9-r10} /* copy from source address [r0] */ stmia r1!, {r9-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end address [r2] */ blo copy_loop
判斷U-Boot是在什么位置上,如果在SDRAM中,則直接跳到BSS段清零函數處即可;如果在FLASH中,則要把U-Boot復制到SDRAM中指定的位置處。

#ifndef CONFIG_SPL_BUILD /* * fix .rel.dyn relocations */ @ldr r0, _TEXT_BASE /* r0 <- Text base */ adr r0, _start sub r9, r6, r0 /* r9 <- relocation offset */ ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ add r10, r10, r0 /* r10 <- sym table in FLASH */ ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ fixloop: ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ add r0, r0, r9 /* r0 <- location to fix up in RAM */ ldr r1, [r2, #4] and r7, r1, #0xff cmp r7, #23 /* relative fixup? */ beq fixrel cmp r7, #2 /* absolute fixup? */ beq fixabs /* ignore unknown type of fixup */ b fixnext fixabs: /* absolute fix: set location to (offset) symbol value */ mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ add r1, r10, r1 /* r1 <- address of symbol in table */ ldr r1, [r1, #4] /* r1 <- symbol value */ add r1, r1, r9 /* r1 <- relocated sym addr */ b fixnext fixrel: /* relative fix: increase location by offset */ ldr r1, [r0] add r1, r1, r9 fixnext: str r1, [r0] add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ cmp r2, r3 blo fixloop b clear_bss _rel_dyn_start_ofs: .word __rel_dyn_start - _start _rel_dyn_end_ofs: .word __rel_dyn_end - _start _dynsym_start_ofs: .word __dynsym_start - _start #endif /* #ifndef CONFIG_SPL_BUILD */
上述代碼的含義是對rel.dyn進行重定向。
... ldr r0, _board_init_r_ofs adr r1, _start add lr, r0, r1 add lr, lr, r9 /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ mov r1, r6 /* dest_addr */ /* jump to it ... */ mov pc, lr ... _board_init_r_ofs: .word board_init_r - _start
該段代碼的作用是跳轉到board_init_r函數,並且給該函數傳遞了兩個參數:全局結構gd在SDRAM中的起始地址和在SDRAM中存儲U-Boot的起始地址。board_init_r函數是在arch/arm/lib目錄下的board.c文件中,也就是又回到了上面執行過的board_init_f函數所在的board.c文件中。以后,程序就開始在SDRAM中運行了。
以上是uboot.bin的流程。