第二章第八节 U-Boot-2013.04启动分析(4)
1) 调用board_init_f函数 bl board_init_f 实际上,board_init_f()函数是U-Boot执行的第一个C语言函数:void board_init_f(ulong bootflag),这个函数位于arch/arm/lib目录下的board.c文件中。 void board_init_f()函数的主要工作是:清空gd指向的结构体、逐步填充结构体,执行init_fnc_ptr函数指针数组中的各个初始化函数和划分内存区域等。结构体成员的初始化贯穿于board_init_f函数的整个过程,多数情况下成员的值是根据顶层配置文件的宏确定的。 /* Pointer is writable since we allocated a register for it */ gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); gd_t是一个结构体类型,其定义在arch/arm/include/asm目录下的global_data.h文件中,前面已经详细分析过。 memset((void *)gd, 0, sizeof(gd_t)); 将gd所指向的结构体内所有变量清零,长度为:sizeof(gd_t)。清空之后,在board_init_f()函中后面有很多代码是对gd所指向的结构体的成员进行重新复赋值。 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } 在U-Boot中定义了一个init_sequence函数指针数组: init_fnc_t *init_sequence[] = { arch_cpu_init, /* basic arch cpu dependent setup */ mark_bootstage, #ifdef CONFIG_OF_CONTROL fdtdec_check_fdt, #endif #if defined(CONFIG_BOARD_EARLY_INIT_F) board_early_init_f, #endif timer_init, /* initialize timer */ #ifdef CONFIG_BOARD_POSTCLK_INIT board_postclk_init, #endif #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 */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c, #endif dram_init, /* configure available RAM banks */ NULL, }; 函数的类型为init_fnc_t,init_fnc_t也是一个新定义的数据类型,这个数据类型是传入参数为空,返回值为有符号整形的函数,函数用于初始化工作。如下: typedef int (init_fnc_t) (void); board_init_f函数使用一个for循环语句来逐一执行数组中的初始化函数,如果初始化函数返回值不为0,程序调用hang函数挂起,不再继续往下运行。 addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为0x5000 0000~0x57FF FFFF。说明SDRAM一共128MB的空间。 接下来的代码程序就是对这128M内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为:0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为128MB。 其中在smdk6410.h中,有这么一个宏定义: #define CONFIG_SYS_SDRAM_BASE 0x50000000 这说明SDRAM的起始地址是:0x5000 0000。 完成gd结构体的初始化和内存的划分之后,执行board_init_f()函数的最后一行代码: relocate_code(addr_sp, id, addr); 这行代码的意思很明显是要跳回到start.S中,跳回start.S中紧接着的是下面这一段代码。 .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 */ 将relocate_code()说带回来的三个参数分别装入r4、r5、r6寄存器中。 但是注意到,relocate_code这个函数的声明实在commom.h中,如下: void relocate_code (ulong, gd_t *, ulong) __attribute__ ((noreturn)); relocate_code函数的三个参数分别栈顶地址、数据ID(即全局结构gd)在SDRAM中的起始地址和在SDRAM中存储U-Boot的起始地址。 在start.S中所接着进行的是设置堆栈指针,如下: /* Set up the stack */ stack_setup: mov sp, r4 adr r0, _start cmp r0, r6 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq clear_bss /* skip relocation */ mov r1, r6 /* r1 <- scratch for copy_loop */ ldr r3, _bss_start_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 r4是刚刚传回来的堆栈指针,那么将r4给sp,设定堆栈指针。 r6是在SDRAM中存储u-boot的起始地址,将r0和r6进行比较。如果此时的u-boot已经是在SDRAM中,则beq clear_bss;如果不是,在NandFlash中,则要将u-boot复制到SDRAM中。 clear_bss: #ifndef CONFIG_SPL_BUILD ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs mov r4, r6 /* reloc addr */ add r0, r0, r4 add r1, r1, r4 mov r2, #0x00000000 /* clear */ clbss_l:cmp r0, r1 /* clear loop... */ bhs clbss_e /* if reached end of bss, exit */ str r2, [r0] add r0, r0, #4 b clbss_l clbss_e: #ifndef CONFIG_NAND_SPL bl coloured_LED_init bl red_led_on #endif #endif 上面这段代码是对BSS进行清零操作。 /* * We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */ #ifdef CONFIG_NAND_SPL ldr pc, _nand_boot _nand_boot: .word nand_boot #else 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 #endif 上面这段代码如果是NAND 启动的话,那么就设置SP后跳到nand_boot()函数里面进行复制代码到SDRAM,然后跳到U-Boot在SDRAM 的起始地址开始运行。但是由于CONFIG_NAND_SPL没有宏定义,则是执行else。在进入board_init_r之前,给了两个参数:r5、r6。 r5:数据ID(即全局结构gd)在SDRAM中的起始地址。 r6:在SDRAM中存储U-Boot的起始地址。
|