TI-AM335x uboot分析

最近手头有一块AM335x的开发板,之前没有玩过TI A8的板子,所以想借由这块开发板了解下TI的芯片。在TI的官网上找到了这款开发板的相关资料和SDK开发套件,真的很赞。

首先来看芯片的启动部分:
一般的芯片启动流程都是Romcode –> BL1 –> BL2 –> Kernel –> User Image,
TI的芯片也不例外,Romcode –> ML0(SPL) –> uboot.img

整个boot阶段被分为三部分,
第一部分是芯片固化的Romcode,上电自动执行,一般支持flash,sd,uart,usb等启动方式,引导加载spl至片内ram运行;
第二部分是uboot spl,这里被称为ML0,是uboot的第一阶段,主要是初始化必要的硬件外设,关闭看门狗,关中断,配置时钟,初始化外部RAM,Flash控制器等。然后从对应启动方式中获取uboot.img,也就是uboot的第二阶段,加载到片外sdram中运行;
第三部分是uboot.img,主要是板级初始化,用来引导加载内核。

下面是详细介绍,
1.Romcode
芯片的Boot Rom,存放在总计176KB的ROM当中,如下图:

TI-AM335x uboot分析_第1张图片

这里写图片描述

TI-AM335x uboot分析_第2张图片

手册中给出了Romcode map图示,而且0x40020000的地址是可读的,我们可以利用uboot的md指令获取这部分内容,

=> md.w 0x40020000
40020000: 0232 ea00 f018 e59f f018 e59f f018 e59f    2...............
40020010: f018 e59f f018 e59f f018 e59f f018 e59f    ................
40020020: 3d86 8a37 ce04 4030 ce08 4030 ce0c 4030    .=7...0@..0@..0@
40020030: ce10 4030 ce14 4030 ce18 4030 ce1c 4030    ..0@..0@..0@..0@
40020040: 0000 0000 0000 0000 0000 0000 0000 0000    ................
40020050: 0000 0000 0000 0000 0000 0000 0000 0000    ................
40020060: 0000 0000 0000 0000 0000 0000 0000 0000    ................
40020070: 0000 0000 0000 0000 0000 0000 0000 0000    ................
=> md.w 0x40020080 0x50
40020080: fffe eaff fffe eaff fffe eaff fffe eaff    ................
40020090: fffe eaff fffe eaff fffe eaff fffe eaff    ................
400200a0: fffe eaff fffe eaff fffe eaff fffe eaff    ................
400200b0: fffe eaff fffe eaff fffe eaff fffe eaff    ................
400200c0: 102c e59f 1000 e591 1c07 e201 1421 e1a0    ,...........!...
400200d0: 0003 e351 0005 1a00 1018 e59f 2000 e591    ..Q.......... ..
400200e0: 2000 e581 1010 e59f 2001 e3a0 2000 e581    . ....... ... ..
400200f0: ff10 e12f 0040 44e1 0f08 44e0 0f00 44e0    ../.@..D...D...D
40020100: 4110 e59f 000c e3a0 2000 e794 2002 e3a0    .A....... ... ..
40020110: 2000 e784 002c e3a0 2000 e794 2002 e3a0    . ..,.... ... ..
=> md.b 0x4002bffc 4
4002bffc: 03 22 00 00 

从上图中我们可以看到vectors table,crc,dead loops,code,version等部分信息,
而下图中我们可以看到Romcode在SRAM中的映射关系,

TI-AM335x uboot分析_第3张图片

TI-AM335x uboot分析_第4张图片

这里写图片描述

=> md.w 0x40020000
40020000: 0232 ea00 f018 e59f f018 e59f f018 e59f    2...............
40020010: f018 e59f f018 e59f f018 e59f f018 e59f    ................
40020020: 3d86 8a37 ce04 4030 ce08 4030 ce0c 4030    .=7...0@..0@..0@
40020030: ce10 4030 ce14 4030 ce18 4030 ce1c 4030    ..0@..0@..0@..0@
40020040: 0000 0000 0000 0000 0000 0000 0000 0000    ................
40020050: 0000 0000 0000 0000 0000 0000 0000 0000    ................
40020060: 0000 0000 0000 0000 0000 0000 0000 0000    ................
40020070: 0000 0000 0000 0000 0000 0000 0000 0000    ................
=> 
40020080: fffe eaff fffe eaff fffe eaff fffe eaff    ................
40020090: fffe eaff fffe eaff fffe eaff fffe eaff    ................
400200a0: fffe eaff fffe eaff fffe eaff fffe eaff    ................
400200b0: fffe eaff fffe eaff fffe eaff fffe eaff    ................
400200c0: 102c e59f 1000 e591 1c07 e201 1421 e1a0    ,...........!...
400200d0: 0003 e351 0005 1a00 1018 e59f 2000 e591    ..Q.......... ..
400200e0: 2000 e581 1010 e59f 2001 e3a0 2000 e581    . ....... ... ..
400200f0: ff10 e12f 0040 44e1 0f08 44e0 0f00 44e0    ../.@..D...D...D
=> md.w 0x4030ce00
4030ce00: f018 e59f f018 e59f f018 e59f f018 e59f    ................
4030ce10: f018 e59f f018 e59f f018 e59f f018 e59f    ................
4030ce20: 0090 0002 0080 0002 0084 0002 0960 0002    ............`...
4030ce30: 096c 0002 0090 0002 0094 0002 0098 0002    l...............
4030ce40: 009f 0010 c000 0001 0000 0000 0000 0000    ................
4030ce50: 0000 0000 0000 0000 0023 0000 0000 0000    ........#.......
4030ce60: 0000 0000 0000 0000 1f9a 1c85 0e3d 4e52    ............=.RN
4030ce70: 4944 4553 4854 5245 454e 0054 32c0 0000    DISETHERNET..2..
=> 

读取对应地址的数据,同时看到0x20000地址的数据和0x40020000地址的数据完全一致,因为0x20000是Romcode的真实物理地址,猜测0x40020000应该是Romcode代码映射。同时根据Ram Exception Vectors表格中的说明,可以看出Ram Exception Vectors向量表中有重定向的操作,执行向量表中断后会跳转到dead loops地址空间。

TI-AM335x uboot分析_第5张图片

=> md.w 0x20000
00020000: 0232 ea00 f018 e59f f018 e59f f018 e59f    2...............
00020010: f018 e59f f018 e59f f018 e59f f018 e59f    ................
00020020: 3d86 8a37 ce04 4030 ce08 4030 ce0c 4030    .=7...0@..0@..0@
00020030: ce10 4030 ce14 4030 ce18 4030 ce1c 4030    ..0@..0@..0@..0@
00020040: 0000 0000 0000 0000 0000 0000 0000 0000    ................
00020050: 0000 0000 0000 0000 0000 0000 0000 0000    ................
00020060: 0000 0000 0000 0000 0000 0000 0000 0000    ................
00020070: 0000 0000 0000 0000 0000 0000 0000 0000    ................
=> 
00020080: fffe eaff fffe eaff fffe eaff fffe eaff    ................
00020090: fffe eaff fffe eaff fffe eaff fffe eaff    ................
000200a0: fffe eaff fffe eaff fffe eaff fffe eaff    ................
000200b0: fffe eaff fffe eaff fffe eaff fffe eaff    ................
000200c0: 102c e59f 1000 e591 1c07 e201 1421 e1a0    ,...........!...
000200d0: 0003 e351 0005 1a00 1018 e59f 2000 e591    ..Q.......... ..
000200e0: 2000 e581 1010 e59f 2001 e3a0 2000 e581    . ....... ... ..
000200f0: ff10 e12f 0040 44e1 0f08 44e0 0f00 44e0    ../.@..D...D...D
=> 

这里的代码主要根据system boot状态选择对应的启动方式,拷贝ML0代码至0x402F0400~0x4030B7FF(可容纳109KB),然后跳转执行代码。

=> md.w 0x402f0400
402f0400: 0016 ea00 f014 e59f f014 e59f f014 e59f    ................
402f0410: f014 e59f f014 e59f f014 e59f f014 e59f    ................
402f0420: 0440 402f 0440 402f 0440 402f 0440 402f    @./@@./@@./@@./@
402f0430: 0440 402f 0440 402f 0440 402f beef dead    @./@@./@@./@....
402f0440: fffe ebff f000 e320 f000 e320 f000 e320    ...... ... ... .
402f0450: f000 e320 f000 e320 f000 e320 f000 e320    .. ... ... ... .
402f0460: 0032 ea00 0000 e10f 101f e200 001a e331    2.............1.
402f0470: 001f 13c0 0013 1380 00c0 e380 f000 e129    ..............).

2.uboot spl部分

首先设置交叉编译工具链环境变量,

#vim ~/.bashrc
export PATH=/work/am3358/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin:$PATH

然后执行make编译

#make CROSS_COMPILE=arm-linux-gnueabihf- O=am335x_evm am335x_evm_config all

spl的配置选项是CONFIG_SPL_BUILD,查看对应的lds文件

 arch/arm/cpu/armv7/omap-common/u-boot-spl.lds

MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\
        LENGTH = CONFIG_SPL_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \
        LENGTH = CONFIG_SPL_BSS_MAX_SIZE }

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
    .text      :
    {
        __start = .;
        *(.vectors)
        arch/arm/cpu/armv7/start.o  (.text*)
        *(.text*)
    } >.sram

    . = ALIGN(4);
    .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram

    . = ALIGN(4);
    .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram

    . = ALIGN(4);
    .u_boot_list : {
        KEEP(*(SORT(.u_boot_list*)));
    } >.sram

    . = ALIGN(4);
    __image_copy_end = .;

    .end :
    {
        *(.__end)
    }

    .bss :
    {
        . = ALIGN(4);
        __bss_start = .;
        *(.bss*)
        . = ALIGN(4);
        __bss_end = .;
    } >.sdram
}

该链接脚本指定代码段,只读数据段,数据段和bss段存放的位置。可以看到真正的代码段是从.vectors段开始的,下面是中断向量表的定义。以_undefined_instruction为例,可以看到_undefined_instruction存放的是对应中断处理的入口地址undefined_instruction,而实际上undefined_instruction的处理方式就是死循环。

<path> /arch/arm/lib/vectors.S

_start:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
    .word   CONFIG_SYS_DV_NOR_BOOT_CFG  @0x11
#endif

    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

/*
 *************************************************************************
 *
 * Indirect vectors table
 *
 * Symbols referenced here must be defined somewhere else
 *
 *************************************************************************
 */

    .globl  _undefined_instruction
    .globl  _software_interrupt
    .globl  _prefetch_abort
    .globl  _data_abort
    .globl  _not_used
    .globl  _irq
    .globl  _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

/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */

/* SPL interrupt handling: just hang */

#ifdef CONFIG_SPL_BUILD

    .align  5
undefined_instruction:
software_interrupt:
prefetch_abort:
data_abort:
not_used:
irq:
fiq:

1:
    bl  1b          /* hang and never return */

根据链接脚本,我们可以找到向量表的代码,可以利用反汇编来对比代码

# arm-linux-gnueabihf-objdump -d vectors.o  > vectors.dump
# gedit vectors.dump &

vectors.o:     file format elf32-littlearm


Disassembly of section .vectors:

00000000 <_start>:
   0:   eafffffe    b   0 
   4:   e59ff014    ldr pc, [pc, #20]   ; 20 <_undefined_instruction>
   8:   e59ff014    ldr pc, [pc, #20]   ; 24 <_software_interrupt>
   c:   e59ff014    ldr pc, [pc, #20]   ; 28 <_prefetch_abort>
  10:   e59ff014    ldr pc, [pc, #20]   ; 2c <_data_abort>
  14:   e59ff014    ldr pc, [pc, #20]   ; 30 <_not_used>
  18:   e59ff014    ldr pc, [pc, #20]   ; 34 <_irq>
  1c:   e59ff014    ldr pc, [pc, #20]   ; 38 <_fiq>

00000020 <_undefined_instruction>:
  20:   00000040    .word   0x00000040

00000024 <_software_interrupt>:
  24:   00000040    .word   0x00000040

00000028 <_prefetch_abort>:
  28:   00000040    .word   0x00000040

0000002c <_data_abort>:
  2c:   00000040    .word   0x00000040

00000030 <_not_used>:
  30:   00000040    .word   0x00000040

00000034 <_irq>:
  34:   00000040    .word   0x00000040

00000038 <_fiq>:
  38:   00000040    .word   0x00000040
  3c:   deadbeef    .word   0xdeadbeef

00000040 :
  40:   ebfffffe    bl  40 
  44:   e320f000    nop {0}
  48:   e320f000    nop {0}
  4c:   e320f000    nop {0}
  50:   e320f000    nop {0}
  54:   e320f000    nop {0}
  58:   e320f000    nop {0}
  5c:   e320f000    nop {0}

链接脚本中sram,sdram的起始地址,查找对应的宏定义。

 include/configs/siemens-am33x-common.h

#define CONFIG_SPL_FRAMEWORK
#define CONFIG_SPL_TEXT_BASE        0x402F0400
#define CONFIG_SPL_MAX_SIZE     (101 * 1024)

#define CONFIG_SPL_BSS_START_ADDR   0x80000000
#define CONFIG_SPL_BSS_MAX_SIZE     0x80000     /* 512 KB */

查看u-boot-spl.map文件,找到对应__start字段位置,也看到指定的链接地址。

Linker script and memory map

.text           0x00000000402f0400     0xcdac
                0x00000000402f0400                __start = .
*(.vectors)
 .vectors       0x00000000402f0400       0x60 arch/arm/lib/built-in.o
                0x00000000402f0400                _start
                0x00000000402f0420                _undefined_instruction
                0x00000000402f0424                _software_interrupt
                0x00000000402f0428                _prefetch_abort
                0x00000000402f042c                _data_abort
                0x00000000402f0430                _not_used
                0x00000000402f0434                _irq
                0x00000000402f0438                _fiq
 arch/arm/cpu/armv7/start.o(.text)
 .text          0x00000000402f0460       0xb0 arch/arm/cpu/armv7/start.o
                0x00000000402f0460                reset
                0x00000000402f0464                save_boot_params_ret
                0x00000000402f04a0                c_runtime_cpu_setup
                0x00000000402f04b4                cpu_init_cp15
                0x00000000402f0508                cpu_init_crit

接下来就是start.o的部分,这里简单说明下CONFIG_SKIP_LOWLEVEL_INIT,这个宏在uboot第一阶段没有定义,所以会执行相应的lowlevel_init;而在第二阶段并不需要重复初始化,所以定义了这个宏。

 /arch/arm/cpu/armv7/start.S

reset:
    /* Allow the board to save important registers */
    b   save_boot_params
save_boot_params_ret:
    /*
     * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
     * except if in HYP mode already
     */
    mrs r0, cpsr
    and r1, r0, #0x1f       @ mask mode bits
    teq r1, #0x1a       @ test for HYP mode
    bicne   r0, r0, #0x1f       @ clear all mode bits
    orrne   r0, r0, #0x13       @ set SVC mode
    orr r0, r0, #0xc0       @ disable FIQ and IRQ
    msr cpsr,r0

/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
    /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
    mrc p15, 0, r0, c1, c0, 0   @ Read CP15 SCTLR Register
    bic r0, #CR_V       @ V = 0
    mcr p15, 0, r0, c1, c0, 0   @ Write CP15 SCTLR Register

    /* Set vector address in CP15 VBAR register */
    ldr r0, =_start
    mcr p15, 0, r0, c12, c0, 0  @Set VBAR
#endif

    /* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl  cpu_init_cp15
    bl  cpu_init_crit
#endif

    bl  _main

/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *  __attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don't save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
    b   save_boot_params_ret        @ back to my caller
ENDPROC(save_boot_params)
    .weak   save_boot_params

代码中涉及到CP15的部分,需要查看armv7手册,找到SCTLR和VBAR的说明,
将中断向量表映射到低地址。

TI-AM335x uboot分析_第6张图片

TI-AM335x uboot分析_第7张图片

TI-AM335x uboot分析_第8张图片

cpu_init_cp15 主要是对cp15协处理器进行设置,关闭MMU和TLB,具体可查询ARM手册中协处理器的章节描述。

ENTRY(cpu_init_cp15)
    /*  
     * Invalidate L1 I/D
     */
    mov r0, #0          @ set up for MCR
    mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs
    mcr p15, 0, r0, c7, c5, 0   @ invalidate icache
    mcr p15, 0, r0, c7, c5, 6   @ invalidate BP array
    mcr     p15, 0, r0, c7, c10, 4  @ DSB 
    mcr     p15, 0, r0, c7, c5, 4   @ ISB 

    /*  
     * disable MMU stuff and caches
     */
    mrc p15, 0, r0, c1, c0, 0
    bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
    bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
    orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
    orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
    bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
    orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
    mcr p15, 0, r0, c1, c0, 0
    mov pc, lr          @ back to my caller
ENDPROC(cpu_init_cp15)

cpu_init_crit 主要是平台或者板级的一些重要的初始化,核心代码时lowlevel_init,这里一般是板级实现,对于am335x来说,代码实现是在s_init

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,memory
ENDPROC(cpu_init_crit)
ENTRY(lowlevel_init)                                                                                                                                                            
    /*  
     * Setup a temporary stack
     */
    ldr sp, =CONFIG_SYS_INIT_SP_ADDR
    bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

    /*  
     * Save the old lr(passed in ip) and the current lr to stack
     */
    push    {ip, lr} 

    /*  
     * go setup pll, mux, memory
     */
    bl  s_init
    pop {ip, pc} 
ENDPROC(lowlevel_init)
void s_init(void)
{
    int in_sdram = is_running_in_sdram();

    watchdog_init();

    try_unlock_memory();

    /* Errata workarounds */
    omap3_setup_aux_cr();

#ifndef CONFIG_SYS_L2CACHE_OFF
    /* Invalidate L2-cache from secure mode */
    omap3_invalidate_l2_cache_secure();
#endif

    set_muxconf_regs();
    sdelay(100);

    prcm_init();

    per_clocks_enable();

#ifdef CONFIG_USB_EHCI_OMAP
    ehci_clocks_enable();
#endif

#ifdef CONFIG_SPL_BUILD
    gd = &gdata;

    preloader_console_init();

    timer_init();
#endif

    if (!in_sdram)
        mem_init();
}

上面的代码主要是设置模式,关闭中断和开门狗,关闭mmu和cache,设置定时器和sdram timing,然后跳转至_main,简单介绍下这部分功能(第二阶段涉及到中断向量表的重映射,这里暂不讨论):
(1)设置栈空间地址,预留global data存储区域,这部分暂时存放在片内ram中;
(2)board_init_f 准备第二阶段代码的执行硬件环境,保存片外ram的起始地址和大小。spl_relocate_stack_gd在片外ram中重新分配sp和gd;
(3)bss段清零
(4)调用board_init_r,复制uboot第二阶段代码到片外ram,然后跳转该地址。

 arch/arm/lib/crt0.S

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr sp, =(CONFIG_SPL_STACK)
#else
    ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
    mov r3, sp
    bic r3, r3, #7
    mov sp, r3
#else
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
#endif
    mov r0, sp
    bl  board_init_f_alloc_reserve
    mov sp, r0
    /* set up gd here, outside any C code */
    mov r9, r0
    bl  board_init_f_init_reserve

    mov r0, #0
    bl  board_init_f

#if ! defined(CONFIG_SPL_BUILD)

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated.
 */

    ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
    mov r3, sp
    bic r3, r3, #7
    mov sp, r3
#else
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
#endif
    ldr r9, [r9, #GD_BD]        /* r9 = gd->bd */
    sub r9, r9, #GD_SIZE        /* new GD is below bd */

    adr lr, here
    ldr r0, [r9, #GD_RELOC_OFF]     /* r0 = gd->reloc_off */
    add lr, lr, r0
#if defined(CONFIG_CPU_V7M)
    orr lr, #1              /* As required by Thumb-only */
#endif
    ldr r0, [r9, #GD_RELOCADDR]     /* r0 = gd->relocaddr */
    b   relocate_code
here:
/*
 * now relocate vectors
 */

    bl  relocate_vectors

/* Set up final (full) environment */

    bl  c_runtime_cpu_setup /* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD
    /* Use a DRAM stack for the rest of SPL, if requested */
    bl  spl_relocate_stack_gd
    cmp r0, #0
    movne   sp, r0
    movne   r9, r0
# endif
    ldr r0, =__bss_start    /* this is auto-relocated! */

#ifdef CONFIG_USE_ARCH_MEMSET
    ldr r3, =__bss_end      /* this is auto-relocated! */
    mov r1, #0x00000000     /* prepare zero to clear BSS */

    subs    r2, r3, r0      /* r2 = memset len */
    bl  memset
#else
    ldr r1, =__bss_end      /* this is auto-relocated! */
    mov r2, #0x00000000     /* prepare zero to clear BSS */

clbss_l:cmp r0, r1          /* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
    itt lo
#endif
    strlo   r2, [r0]        /* clear 32-bit BSS word */
    addlo   r0, r0, #4      /* move to next */
    blo clbss_l
#endif

#if ! defined(CONFIG_SPL_BUILD)
    bl coloured_LED_init
    bl red_led_on
#endif
    /* call board_init_r(gd_t *id, ulong dest_addr) */
    mov     r0, r9                  /* gd_t */
    ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
    /* call board_init_r */
#if defined(CONFIG_SYS_THUMB_BUILD)
    ldr lr, =board_init_r   /* this is auto-relocated! */
    bx  lr
#else
    ldr pc, =board_init_r   /* this is auto-relocated! */
#endif
    /* we should not return here. */
#endif

ENDPROC(_main)
#ifdef CONFIG_SPL_BUILD
void board_init_f(ulong dummy)
{     
    board_early_init_f();
    sdram_init();  
    /* dram_init must store complete ramsize in gd->ram_size */
    gd->ram_size = get_ram_size(
            (void *)CONFIG_SYS_SDRAM_BASE,
            CONFIG_MAX_RAM_BANK_SIZE);
}     
#endif

ulong spl_relocate_stack_gd(void)
{
#ifdef CONFIG_SPL_STACK_R
    gd_t *new_gd;
    ulong ptr = CONFIG_SPL_STACK_R_ADDR;

#ifdef CONFIG_SPL_SYS_MALLOC_SIMPLE
    if (CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN) {
        if (!(gd->flags & GD_FLG_SPL_INIT))
            panic_str("spl_init must be called before heap reloc");

        ptr -= CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN;
        gd->malloc_base = ptr;
        gd->malloc_limit = CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN;
        gd->malloc_ptr = 0;
    }   
#endif  
    /* Get stack position: use 8-byte alignment for ABI compliance */
    ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16);
    new_gd = (gd_t *)ptr;
    memcpy(new_gd, (void *)gd, sizeof(gd_t));
#if !defined(CONFIG_ARM) 
    gd = new_gd;
#endif 
    return ptr;
#else
    return 0;
#endif
 common/spl/spl.c 

void board_init_r(gd_t *dummy1, ulong dummy2)
{
    int i;

    debug(">>spl:board_init_r()\n");
    gd->bd = &bdata;

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
        defined(CONFIG_ARM)
    dram_init_banksize();
    reserve_mmu();
    enable_caches();
#endif

#if defined(CONFIG_SYS_SPL_MALLOC_START)
    mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
            CONFIG_SYS_SPL_MALLOC_SIZE);
    gd->flags |= GD_FLG_FULL_MALLOC_INIT;
#endif
    if (!(gd->flags & GD_FLG_SPL_INIT)) {
        if (spl_init())
            hang();
    }
#ifndef CONFIG_PPC
    /*
     * timer_init() does not exist on PPC systems. The timer is initialized
     * and enabled (decrementer) in interrupt_init() here.
     */
    timer_init();
#endif

#ifdef CONFIG_SPL_BOARD_INIT
    spl_board_init();
#endif

    board_boot_order(spl_boot_list);
    for (i = 0; i < ARRAY_SIZE(spl_boot_list) &&
            spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
        announce_boot_device(spl_boot_list[i]);
        if (!spl_load_image(spl_boot_list[i]))
            break;
    }

    if (i == ARRAY_SIZE(spl_boot_list) ||
        spl_boot_list[i] == BOOT_DEVICE_NONE) {
        puts("SPL: failed to boot from all boot devices\n");
        hang();
    }

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
        defined(CONFIG_ARM)
    cleanup_before_linux();
#endif

    switch (spl_image.os) {
    case IH_OS_U_BOOT:
        debug("Jumping to U-Boot\n");
        break;
#ifdef CONFIG_SPL_OS_BOOT
    case IH_OS_LINUX:
        debug("Jumping to Linux\n");
        spl_board_prepare_for_linux();
        jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
    default:
        debug("Unsupported OS image.. Jumping nevertheless..\n");
    }
#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
    debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
          gd->malloc_ptr / 1024);
#endif

    debug("loaded - jumping to U-Boot...");
    spl_board_prepare_for_boot();
    jump_to_image_no_args(&spl_image);
}

3.u-boot.img部分

首先来看u-boot.lds,一样的套路,我们继续看.vectors段

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
 . = 0x00000000;
 . = ALIGN(4);
 .text :
 {
  *(.__image_copy_start)
  *(.vectors)
  arch/arm/cpu/armv7/start.o (.text*)
  board/ti/am335x/built-in.o (.text*)
  *(.text*)
 }
 . = ALIGN(4);
 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
 . = ALIGN(4);
 .data : {
  *(.data*)
 }
 . = ALIGN(4);
 . = .;
 . = ALIGN(4);
 .u_boot_list : {
  KEEP(*(SORT(.u_boot_list*)));
 }
 . = ALIGN(4);
 .__efi_runtime_start : {
  *(.__efi_runtime_start)
 }
 .efi_runtime : {
  *(efi_runtime_text)
  *(efi_runtime_data)
 }
 .__efi_runtime_stop : {
  *(.__efi_runtime_stop)
 }
 .efi_runtime_rel_start :
 {
  *(.__efi_runtime_rel_start)
 }
 .efi_runtime_rel : {
  *(.relefi_runtime_text)
  *(.relefi_runtime_data)
 }
 .efi_runtime_rel_stop :
 {
  *(.__efi_runtime_rel_stop)
 }
 . = ALIGN(4);
 .image_copy_end :
 {
  *(.__image_copy_end)
 }
 .rel_dyn_start :
 {
  *(.__rel_dyn_start)
 }
 .rel.dyn : {
  *(.rel*)
 }
 .rel_dyn_end :
 {
  *(.__rel_dyn_end)
 }
 .hash : { *(.hash*) }
 .end :
 {
  *(.__end)
 }
 _image_binary_end = .;
 . = ALIGN(4096);
 .mmutable : {
  *(.mmutable)
 }
 .bss_start __rel_dyn_start (OVERLAY) : {
  KEEP(*(.__bss_start));
  __bss_base = .;
 }
 .bss __bss_base (OVERLAY) : {
  *(.bss*)
   . = ALIGN(4);
   __bss_limit = .;
 }
 .bss_end __bss_limit (OVERLAY) : {
  KEEP(*(.__bss_end));
 }
 .dynsym _image_binary_end : { *(.dynsym) }
 .dynbss : { *(.dynbss) }
 .dynstr : { *(.dynstr*) }
 .dynamic : { *(.dynamic*) }
 .gnu.hash : { *(.gnu.hash) }
 .plt : { *(.plt*) }
 .interp : { *(.interp*) }
 .gnu : { *(.gnu*) }
 .ARM.exidx : { *(.ARM.exidx*) }
}
.text           0x0000000080800000    0x41bf8
 *(.__image_copy_start)
 .__image_copy_start
                0x0000000080800000        0x0 arch/arm/lib/built-in.o
                0x0000000080800000                __image_copy_start
 *(.vectors)     
 .vectors       0x0000000080800000      0x300 arch/arm/lib/built-in.o
                0x0000000080800000                _start
                0x0000000080800020                _undefined_instruction
                0x0000000080800024                _software_interrupt
                0x0000000080800028                _prefetch_abort
                0x000000008080002c                _data_abort
                0x0000000080800030                _not_used
                0x0000000080800034                _irq
                0x0000000080800038                _fiq
                0x0000000080800040                IRQ_STACK_START_IN
 arch/arm/cpu/armv7/start.o(.text*)               
 .text          0x0000000080800300       0xa4 arch/arm/cpu/armv7/start.o
                0x0000000080800300                reset
                0x0000000080800304                save_boot_params_ret
                0x0000000080800338                c_runtime_cpu_setup

代码仍然是在/arch/arm/lib/vectors.S,不过这次的中断处理函数并不是死循环,而是换成了另外的代码实现,先保存进入对应模式前的堆栈和相关寄存器信息,然后跳转进入后续的处理,这里不在赘述。

在crt0.S通过调用board_init_r进入

void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    int i;
#endif

#ifdef CONFIG_AVR32
    mmu_init_r(dest_addr);
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
    gd = new_gd;
#endif

#ifdef CONFIG_NEEDS_MANUAL_RELOC
    for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
        init_sequence_r[i] += gd->reloc_off;
#endif

    if (initcall_run_list(init_sequence_r))
        hang();

    /* NOTREACHED - run_main_loop() does not return */
    hang();
}

这里的init_sequence_r是一个函数指针数组,遍历执行每一个函数,最后一个是run_main_loop,会调用main_loop函数进入uboot命令行主循环体。

static int run_main_loop(void)
{       
#ifdef CONFIG_SANDBOX
    sandbox_main_loop_init();
#endif  
    /* main_loop() can return to retry autoboot, if so just run it again */
    for (;;)                                                                                                                                        
        main_loop();
    return 0;   
}       
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{  
    const char *s;

    bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifndef CONFIG_SYS_GENERIC_BOARD
    puts("Warning: Your board does not use generic board. Please read\n");
    puts("doc/README.generic-board and take action. Boards not\n");
    puts("upgraded by the late 2014 may break or be removed.\n");
#endif

#ifdef CONFIG_VERSION_VARIABLE
    setenv("ver", version_string);  /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */

    cli_init();

    run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
    update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */

    s = bootdelay_process();
    if (cli_process_fdt(&s))
        cli_secure_boot_cmd(s);

    autoboot_command(s);

    cli_loop();
    panic("No CLI available");

uboot解析bootargs启动参数,根据设置加载内核进行启动

因为在实际操作过程中用到.dtb文件,不同于早期的uboot版本,所以后续需要了解device tree的书写格式,以及uboot和kernel是如何对dtb文件进行解析的。

# tftp 0x82000000 zImage
# tftp 0x88000000 am335x-evm.dtb
# bootz 0x82000000 - 0x88000000
Kernel image @ 0x82000000 [ 0x000000 - 0x34a108 ]

## Flattened Device Tree blob at 88000000
   Booting using the fdt blob at 0x88000000
   Loading Device Tree to 8fff3000, end 8ffff08b ... OK

Starting kernel ...

你可能感兴趣的:(Uboot,am335x)