NanoPC-T2 Uboot启动过程分析 - 2-1 初始启动

来源:互联网 发布:网络管理高级题库 编辑:程序博客网 时间:2024/05/08 04:35

从上一章的分析可看到,UBoot已经从SD卡或Flash被复制到 0x42C0_0000 内存上,并开始从这里开始执行。

为了方便说明,以下将UBoot源码的根目录位置表示为 /uboot-root 。

首先,先找到 /uboot-root/u-boot.lds,这是uboot编译时的链接文件,打开后重点关注以下内容:

ENTRY(_stext)SECTIONS{ . = 0x00000000; . = ALIGN(4); .text : {  *(.__image_copy_start)  arch/arm/cpu/slsiap/s5p4418/start.o (.text*)  *(.text*) } ...  #以下内容省略。}

从上面的内容可以看出两个信息,一是代码从 _stext 标签开始执行,二是该标签位于 /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S 。

打开 /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S 并找到 _stext 标签。代码如下所示:

    .globl    _stext_stext:    b     reset    ldr    pc, _undefined_instruction    ldr    pc, _software_interrupt    ldr    pc, _prefetch_abort    ldr    pc, _data_abort    ldr    pc, _not_used    ldr    pc, _irq    ldr    pc, _fiq

这是S5P4418的 Exception Handlers Vectors。这是芯片所规定的,可以参考S5P4418的用户手册说明。按照执行规律,CPU会先执行 b reset 的指令,跳转到 reset 标签继续执行。该标签在同一个文件中的第78行。从这里开始就是uboot正式开始工作的地方。

首先要将CPU设置为SVC32的模式。

    .globl resetreset:    bl    save_boot_params    /*     * set the cpu to SVC32 mode     */    mrs    r0, cpsr    bic    r0, r0, #0x1f    orr    r0, r0, #0xd3    msr    cpsr,r0

具体的设置方式与所涉及的寄存器,和CPU的内核有关,此处可以到ARM官网搜索 cortex-a9 内核相关的用户手册。接下来会遇到这样的一段代码:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT    bl    cpu_init_cp15    bl    cpu_init_crit#endif

这段代码是否被执行取决于 CONFIG_SKIP_LOWLEVEL_INIT 这个宏的定义。通常这些选择性开关的宏会定义在 /uboot-root/include/configs/XXX.h 中。此处找到 /uboot-root/include/configs/s5p4418_nanopi2.h 。发现这个头文件中并没有定义 CONFIG_SKIP_LOWLEVEL_INIT 这个宏,即上述这段代码是要被执行的。

首先是执行 bl cpu_init_cp15 这条语句。由于此时系统栈还没有初始化,因此只能靠 lr 寄存器来记录调用返回地址。因此,先记录当前相关寄存器的内容:

lr = &( bl cpu_init_cp15 ) @ /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S

标签 cpu_init_cp15 在同一文件中的第207行。根据注释可以看出这里主要完成了两件事,一是让一级的Icache和Dcache失效,二是禁用MMU和Caches。完成这两个任务后就根据 lr 寄存器的内容返回了。接下来是执行 bl cpu_init_crit 这条语句。同样,记录当前相关寄存器的内容:

lr = &( bl cpu_init_crit ) @ /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S

cpu_init_crit的代码如下:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT/************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * *************************************************************************/ENTRY(cpu_init_crit)    /*     * 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.     */    b    lowlevel_init        @ go setup pll,mux,memoryENDPROC(cpu_init_crit)#endif

从代码可以看出, cpu_init_crit 标签仅调用了 lowlevel_init 这个标签。由于使用的是 b 跳转命令,因此 lr 寄存器的值没变,从而直接跳转到 lowlevel_init 标签。该标签位于 /uboot-root/arch/arm/cpu/slsiap/s5p4418/low_init.S 中。在这个过程中主要完成了4件事,由于这4件事是和CPU有关的,因此不在Uboot分析的讨论范围内,这里忽略了部分代码:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT/* ************************************************************************* * * CPU_init_critical * ************************************************************************* */    .globl lowlevel_initlowlevel_init:    /* get cpu id */    ...    /* secure SCU invalidate */    ...    /* join SMP */    ...    /* enable maintenance broadcast */    ...    mov     pc, lr                            @ back to caller#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

过程的最后一句,是根据 lr 寄存器进行返回。因此uboot返回到 lr 的地址继续执行。重新回到 /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S 的94行,出现以下语句:

#ifdef CONFIG_RELOC_TO_TEXT_BASE

由于宏 CONFIG_RELOC_TO_TEXT_BASE 已定义在 /uboot-root/include/configs/s5p4418_nanopi2.h 中,因此被该宏包括的语句都要执行。首先是这样的一段代码:

relocate_to_text:    /*     * relocate u-boot code on memory to text base     * for nexell arm core (add by jhkim)     */    adr    r0, _stext                /* r0 <- current position of code   */    ldr    r1, TEXT_BASE            /* test if we run from flash or RAM */    cmp r0, r1                  /* don't reloc during debug         */    beq clear_bss    ldr    r2, _bss_start_ofs    add    r2, r0, r2                /* r2 <- source end address         */

这段代码是用来判断当前运行的位置是否在RAM上。代码中的 adr r0, _stext 这条语句是用来获取当前 _stext 标签所位于的真实地址。然后用 ldr r1, TEXT_BASE 来获取目标运行的地址。经过搜索,TEXT_BASE这个标签位于同一文件的第50行:

.globl TEXT_BASETEXT_BASE:    .word    CONFIG_SYS_TEXT_BASE

可以看到,TEXT_BASE的值由宏 CONFIG_SYS_TEXT_BASE 来决定。该宏同样定义在 /uboot-root/include/configs/s5p4418_nanopi2.h 中,其值为 0x42C0_0000 。在第一章上电启动的时候已经分析过,UBoot被拷贝到 0x42C0_0000 的地址上,并且从 _stext 标签开始执行。因此, _stext 与 TEXT_BASE 的值相同,即 UBoot 已在RAM上运行。根据程序流程,UBoot会继续执行 clear_bss 标签上的内容。以下是该标签的代码:

clear_bss:    ldr    r0, _bss_start_ofs    ldr    r1, _bss_end_ofs    ldr    r4, TEXT_BASE            /* text addr */    add    r0, r0, r4    add    r1, r1, r4    mov    r2, #0x00000000            /* clear */clbss_l:str    r2, [r0]            /* clear loop... */    add    r0, r0, #4    cmp    r0, r1    bne    clbss_l

从代码中可以看出,这部分的功能主要是用0去填充bss段的内容。bss段是存放未初始化的变量,经过这一步骤后,所有未初始化的变量都被初始化为0了。接下来会执行到这样的一段代码:

#ifdef CONFIG_MMU_ENABLE    bl    mmu_turn_on#endif

经过搜索,宏 CONFIG_MMU_ENABLE 在 /uboot-root/include/configs/s5p4418_nanopi2.h 中已被定义,因此UBoot会执行 bl mmu_turn_on 这条指令。同样,由于使用 bl 指令跳转,因此先记录相关寄存器的值:

lr = &( bl mmu_turn_on ) @ /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S

该标签的内容位于 /uboot-root/arch/arm/cpu/slsiap/s5p4418/mmu_asm.S 中,其内容如下:

    .globl mmu_turn_onmmu_turn_on:    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)    mov r13, lr    bl mmu_on    mov     pc, r13                        @ back to caller

这部分的代码是为调用 mmu_on 做准备的。由于 mmu_on 是用c语言编写的,所以这里要为c语言构造一个运行环境,包括设置系统栈等等。在调用到 bl mmu_on 的时候,相关寄存器的内容如下:

r13 = &( bl mmu_turn_on ) @ /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.Ssp = 0x42C0_0000lr = &( bl mmu_on ) @ /uboot-root/arch/arm/cpu/slsiap/s5p4418/mmu_asm.S

到这里,终于可以先解释一个问题了。那就是根据芯片手册的说明,RAM的起始地址在0x4000_0000,为什么UBoot不是复制到RAM起始地址,而是在0x42C0_0000这里呢?那是因为0x4000_0000到0x42BF_FFFF这44KB已作为了UBoot的栈空间了。因此目前的内容空间使用分配如下:

0xC000_0000  UBoot0x42C0_0000  UBoot-Stack0x4000_0000

言归正传, mmu_on 这一个函数位于 /uboot-root/arch/arm/cpu/slsiap/s5p4418/mmu.c 中,其代码如下:

void mmu_on(void){    //void *vector_base = (void *)0xFFFF0000;    dcache_disable();     arm_init_before_mmu(); /* Flush DCACHE */    /* copy vector table */    //memcpy(vector_base, (void const *)CONFIG_SYS_TEXT_BASE, 64);    mmu_page_table_flush(PAGE_TABLE_START, PAGE_TABLE_SIZE);            make_page_table((u32*)ptable); /* Make MMU PAGE TABLE */     enable_mmu(PAGE_TABLE_START);#if defined (SMP_SCU_ENABE)     scu_enable((void __iomem *)MPPR_REG);#endif}

由于本人对MMU还没有深入的研究,而且这部分代码也与UBoot的执行流程没多大的影响。因此这部分的解析暂时先省略,等以后有了深入的了解后再进行补充说明。该函数返回后将回到 /uboot-root/arch/arm/cpu/slsiap/s5p4418/mmu_asm.S 继续执行 mov pc, r13 这一条语句,这里又是一次返回。根据寄存器的记录,这次返回到 /uboot-root/arch/arm/cpu/slsiap/s5p4418/start.S 中,并从第134行继续执行。

由于篇幅原因,并且之后的内容又是另一个小阶段,因此本节先暂且结束。

0 0
原创粉丝点击