s5pv210-Uboot启动流程

来源:互联网 发布:mac php集成开发环境 编辑:程序博客网 时间:2024/05/22 04:03

一、启动流程

1.s5pv210的启动过程

根据三星公司的《S5PV210_UM_REV1.1》手册可知,S5PV210 启动过程主要可 分为 3 个阶段

S5PV210 上电复位后将从 IROM 处执行已固化的启动代码 -------BL0
在 BL0 里初始化过程中对启动设备进行判断,并从启动设备拷贝 BL1(最大16KB ) 到 IRAM 处 , 即 刚 才 所 说 的 0xD0020000 开 始 的 地 址 , 其 中0xD0020000~0xD0020010 的 16 字节为 BL1 的校验信息和 BL1 尺寸,并对 BL1进行校验,校验通过转入 BL1 进行执行,BL1 继续初始化,并拷贝 BL2(最大80KB)到 IRAM 中并对其校验,通过后转入 BL2。
BL2 完成一些比较复杂的初始化,包括 DRAM 的初始化,完成后将 OS 代码拷贝到 DRAM 中,并跳到 OS 中执行并完成启动引导。


BL0 固化代码主要完成以下初始化:

关闭看门狗;
初始化 icache;
初始化栈;
初始化堆;
初始化块设备拷贝功能;
设置系统时钟;
拷贝 BL1 到 iRAM;
检查 BL1 的校验和,如果失败则第二启动模式(安全启动模式),校验成功则跳到 0xD0020000(IRAM)处执行。

其中 0xD0020000 ~ 0xD0020010 里的 16 字节头部信息是什么呢?这 16 字节信息用户是不能随便设置的.在《S5PV210_iROM_ApplicationNote_Preliminary_20091126》文档中规定

2.uboot中s5pv210的启动

2.1 uboot启动流程简介

BL0是出厂的时候就固化在IROM里的,所以我们的uboot就要实现BL1和BL2,BL1在uboot里叫做u-boot-spl.bin,BL2就是我们很熟悉的u-boot.bin.上面的三个阶段对应于uboot启动,他们的功能如下:

BL0:出厂的时候就固化在irom中一段代码,主要负责拷贝8kb的bl1到s5pv210的一个96kb大小内部sram(Internal SRAM)中运行。值得注意的是s5pv210的Internal SRAM支持的bl1的大小可以达到16kb,容量的扩增是为了适应bootloder变得越来复杂而做的。虽然如此,但目前我们制作出来的bl1的大小仍然可以保持在8kb以内,同样能满足需求。
BL1:u-boot的前8kb代码(s5pv210也支持16kb大小,原因上一点提过了),除了初始化系统时钟和一些基本的硬件外,主要负责完成代码的搬运工作(我设计成搬运bl1+bl2,而不仅仅是bl2),也就是将完整的u-boot代码(bl1+bl2)从nand flash或者mmcSD等的存储器中读取到内存中,然后跳转到内存中运行u-boot
BL2:完成全面的硬件初始化和加载OS到内存中,接着运行OS。上述几个阶段的流程描述在s5pv210_irom_application手册中有详细描述.
在实际的编译中会生成两个uboot.bin。拿webee210的uboot来说,最后在根目录下生成了uboot.bin和webee210-uboot.bin连个.bin文件。其中webee210-uboot.bin是通过/board/samsung/webee210/tools目录下的mkwebee210spl.bin对uboot.bin处理 生成的。主要是为其添加16k的头文件信息。


首先把启动部分的代码分为3部分,以start.S为主,另外还有lowlevel_init.S,mem_setup.S,ctr0.S。

lowlevel_init.S:主要是一部分硬件的初始化,尤其是系统时钟和DRAM的初始化。如果u-boot一旦被搬运到内存中运行,那么是必须要跳过时钟和DRAM的初始化的,因为这在搬运之前已经做过了。并且如果代码在内存中运行的时侯你却去初始化DRAM,那必然导致崩溃!
mem_setup.S:DRAM初始化代码和MMU相关代码放在这个文件中。
ctr0.S:u-boot自带的代码文件,存放汇编函数main。


2.2 启动过程

必须要明白的一点是,当代码从存储介质(nand flash,SD,norflash,onenand等)中搬运到了DRAM中后随即会跳转到内存中运行u-boot,接着会有一个重定位(relocate_code)的过程,relocate_code子函数在start.S中,而给relocate_code子函数传参数的是crt0.S中的main子函数。当判断到当前u-boot在内存的低地址处,那么relocate_code就会工作,把u-boot代码从低地址处再搬运到内存地址的顶端,然后跳转到新的位置去继续运行u-boot。而搬运的目标地址是在board_init_f()函数(此函数在/arch/arm/lib/board.c中)中计算出来的


2.2.1 start.S

#include <asm-offsets.h>#include <config.h>#include <version.h>#include <common.h>#include <configs/smart210.h>#include <s5pc110.h>.globl _start_start: bresetldrpc, _undefined_instruction  /*未定义指令异常,0x04*/  ldrpc, _software_interrupt/*软中断异常,0x08*/ ldrpc, _prefetch_abort         /*内存操作异常,0x0c*/ ldrpc, _data_abort/*数据异常,0x10*/ ldrpc, _not_used /*未适用,0x14*/ ldrpc, _irq/*慢速中断异常,0x1c*/ldrpc, _fiq/*快速中断异常,0x1c*/#ifdef CONFIG_SPL_BUILD_undefined_instruction: .word _undefined_instruction_software_interrupt:.word _software_interrupt_prefetch_abort:.word _prefetch_abort_data_abort:.word _data_abort_not_used:.word _not_used_irq:.word _irq_fiq:.word _fiq_pad:.word 0x12345678 /* now 16*4=64 */#else_undefined_instruction: .word undefined_instruction_software_interrupt:.word software_interrupt_prefetch_abort:.word prefetch_abort_data_abort:.word data_abort_not_used:.word not_used_irq:.word irq_fiq:.word fiq_pad:.word 0x12345678 /* now 16*4=64 */#endif/* CONFIG_SPL_BUILD */.global _end_vect_end_vect:.balignl 16,0xdeadbeef   //所以意思就是,接下来的代码,都要16字节对齐,不知之处,用0xdeadbeef填充。/************************************************************************* * * Startup Code (reset vector) * * do important init only if we don't start from memory! * setup Memory and board specific bits prior to relocation. * relocate armboot to ram * setup stack * *************************************************************************/ /*此地址中是一个word类型的变量,变量名是TEXT_BASE,此值见名知意,是text的base,即代码的基地址,在include/configs/webee210.h#define CONFIG_SYS_TEXT_BASE            0x23E00000*/.globl _TEXT_BASE_TEXT_BASE:.wordCONFIG_SYS_TEXT_BASE#ifdef CONFIG_TEGRA2/* * Tegra2 uses 2 separate CPUs - the AVP (ARM7TDMI) and the CPU (dual A9s). * U-Boot runs on the AVP first, setting things up for the CPU (PLLs, * muxes, clocks, clamps, etc.). Then the AVP halts, and expects the CPU * to pick up its reset vector, which points here. */.globl _armboot_start_armboot_start:.word _start        //*(_armboot_start) = _start#endif/* * These are defined in the board-specific linker script. */.globl _bss_start_ofs_bss_start_ofs:.word __bss_start - _start.global_image_copy_end_ofs_image_copy_end_ofs:.word __image_copy_end - _start.globl _bss_end_ofs_bss_end_ofs:.word __bss_end__ - _start.globl _end_ofs_end_ofs:.word _end - _start#ifdef CONFIG_USE_IRQ/* IRQ stack memory (calculated at run-time) */.globl IRQ_STACK_STARTIRQ_STACK_START:.word0x0badc0de/* IRQ stack memory (calculated at run-time) */.globl FIQ_STACK_STARTFIQ_STACK_START:.word 0x0badc0de#endif/* IRQ stack memory (calculated at run-time) + 8 bytes */.globl IRQ_STACK_START_INIRQ_STACK_START_IN:.word0x0badc0de

reset函数里主要是设置了cpu的模式,然后就跳转到cpu_init_crit函数.

/* * the actual reset code */reset:    bl    save_boot_params    //其不做任何事情,直接返回    /*     * set the cpu to SVC32 mode  //设置CPU模式为SVC32     */    mrs    r0, cpsr    bic    r0, r0, #0x1f    orr    r0, r0, #0xd3    msr    cpsr,r0#ifndef CONFIG_SKIP_LOWLEVEL_INIT    bl    cpu_init_crit  //#endif



/************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * *************************************************************************/cpu_init_crit:/* * Invalidate L1 I/D */movr0, #0@ set up for MCRmcrp15, 0, r0, c8, c7, 0@ invalidate TLBsmcrp15, 0, r0, c7, c5, 0@ invalidate icachemcrp15, 0, r0, c7, c5, 6@ invalidate BP arraymcr     p15, 0, r0, c7, c10, 4@ DSBmcr     p15, 0, r0, c7, c5, 4@ ISB/* * disable MMU stuff and caches */mrcp15, 0, r0, c1, c0, 0bicr0, r0, #0x00002000@ clear bits 13 (--V-)bicr0, r0, #0x00000007@ clear bits 2:0 (-CAM)orrr0, r0, #0x00000002@ set bit 1 (--A-) Alignorrr0, r0, #0x00000800@ set bit 11 (Z---) BTB#ifdef CONFIG_SYS_ICACHE_OFFbicr0, r0, #0x00001000@ clear bit 12 (I) I-cache#elseorrr0, r0, #0x00001000@ set bit 12 (I) I-cache#endifmcrp15, 0, r0, c1, c0, 0/* * Jump to board specific initialization... * The Mask ROM will have already initialized * basic memory. Go here to bump up clock rate and handle * wake up conditions. */movip, lr@ persevere link reg across callbllowlevel_init//跳转到lowlevel_init,与开发板相关的初始化movlr, ip@ restore linkmovpc, lr@ back to my caller#endif
cpu_init_crit中主要做初始化了memory和串口,接下来就是调用C函数board_init_f了,在调用C函数之前得设置栈

lowlevel_init.s存放于`board/samsung/webee210/lowlevel_init.s`


.globl lowlevel_initlowlevel_init:push{lr}   //把返回地址保存到栈中/* check reset status  */ldrr0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)ldrr1, [r0]bicr1, r1, #0xfff6ffffcmpr1, #0x10000beqwakeup_reset_precmpr1, #0x80000beqwakeup_reset_from_didle/* IO Retention release */ldrr0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)ldrr1, [r0]ldrr2, =IO_RET_RELorrr1, r1, r2strr1, [r0]/* Disable Watchdog */ldrr0, =ELFIN_WATCHDOG_BASE/* 0xE2700000 */movr1, #0strr1, [r0]/* SRAM(2MB) init for SMDKC110 *//* GPJ1 SROM_ADDR_16to21 */ldrr0, =ELFIN_GPIO_BASEldrr1, [r0, #GPJ1CON_OFFSET]bicr1, r1, #0xFFFFFFldrr2, =0x444444orrr1, r1, r2strr1, [r0, #GPJ1CON_OFFSET]ldrr1, [r0, #GPJ1PUD_OFFSET]ldrr2, =0x3ffbicr1, r1, r2strr1, [r0, #GPJ1PUD_OFFSET]/* GPJ4 SROM_ADDR_16to21 */ldrr1, [r0, #GPJ4CON_OFFSET]bicr1, r1, #(0xf<<16)ldrr2, =(0x4<<16)orrr1, r1, r2strr1, [r0, #GPJ4CON_OFFSET]ldrr1, [r0, #GPJ4PUD_OFFSET]ldrr2, =(0x3<<8)bicr1, r1, r2strr1, [r0, #GPJ4PUD_OFFSET]/* CS0 - 16bit sram, enable nBE, Byte base address */ldrr0, =ELFIN_SROM_BASE/* 0xE8000000 */movr1, #0x1strr1, [r0]/* PS_HOLD pin(GPH0_0) set to high */ldrr0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)ldrr1, [r0]orrr1, r1, #0x300orrr1, r1, #0x1strr1, [r0]/* when we already run in ram, we don't need to relocate U-Boot. * and actually, memory controller must be configured before U-Boot * is running in ram. */ldrr0, =0x00ffffffbicr1, pc, r0/* r0 <- current base addr of code */ldrr2, _TEXT_BASE/* r1 <- original base addr in ram */bicr2, r2, r0/* r0 <- current base addr of code */cmp     r1, r2                  /* compare r0, r1                  */beq     1f/* r0 == r1 then skip sdram init   *//* init system clock */bl system_clock_init/* Memory initialize */bl mem_ctrl_asm_init1:/* for UART */bl uart_asm_initbl tzpc_init#if defined(CONFIG_ONENAND)bl onenandcon_init#endif#if defined(CONFIG_NAND)/* simple init for NAND */bl nand_asm_init#endif/* check reset status  */ldrr0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)ldrr1, [r0]bicr1, r1, #0xfffeffffcmpr1, #0x10000beqwakeup_reset_pre/* ABB disable */ldrr0, =0xE010C300orrr1, r1, #(0x1<<23)strr1, [r0]/* Print 'K' */ldrr0, =ELFIN_UART_CONSOLE_BASEldrr1, =0x4b4b4b4bstrr1, [r0, #UTXH_OFFSET]pop{pc}
总结一下上面的东东,看看,start.S是如何对CPU和开发板进行初始化的.首先start.S 执行reset函数,在reset函数里初始化了CPU 为SVC32后,就跳转到cpu_init_crit函数.在cpu_init_crit函数里设置了 TLBs、icache、BP array等,然后就跳转到`board/samsung/webee210/lowlevel_init.S`进行与开发板相关的初始化配置.在这里
关看门狗,初始化 SRAM,初始化 SROM,初始化电源保持控制器,初始化系统时钟,内存初始化
初始化串口,Nand Flash 早期初始化等.

接下来我们继续回到start.S里,看call_board_init_f这个函数.

/* Set stackpointer in internal RAM to call board_init_f */call_board_init_f:/* 为待会要执行的 C 函数设置栈 */ldrsp, =(CONFIG_SYS_INIT_SP_ADDR)bicsp, sp, #7 /* 8-byte alignment for ABI compliance */ldrr0,=0x00000000#if defined(CONFIG_TINY210) || defined(CONFIG_WEBEE210)adrr4, _startldrr5,_TEXT_BASE/* 判断当前代码是否已在 RAM 上运行(DDR2) *//* 因为现在还在第一阶段,还在代码 SRAM 上运行,* 所以不会直接跳到 board_init_in_ram*/cmp     r5,r4beqboard_init_in_ram/* PRO_ID_BASE 是判断启动方式的寄存器 */ldrr0, =PRO_ID_BASE        ldrr1, [r0,#OMR_OFFSET]        bicr2, r1, #0xffffffc1/* NAND BOOT ,判断是否通过 Nand Flash 方式启动 */cmpr2, #0x0@ 512B 4-cyclemoveqr3, #BOOT_NANDcmpr2, #0x2@ 2KB 5-cyclemoveqr3, #BOOT_NANDcmpr2, #0x4@ 4KB 5-cycle8-bit ECCmoveqr3, #BOOT_NANDcmpr2, #0x6@ 4KB 5-cycle16-bit ECCmoveqr3, #BOOT_NANDcmpr2, #0x8@ OneNAND Muxmoveqr3, #BOOT_ONENAND/* SD/MMC BOOT */cmp     r2, #0xcmoveq   r3, #BOOT_MMCSD/* NOR BOOT */cmp     r2, #0x14moveq   r3, #BOOT_NOR/* Uart BOOTONG failed */cmp     r2, #(0x1<<4)moveq   r3, #BOOT_SEC_DEVldrr0, =INF_REG_BASEstrr3, [r0, #INF_REG3_OFFSET]ldrr1, [r0, #INF_REG3_OFFSET]cmpr1, #BOOT_NAND/* 0x0 => boot device is nand */beqnand_boot_210cmp     r1, #BOOT_MMCSDbeq     mmcsd_boot_210nand_boot_210:bl     board_init_f_nandmmcsd_boot_210:bl     board_init_fboard_init_in_ram:#endifblboard_init_f

假如是nand启动,`arch\arm\cpu\armv7\s5pc1xx/nand_cp.c`

void board_init_f_nand(unsigned long bootflag)
{
        __attribute__((noreturn)) void (*uboot)(void);
        copy_uboot_to_ram_nand();

        /* Jump to U-Boot image */
        uboot = (void *)CONFIG_SYS_TEXT_BASE;
    (*uboot)();
        /* Never returns Here */
}

假如是sd卡启动 ,`arch\arm\cpu\armv7\s5pc1xx/mmc_boot.c`
void board_init_f(unsigned long bootflag)
{
        __attribute__((noreturn)) void (*uboot)(void);
        /*把第二阶段的 U-Boot 从 sd 拷贝到内存的函数*/
        copy_uboot_to_ram();

        /* Jump to U-Boot image */
        uboot = (void *)CONFIG_SYS_TEXT_BASE;
        /* 跳转到第二阶段所在的内存(DDR2)地址处 */
        (*uboot)();
        /* Never returns Here */
}


到此为止,上面的内容为第一阶段所做的工作,即bl1,接下来是bl2,完成全面的硬件初始化和加载OS到内存中,接着运行OS
看回start.S里的call_board_init_f函数,在我们进行是在nand flash启动还是sd卡启动之前有一段代码如下,这段代码是判断当前的代码是否运行于RAM上的.假如不是则根据PRO_ID_BASE判断是哪种启动方式,然后调用board_init_f()或board_init_f_nand(),在他们里面有调用了copy_uboot_to_ram()0或copy_uboot_to_ram_nand(),所以uboot将会被copy到ram中,即代码开始从ram上运行.当代码已经在ram上运行了,则执行board_init_in_ram()函数.我看看接下来看看这个函数都做了什么.


#if defined(CONFIG_TINY210) || defined(CONFIG_WEBEE210)adrr4, _startldrr5,_TEXT_BASE/* 判断当前代码是否已在 RAM 上运行(DDR2) *//* 因为现在还在第一阶段,还在代码 SRAM 上运行,* 所以不会直接跳到 board_init_in_ram*/cmp     r5,r4beqboard_init_in_ram/* PRO_ID_BASE 是判断启动方式的寄存器 */ldrr0, =PRO_ID_BASE        ldrr1, [r0,#OMR_OFFSET]        bicr2, r1, #0xffffffc1



board_init_in_ram执行的也是board_init_f()这个函数,这个函数在前也出现过,当我们搜索board_init_f函数时,我们将发现在`arch\arm\cpu\armv7\s5pc1xx/mmc_boot.c`和`arch\arm\lib\board.c`中都有出现过.那么系统是如何判断是执行哪个board_init_f的呢.我觉得应该是与程序执行的内存地址不同有关.在没有判断进入ram之前,board_init_f在一个内存里,当进入ram之后,board_init_f则在另外的地址里.
这里使用相同的函数名,不知道通过什么连接方式实现不冲突,令人疑惑.网上搜到大多数的board_init_f函数都指的是`arch\arm\lib\board.c`里的这个board_init_f.


board_init_in_ram:
#endif
    bl    board_init_f


下面看看这个board_init_f都做了什么工作.

void board_init_f(ulong bootflag){bd_t *bd;init_fnc_t **init_fnc_ptr;gd_t *id;ulong addr, addr_sp;/* Pointer is writable since we allocated a register for it */gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);/* compiler optimization barrier needed for GCC >= 3.4 */__asm__ __volatile__("": : :"memory");memset((void *)gd, 0, sizeof(gd_t));gd->mon_len = _bss_end_ofs; /* 依次调用 init_fnc_ptr 指针数组里的每个函数 */for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}


涉及到两个重要的数据结构:1)bd_t结构体,关于开发板信息(波特率,ip, 平台号,启动参数)。2)gd_t结构体成员主要是一些全局的系统初始化参数

  `gd=(gd_t *)(CONFIG_SYS_INIT_SP_ADDR) &~0x07)`

CONFIG_SYS_INIT_SP_ADDR的定义如下:
`#define CONFIG_SYS_INIT_SP_ADDR (0x22000)`
0x22000 & ~0x07,为了保证结果八字节对齐。
查芯片手册,可知0x0002_0000----0x0003_8000范围,为96K,是IRAM的空间。即gd指向IRAM的一个地址。

在本文中前面有声明DECLARE_GLOBAL_DATA_PTR
跟踪定义可看到下面形式:
`#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")`这个声明告诉编译器使用寄存器r8来存储gd_t类型的指针gd,即这个定义声明了一个指针,并且指明了它的存储位置。(即gd值放在寄存器r8中)

- register表示变量放在机器的寄存器
- volatile用于指定变量的值可以由外部过程异步修改
- 并且这个指针现在被赋值为CONFIG_SYS_INIT_SP_ADDR) &~0x07, __asm__ __volatile__("": : :"memory"); memset((void *)gd, 0, sizeof(gd_t));
- memory 强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。
- __asm__用于指示编译器在此插入汇编语句。                           
- volatile 用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。 memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。

- "":::表示这是个空指令。

`gd->mon_len = _bss_end_ofs; `

_bss_end_ofs的定义在start.S中: .globl _bss_end_ofs

_bss_end_ofs:
        .word __bss_end__ - _start

.word就是在当前地址_bss_end_ofs放值 __bss-end__ - _start。看到是所以段的大小,此时,新开终端。打开u-boot.lds,找__bss_end__ Gd->mon_len存的值就是u-boot的实际大小。

init_fnc_ptr是一个函数指针,而init_sequence是一个函数队列,定义如下:

init_fnc_t *init_sequence[] = {#if defined(CONFIG_ARCH_CPU_INIT)    arch_cpu_init,        /* basic arch cpu dependent setup */#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 */#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,};

所以通过循环,调用了函数指针数组中的一系列初始化函数:arch_cpu_init,timer_init,env_int,init_baudrate, serial_init,console_init_f,         display_banner, dram_init。

接下是board_init_f的后半段函数内容:

debug("monitor len: %08lX\n", gd->mon_len);/* * Ram is setup, size stored in gd !! */debug("ramsize: %08lX\n", gd->ram_size);#if defined(CONFIG_SYS_MEM_TOP_HIDE)/* * Subtract specified amount of memory to hide so that it won't * get "touched" at all by U-Boot. By fixing up gd->ram_size * the Linux kernel should now get passed the now "corrected" * memory size and won't touch it either. This should work * for arch/ppc and arch/powerpc. Only Linux board ports in * arch/powerpc with bootwrapper support, that recalculate the * memory size from the SDRAM controller setup will have to * get fixed. */gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;#endif  /*这里addr是内存的高地址,0x20000000+256M        关于gd->ram_size,在初始化函数dram_init中,已经给过值了 */addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;#ifdef CONFIG_LOGBUFFER#ifndef CONFIG_ALT_LB_ADDR/* reserve kernel log buffer */addr -= (LOGBUFF_RESERVE);debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,addr);#endif#endif#ifdef CONFIG_PRAM/* * reserve protected RAM */i = getenv_r("pram", (char *)tmp, sizeof(tmp));reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) :CONFIG_PRAM;addr -= (reg << 10);/* size is in kB */debug("Reserving %ldk for protected RAM at %08lx\n", 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: %08lx\n", addr);#endif/* round down to next 4 kB limit */addr &= ~(4096 - 1);debug("Top of RAM usable for U-Boot at: %08lx\n", addr);#ifdef CONFIG_LCD#ifdef CONFIG_FB_ADDRgd->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: %08lx\n", gd->mon_len >> 10, addr);/*宏CONFIG_SPL_BUILD,用ctags找,在spl/Makefile中能找到,如下:        CONFIG_SPL_BUILD := y        export CONFIG_SPL_BUILD        但是从u-boot根目录的Makefile中,可以看到,ALL-$(CONFIG_SPL),如下:        ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin        CONFIG_SPL没有定义(include/configs/fsc100.h中没这个宏),因此,相当于spl/Makefile没有执行。*/#ifndef CONFIG_SPL_BUILD/* * reserve memory for malloc() arena */addr_sp = addr - TOTAL_MALLOC_LEN;debug("Reserving %dk for malloc() at: %08lx\n",TOTAL_MALLOC_LEN >> 10, addr_sp);/* * (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: %08lx\n",sizeof (bd_t), addr_sp);#ifdef CONFIG_MACH_TYPEgd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */#endifaddr_sp -= sizeof (gd_t);id = (gd_t *) addr_sp;debug("Reserving %zu Bytes for Global Data at: %08lx\n",sizeof (gd_t), addr_sp);/* setup stackpointer for exeptions */gd->irq_sp = addr_sp;#ifdef CONFIG_USE_IRQaddr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",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;#elseaddr_sp += 128;/* leave 32 words for abort-stack   */gd->irq_sp = addr_sp;#endifdebug("New Stack Pointer is: %08lx\n", addr_sp);#ifdef CONFIG_POSTpost_bootmode_init();post_run(NULL, POST_ROM | post_bootmode_get(0));#endifgd->bd->bi_baudrate = gd->baudrate;/* Ram ist board specific, so move it to board code ... */dram_init_banksize();display_dram_config();/* and display it */gd->relocaddr = addr;gd->start_addr_sp = addr_sp;gd->reloc_off = addr - _TEXT_BASE;debug("relocation Offset is: %08lx\n", gd->reloc_off);memcpy(id, (void *)gd, sizeof(gd_t));/* 跳去执行重定位代码 */relocate_code(addr_sp, id, addr);/* NOTREACHED - relocate_code() does not return */}



relocate_code(addr_sp, id, addr);在start.S中定义,C又回到了汇编
(栈指针,全局数据的地方, 搬后起始地址)
完成了第二次搬运过程,把u-boot从27e00000搬到了addr.

/** 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_coderelocate_code:         mov r4, r0 /* save addr_sp */         mov r5, r1 /* save addr of gd */         mov r6, r2          /* save addr of destination */……/* Set up the stack */stack_setup:         mov sp, r4adr r0, _start//得到u-boot的链接地址cmp r0, r6//判断u-boot是否已经搬移到最终地址moveq r9, #0      /* no relocation. relocation offset(r9) = 0 */beq clear_bss               /* skip relocation *///若不需要再次搬移,直接清bss段。mov r1, r6 /* r1 <- scratch for copy_loop */ldr r3, _image_copy_end_ofs//需要搬移的u-boot的大小。add r2, r0, r3 /* r2 <- source end address *///r0和r2之间的内容全部搬走。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需要自搬移,即不在最终地址运行,把u-boot复制到了SDRAM的高端地址  .....jump_2_ram:/* * If I-cache is enabled invalidate it */#ifndef CONFIG_SYS_ICACHE_OFFmcrp15, 0, r0, c7, c5, 0@ invalidate icachemcr     p15, 0, r0, c7, c10, 4@ DSBmcr     p15, 0, r0, c7, c5, 4@ ISB#endif//程序从_board_init_r_ofs开始执行,目的是为了跳到board_init_rldrr0, _board_init_r_ofsadrr1, _start/* _board_init_r_ofs + _start = board_init_r */addlr, r0, r1addlr, lr, r9/* setup parameters for board_init_r */movr0, r5/* gd_t */movr1, r6/* dest_addr *//* jump to it ... */movpc, lr             /* 跳转到 board_init_r */ /* board_init_r - _start = _board_init_r_ofs */_board_init_r_ofs:.word board_init_r - _start

在board_init_r函数中,主要针对开发板自身的外设资源进行初始化,包括串口,网卡,sd卡等
/* ************************************************************************ * * This is the next part if the initialization sequence: we are now * running from RAM and have a "normal" C environment, i. e. global * data can be written, BSS has been cleared, the stack size in not * that critical any more, etc. * ************************************************************************ */void board_init_r(gd_t *id, ulong dest_addr){char *s;bd_t *bd;ulong malloc_start;#if !defined(CONFIG_SYS_NO_FLASH)ulong flash_size;#endifgd = id;bd = gd->bd;gd->flags |= GD_FLG_RELOC;/* tell others: relocation done */monitor_flash_len = _end_ofs;/* Enable caches */enable_caches();debug("monitor flash len: %08lX\n", monitor_flash_len);board_init();/* Setup chipselects */#ifdef CONFIG_SERIAL_MULTIserial_initialize();#endifdebug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);#ifdef CONFIG_LOGBUFFERlogbuff_init_ptrs();#endif#ifdef CONFIG_POSTpost_output_backlog();#endif/* The Malloc area is immediately below the monitor copy in DRAM */malloc_start = dest_addr - TOTAL_MALLOC_LEN;mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);#if !defined(CONFIG_SYS_NO_FLASH)puts("Flash: ");flash_size = flash_init();if (flash_size > 0) {# ifdef CONFIG_SYS_FLASH_CHECKSUMprint_size(flash_size, "");/* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */s = getenv("flashchecksum");if (s && (*s == 'y')) {printf("  CRC: %08X", crc32(0,(const unsigned char *) CONFIG_SYS_FLASH_BASE,flash_size));}putc('\n');# else/* !CONFIG_SYS_FLASH_CHECKSUM */print_size(flash_size, "\n");# endif /* CONFIG_SYS_FLASH_CHECKSUM */} else {puts(failed);hang();}#endif#if defined(CONFIG_CMD_NAND)puts("NAND:  ");nand_init();/* go init the NAND */#endif#if defined(CONFIG_CMD_ONENAND)onenand_init();#endif#ifdef CONFIG_GENERIC_MMC       puts("MMC:   ");       mmc_initialize(bd);#endif#ifdef CONFIG_HAS_DATAFLASHAT91F_DataflashInit();dataflash_print_info();#endif/* initialize environment */env_relocate();#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)arm_pci_init();#endif/* IP Address */gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");stdio_init();/* get the devices list going. */jumptable_init();#if defined(CONFIG_API)/* Initialize API */api_init();#endifconsole_init_r();/* fully init console as a device */#if defined(CONFIG_ARCH_MISC_INIT)/* miscellaneous arch dependent initialisations */arch_misc_init();#endif#if defined(CONFIG_MISC_INIT_R)/* miscellaneous platform dependent initialisations */misc_init_r();#endif /* set up exceptions */interrupt_init();/* enable exceptions */enable_interrupts();/* Perform network card initialisation if necessary */#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)/* XXX: this needs to be moved to board init */if (getenv("ethaddr")) {uchar enetaddr[6];eth_getenv_enetaddr("ethaddr", enetaddr);smc_set_mac_addr(enetaddr);}#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 *//* Initialize from environment */s = getenv("loadaddr");if (s != NULL)load_addr = simple_strtoul(s, NULL, 16);#if defined(CONFIG_CMD_NET)s = getenv("bootfile");if (s != NULL)copy_filename(BootFile, s, sizeof(BootFile));#endif#ifdef BOARD_LATE_INITboard_late_init();#endif#ifdef CONFIG_BITBANGMIIbb_miiphy_init();#endif#if defined(CONFIG_CMD_NET)#if defined(CONFIG_NET_MULTI)puts("Net:   ");#endifeth_initialize(gd->bd);puts("\n ");puts("\n");#if defined(CONFIG_RESET_PHY_R)debug("Reset Ethernet PHY\n");reset_phy();#endif#endif#ifdef CONFIG_POSTpost_run(NULL, POST_RAM | post_bootmode_get(0));#endif#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)/* * Export available size of memory for Linux, * taking into account the protected RAM at top of memory */{ulong pram;uchar memsz[32];#ifdef CONFIG_PRAMchar *s;s = getenv("pram");if (s != NULL)pram = simple_strtoul(s, NULL, 10);elsepram = CONFIG_PRAM;#elsepram = 0;#endif#ifdef CONFIG_LOGBUFFER#ifndef CONFIG_ALT_LB_ADDR/* Also take the logbuffer into account (pram is in kB) */pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;#endif#endifsprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);setenv("mem", (char *)memsz);}#endif/* * 进入主循环,也就说,U-Boot 的命令状态,* 其实就是个死循环状态,它会不断地检测用户* 是否有命令输入,然后分析输入的命令是什么,* 最后执行输入命令对应的函数,之后又再继续地循环*//* main_loop() can return to retry autoboot, if so just run it again. */for (;;) {main_loop();}/* NOTREACHED - no way out of command loop except booting */}

这个main_loop函数定义于`common/main.c`

void main_loop (void){#ifndef CONFIG_SYS_HUSH_PARSERstatic char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };int len;int rc = 1;int flag;#endif#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)char *s;int bootdelay;#endif#ifdef CONFIG_PREBOOTchar *p;#endif#ifdef CONFIG_BOOTCOUNT_LIMITunsigned long bootcount = 0;unsigned long bootlimit = 0;char *bcs;char bcs_set[16];#endif /* CONFIG_BOOTCOUNT_LIMIT */#ifdef CONFIG_BOOTCOUNT_LIMITbootcount = bootcount_load();bootcount++;bootcount_store (bootcount);sprintf (bcs_set, "%lu", bootcount);setenv ("bootcount", bcs_set);bcs = getenv ("bootlimit");bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;#endif /* CONFIG_BOOTCOUNT_LIMIT */#ifdef CONFIG_MODEM_SUPPORTdebug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);if (do_mdm_init) {char *str = strdup(getenv("mdm_cmd"));setenv ("preboot", str);  /* set or delete definition */if (str != NULL)free (str);mdm_init(); /* wait for modem connection */}#endif  /* CONFIG_MODEM_SUPPORT */#ifdef CONFIG_VERSION_VARIABLE{setenv ("ver", version_string);  /* set version variable */}#endif /* CONFIG_VERSION_VARIABLE */#ifdef CONFIG_SYS_HUSH_PARSERu_boot_hush_start ();#endif#if defined(CONFIG_HUSH_INIT_VAR)hush_init_var ();#endif#ifdef CONFIG_PREBOOTif ((p = getenv ("preboot")) != NULL) {# ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1);/* disable Control C checking */# endif# ifndef CONFIG_SYS_HUSH_PARSERrun_command (p, 0);# elseparse_string_outer(p, FLAG_PARSE_SEMICOLON |    FLAG_EXIT_FROM_LOOP);# endif# ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev);/* restore Control C checking */# endif}#endif /* CONFIG_PREBOOT */#if defined(CONFIG_UPDATE_TFTP)update_tftp (0UL);#endif /* CONFIG_UPDATE_TFTP */#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)s = getenv ("bootdelay");bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);# ifdef CONFIG_BOOT_RETRY_TIMEinit_cmd_timeout ();# endif/* CONFIG_BOOT_RETRY_TIME */#ifdef CONFIG_POSTif (gd->flags & GD_FLG_POSTFAIL) {s = getenv("failbootcmd");}else#endif /* CONFIG_POST */#ifdef CONFIG_BOOTCOUNT_LIMITif (bootlimit && (bootcount > bootlimit)) {printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",        (unsigned)bootlimit);s = getenv ("altbootcmd");}else#endif /* CONFIG_BOOTCOUNT_LIMIT */s = getenv ("bootcmd");debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");if (bootdelay >= 0 && s && !abortboot (bootdelay)) {# ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1);/* disable Control C checking */# endif# ifndef CONFIG_SYS_HUSH_PARSERrun_command (s, 0);# elseparse_string_outer(s, FLAG_PARSE_SEMICOLON |    FLAG_EXIT_FROM_LOOP);# endif# ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev);/* restore Control C checking */# endif}# ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) {s = getenv("menucmd");if (s) {# ifndef CONFIG_SYS_HUSH_PARSERrun_command(s, 0);# elseparse_string_outer(s, FLAG_PARSE_SEMICOLON |FLAG_EXIT_FROM_LOOP);# endif}}#endif /* CONFIG_MENUKEY */#endif /* CONFIG_BOOTDELAY *//*添加MENU菜单 make by nietao email: nietaooldman@126.com */#ifdef CONFIG_CMD_MENUrun_command("menu", 0);#endif/* * Main Loop for Monitor Command Processing */#ifdef CONFIG_SYS_HUSH_PARSERparse_file_outer();/* This point is never reached */for (;;);#elsefor (;;) {#ifdef CONFIG_BOOT_RETRY_TIMEif (rc >= 0) {/* Saw enough of a valid command to * restart the timeout. */reset_cmd_timeout();}#endiflen = readline (CONFIG_SYS_PROMPT);flag = 0;/* assume no special flags for now */if (len > 0)strcpy (lastcommand, console_buffer);else if (len == 0)flag |= CMD_FLAG_REPEAT;#ifdef CONFIG_BOOT_RETRY_TIMEelse if (len == -2) {/* -2 means timed out, retry autoboot */puts ("\nTimed out waiting for command\n");# ifdef CONFIG_RESET_TO_RETRY/* Reinit board to run initialization code again */do_reset (NULL, 0, 0, NULL);# elsereturn;/* retry autoboot */# endif}#endifif (len == -1)puts ("<INTERRUPT>\n");elserc = run_command (lastcommand, flag);if (rc <= 0) {/* invalid command or not repeatable, forget it */lastcommand[0] = 0;}}#endif /*CONFIG_SYS_HUSH_PARSER*/}





0 0
原创粉丝点击