上篇博客: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的流程。