zoukankan      html  css  js  c++  java
  • Uboot启动分析笔记-----Stage1(start.S与lowlevel_init.S详解)

    Uboot启动分析笔记-----Stage1(start.S与lowlevel_init.S详解)

     

    1  u-boot.lds

        首先了解uboot的链接脚本board/my2410/u-boot.lds,它定义了目标程序各部分的链接顺序。

    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")  /*指定输出可执行文件为ELF格式,32为,ARM小端*/

    OUTPUT_ARCH(arm)/*指定输出可执行文件为ARM平台*/

    ENTRY(_start)/*起始代码段为 _start*/

    SECTIONS

    {

             /* 指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置*、

             . = 0x00000000;从 0x0位置开始

             . = ALIGN(4); 4字节对齐

             .text :

             {

                       cpu/arm920t/start.o    (.text)

                       board/my2440/lowlevel_init.o      (.text)

                       *(.text)

             }

             . = ALIGN(4);

             .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

             . = ALIGN(4);

             .data : { *(.data) }  /*  只读数据段 ,所有的只读数据段都放在这个位置*/

             . = ALIGN(4);

             .got : { *(.got) }  /*指定got段, got段式是uboot自定义的一个段, 非标准段*/

             . = .;

             __u_boot_cmd_start = .; /*把__u_boot_cmd_start赋值为当前位置, 即起始位置*/

             .u_boot_cmd : { *(.u_boot_cmd) }  /*  u_boot_cmd段,所有的u-boot命令相关的定义都放在这个位置,因为每个命令定义等长,

                           所以只要以__u_boot_cmd_start为起始地址 进行查找就可以很快查找到某一个命令的定义,并依据定义的命令指针调用相应的函数进行处理用户的任务*/

             __u_boot_cmd_end = .;   /* u_boot_cmd段结束位置,由此可以看出,这段空间的长度并没有严格限制,

                                                     用户可以添加一些u-boot的命令,最终都会在连接是存放在这个位置。*/

             . = ALIGN(4);

             __bss_start = .; /*把__bss_start赋值为当前位置,即bss段的开始位置*/

             .bss (NOLOAD) : { *(.bss) . = ALIGN(4); } /*指定bss段,这里NOLOAD的意思是这段不需装载,仅在执行域中才会有这段*/

             _end = .; /*把_end赋值为当前位置,即bss段的结束位置*/

    }

    第一个链接的是cpu/board/start.o,也即Uboot的入口指令在start中,下面详细分析程序的跳转和函数调用关系。

    2 Stage1 : cpu/arm920t/start.S

        这个汇编程序时UBoot的入口程序,以复位向量开头。

                        reset

                            ↓

                    cpu_init_crit

                            ↓

                       relocate

                            ↓

                    stack_setup

                            ↓

                   start_armboot()

                            ↓

                 init_sequence[]

                            ↓

                      getenv()

                            ↓

                     main_loop()

     其中前面4步为Stage1下面来详细分析一下  cpu/arm920t/start.S

    这里以ARM9 2410为例,2440移植时需要修改一些配置,具体的再后面的移植中介绍.

    /* 这段代码的主要作用:

    进入SVC模式

    关闭看门狗

    屏蔽所有IRG掩码

    设置时钟频率 FCLK HCLK PCLK

    清楚I/D Cache

    禁止MMU和CACHE

    配置memory control

    重定位:如果代码不在指定的地址上需要把uboot从当前位置copy到RAM指定位置上

    建立堆栈,为进入C函数做准备

    清0 .bss段

    跳入start_armboot函数进入stage2(lib_arm/board.c)*/

    /****************************************************/

    .globl _start
    _start: /* 系统复位位置, 各个异常向量对应的跳转代码 */
    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 /* 快速中断异常向量 */
    _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
    .balignl 16,0xdeadbeef


    /**ARM9支持7种异常。下面是异常的响应过程

    *第一个复位异常,它放在0x0的位置,一上电就执行它,而且我们的程序总是从复位异常处理程序

    *    开始执行的,因此复位异常处理程序不需要返回。

    *其他异常处理的如下:

    *当一个异常出现以后,ARM会自动执行以下几个步骤:

    *(1) 把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。

    *(2) 将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。

    *(3) 根据异常类型,强制设置CPSR的运行模式位。

    *(4) PC(程序计数器)被强制成相关异常向量处理函数地址,从而跳转到相应的异常处理程序中。

    * 当异常处理完毕后,ARM会执行以下几步操作从异常返回:

    *(1)将连接寄存器LR的值减去相应的偏移量后送到PC中

    *(2) 将SPSR复制回CPSR中

    *(3) 若在进入异常处理时设置了中断禁止位,要在此清除

    *

    * ARM规定了异常向量的地址:
    * b reset ; 复位 0x0

    * ldr pc, _undefined_instruction ; 未定义的指令异常 0x4

    * ldr pc, _software_interrupt ; 软件中断异常 0x8

    * ldr pc, _prefetch_abort ; 预取指令 0xc

    * ldr pc, _data_abort ; 数据 0x10

    * ldr pc, _not_used ; 未使用 0x14

    * ldr pc, _irq ; 慢速中断异常 0x18

    * ldr pc, _fiq ; 快速中断异常 0x1c
    * 当处理器碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到

    * 相应的处理程序,然后再返回到主程序继续执行。

    * .balignl 16,0xdeadbeef, 将地址对齐到16的倍数,如果地址寄存器的值(PC)跳过4个字节才是16的倍数,

    * 则使用0xdeadbeef填充这4个字节,如果它跳过1、2、3个字节,则填充值不确定。如果地址寄存器的值(PC)是16的倍数,则无需移动。********************/

    /*************************************************************************
    * Startup Code (reset vector) ………………….

    *************************************************************************/
    /* 保存变量的数据区 */
    _TEXT_BASE:
    .word TEXT_BASE   ;定义一个字并分配空间 4bytes

    .globl _armboot_start
    _armboot_start:
    .word _start  ;声明一个全局的,并用 _start 初始化它, 在u-boot.lds中定义

    /* These are defined in the board-specific linker script.*/
    .globl _bss_start
    _bss_start:
    .word __bss_start
    .globl _bss_end
    _bss_end:
    .word _end
    #ifdef CONFIG_USE_IRQ
    /* IRQ stack memory (calculated at run-time) */
    .globl IRQ_STACK_START
    IRQ_STACK_START:
    .word 0x0badc0de
    /* IRQ stack memory (calculated at run-time) */
    .globl FIQ_STACK_START
    FIQ_STACK_START:
    .word 0x0badc0de
    #endif
    /* the actual reset code*/
    /* 系统的复位代码。系统一上电,就跳到这里运行 */

    reset:
        mrs r0,cpsr          /* 取得当前程序状态寄存器cpsr到r0 */
        bic r0,r0,#0x1f    /* 这里使用位清除指令,把中断全部清除,只置位模式控制位为中断提供服务通常是 OS设备驱动程序的责任,

                                     因此在 Boot Loader 的执行全过程中可以不必响应任何中断*/
        orr r0,r0,#0xd3   /* 计算为超级保护模式,并disable IRQ和FIQ */
        msr cpsr,r0 /* 设置cpsr为超级保护模式 */
    /*****************

    *CPSR 的底8位为I     F        T        M4       M3       M2       M1       M0

              IRQdisable FIQdisable StateBit

    SVC[M4~M0] = 10011

    StateBit = set:THUMB state, others:ARM state

    * 设置cpu运行在SVC32模式。ARM9共有7种模式:
    * 用户模式(usr): arm处理器正常的程序执行状态
    * 快速中断模式(fiq): 用于高速数据传输或通道处理
    * 外部中断模式(irq): 用于通用的中断处理
    * 超级保护模式(svc): 操作系统使用的保护模式
    * 数据访问终止模式(abt): 当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护
    * 系统模式(sys): 运行具有特权的操作系统任务
    * 未定义指令中止模式(und): 当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
    * 通过设置ARM的CPSR寄存器,让CPU运行在操作系统保护模式,为后面进行其它操作作好准备了。

    *********************************************************/

    #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

         /* turn off the watchdog */

    //这段是关watchdog

    #if defined(CONFIG_S3C2400)

    #  define pWTCON   0x15300000

    #  define INTMSK   0x14400008    /* Interupt-Controller base addresses */

    #  define CLKDIVN  0x14800014    /* clock divisor register */

    #else

    #  define pWTCON   0x53000000

    #  define INTMSK   0x4A000008    /* Interupt-Controller base addresses */

    #  define INTSUBMSK    0x4A00001C

    #  define CLKDIVN  0x4C000014    /* clock divisor register */

    # endif

         ldr  r0, =pWTCON   //具体可以查看手册

         mov  r1, #0x0

         str  r1, [r0]

         /*

          * mask all IRQs by setting all bits in the INTMR - default

          */

         mov  r1, #0xffffffff //禁止所有中断

         ldr  r0, =INTMSK

         str  r1, [r0]

    # if defined(CONFIG_S3C2410)

         ldr  r1, =0x3ff

         ldr  r0, =INTSUBMSK

         str  r1, [r0]

    # endif

         /* FCLK:HCLK:PCLK = 1:2:4 */

         /* default FCLK is 120 MHz ! */

         ldr  r0, =CLKDIVN

         mov  r1, #3

         str  r1, [r0]

    #endif  /* defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)*/

    /*
    * we do sys-critical inits only at reboot,
    * not when booting from ram!
    */
    #ifndef CONFIG_SKIP_LOWLEVEL_INIT
           bl cpu_init_crit  ;cpu初始化,其中会调用lowlevel_init.S

    /******************************************************************************
    * BL为相对寻址,以程序计数器PC 的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址
    * ARM 指令集中的4条跳转指令可以完成从当前指令向前或向后的32MB 的地址空间的跳转,
    * 用的是相对寻址,它们是:B、BL、BLX、BX
    *******************************************************************************/

    #endif


    #ifndef CONFIG_SKIP_RELOCATE_UBOOT
    /* 重定位Boot代码到RAM内存,将Boot代码从FLASH移到RAM中 。因为flash中执行速度很慢,而且系统每次复位了都会在0x00000000处执行*/


    relocate: /* relocate U-Boot to RAM */
    adr r0, _start /* r0 <- current position of code */

    /**************************************************************************
    * 把_start的相对地址移到r0, 相对寻址以程序计数器PC 的当前值为基地址,
    * 指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
    * 它是与位置无关的,主要看Boot在哪里运行,也就是PC指针在哪里 (假设_start偏移量为0),
    * 例如这段代码在 0x05000000 (FLASH起始地址)运行,即此时PC=0x05000000,

    那么 adr r0, _start 得到 r0 = 0x05000000;
    * 如果在地址 0x33008000(Boot在RAM中加载地址)运行,即此时PC=0x33008000,那么r0就是 0x33008000 了。

    *通过adr指令得到当前代码的地址信息:如果U-boot是从RAM开始运行,则从adr,r0,_start得到的地址信息为*r0=_start=_TEXT_BASE=TEXT_BASE=0x3ff80000;如果U-boot从Flash开始运行,即从处理器对应的地址运 行,则*r0=0x0000,这时将会执行copy_loop标识的那段代码了
    **************************************************************************/
         ldr r1, _TEXT_BASE/* test if we run from flash or RAM */

    /* 把_TEXT_BASE地址处的值TEXT_BASE,也就是BOOT在RAM中运行地址移到r1 */
       cmp r0, r1 /* don't reloc during debug */

    /* 比较两个地址是否相同,如果相同,就已经在RAM运行,否则就是FLASH中运行 */

    beq stack_setup
    /* 如果是在FLASH中运行, 则把FLASH中的Boot代码移到RAM中,然后再运行 */
    ldr r2, _armboot_start /* 把_armboot_start地址处的值也就是_start绝对地址(也即在内存中的地址,这个绝对
    * 地址是在 link 的时候确定的,如0x81008000)移到r2 */
            ldr r3, _bss_start /* 把_bss_start地址处的值也就是__bss_start绝对地址(也即在内存中的地址,这个绝对
    * 地址是在 link 的时候确定的)移到r3 */
            sub r2, r3, r2 /* r2 <- size of armboot */ /* 计算引导代码大小并存到r2 */
            add r2, r0, r2 /* r2 <- source end address */ /* 计算引导代码最后相对地址并存入r2 */
    copy_loop:
            ldmia r0!, {r3-r10} /* copy from source address [r0] */ /* 从源地址[r0]读取32个字节到寄存器,并更新r0 */
            stmia r1!, {r3-r10} /* copy to target address [r1] */ /* 拷贝寄存器r3-r10的32个字节值保存到 [r1]指明的地址,并更新r1的值 */
            cmp r0, r2 /* until source end addreee [r2] */ /* 循环拷贝,直到把所有引导代码都移到内存 */
            ble copy_loop
    #endif /* CONFIG_SKIP_RELOCATE_UBOOT */

    /* Set up the stack */
    /* 在内存中建立起堆栈 */

    stack_setup:
            ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
            sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
            sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
    #ifdef CONFIG_USE_IRQ
            sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
    #endif
            sub sp, r0, #12 /* leave 3 words for abort-stack */

    /* 初始化内存中bss段中数据为0 */
    clear_bss:
            ldr r0, _bss_start /* find start of bss segment*/

     /* 把_bss_start地址处存储的绝对地址移到r0 */
    ldr r1, _bss_end /* stop here */ /* 把_bss_end地址处存储的绝对地址移到r1 */
    mov r2, #0x00000000 /* clear */
    clbss_l:
    str r2, [r0] /* clear loop... STR 指令用于从源寄存器中r2将一个32 位的字数据传送到存储器中[r0]*/
    add r0, r0, #4
    cmp r0, r1
    ble clbss_l /* 小于或等于跳转 */
    ldr pc, _start_armboot

    /***********************************************************
    * 已经准备好了堆栈,就可跳到C写的代码里了,也就是
    * 跳到内存中的/u-boot-1.1.6/board.c --> start_armboot中运行了
    * 把_start_armboot地址处的值也就是start_armboot绝对地址值移到pc
    * 神啊!终于跳到C代码了。
    ***********************************************************/
    _start_armboot:
    .word start_armboot
    /*************************************************************************
    *
    * CPU_init_critical registers
    *
    * setup important registers
    * setup memory timing
    *
    *************************************************************************/
    /**************************************************************************
    * 1、关闭 MMU和CPU 内部指令/数据 (I/D)cache。
    * 2、设置 CPU 的速度和时钟频率。
    * 3 、RAM 初始化。
    ****************************************************************************/
    cpu_init_crit:
    /* flush v4 I/D caches*/
    mov r0, #0
    mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
    /******************************************************************************************************
    * MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,格式为:
    * MCR 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
    * 其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,
    * 源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
    ******************************************************************************************************/
    mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
    / * disable MMU stuff and caches*/
    mrc p15, 0, r0, c1, c0, 0
    bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */
    bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */
    orr r0, r0, #0x00000002 /* set bit 2 (A) Align */
    orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
    mcr p15, 0, r0, c1, c0, 0
    / * Go setup Memory and board specific bits prior to relocation.*/
    mov ip, lr /* perserve link reg across call */
    bl lowlevel_init /* go setup pll,mux,memory */ /* 位于u-boot-1.1.6/board/xxx(开发板目录名称)/lowlevel_init.S */
    mov lr, ip /* restore link */
    mov pc, lr /* back to my caller */ /* 从cpu_init_crit子函数返回 */
    /*************************************************************************
    *
    * Interrupt handling
    *
    *************************************************************************/

    /*下面是中断相关的东西,这里略去*/

    3 ./board/my2410/lowlevel_init.S

    .globl lowlevel_init //读取下面标号为SMRDATA处的地址到R0中
        ldr r0, =SMRDATA
         /*读取上面标号为_TEXT_BASE处的地址内容到R1中
         *也就是取得TEXT_BASE的值到R1中*/
        ldr    r1, _TEXT_BASE
         /*计算SMRDATA的相对地址保存到R0中
         *SMRDATA为虚拟地址,而TEXT_BASE为虚拟地址的起始地址
         *而现在Uboot的起始地址并不为虚拟地址
         *TEXT_BASE为0x33F8 0000,SMRDATA为0x33F8 06C8
         *而现在程序运行在起始地址为0x0000 0000的地方
         *所以需要计算以0x0000 0000为标准的相对地址*/
        sub    r0, r0, r1
         //取得带宽与等待状态控制寄存器地址到R1中
        ldr    r1, =BWSCON    /* Bus Width Status Controller */
         //一共需要设置13个寄存器,每个寄存器4字节,详见芯片手册
        add r2, r0, #13*4
    0:
         //读取R0所指的项的值到R3中后R0自加4字节
        ldr r3, [r0], #4
         //将R3中的值保存到R1所指的地址中后R1自加4字节
        str r3, [r1], #4
         //比较R0和R2是否相等,相等则说明13个寄存器全部设置完毕
        cmp r2, r0
         //不等则跳转到上面标号为0处的地址继续执行
        bne 0b
         //跳回到返回地址中继续执行
        mov    pc, lr
        .ltorg
    /* the literal pools origin */
    SMRDATA:
        .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

    //设置每个BWSCON,注意BANK0由硬件连线决定了
        .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
        .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
        .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
        .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
        .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
        .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
       //设置BANKCON0~BANKCON5    

        .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
        .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))

    //设置BANKCON6~BANKCON7
        .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

    //设置REFRESH,在S3C2440中11~17位是保留的,也即(Tchr<<16)无意义
        .word 0x32

    //设置BANKSIZE,对于容量可以设置大写,多出来的空内存会被自动检测出来
        .word 0x30

    //设置MRSRB6
        .word 0x30

    //设置MRSRB7

  • 相关阅读:
    括号匹配的检验
    学习过程中遇到的比较有价值的博客--整理
    Spring+SpringMVC+MyBatis的pom.xml依赖
    Hibernate内容详解
    Struts2的拦截器配置
    Maven构建Struts2项目
    Mybatis增删改查,Demo整合
    简单Java类 全网最详细讲解 !!!
    Javaoop 遇到的问题
    Bootstrap 前端框架 遇到的问题 解决方案
  • 原文地址:https://www.cnblogs.com/80scd/p/5872373.html
Copyright © 2011-2022 走看看