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当中,如下图:

这里写图片描述

这里写图片描述

这里写图片描述

手册中给出了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中的映射关系,

这里写图片描述

这里写图片描述

这里写图片描述

=> 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地址空间。

这里写图片描述

=> 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文件

<path> 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 <reset>
   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 <data_abort>:
  40:   ebfffffe    bl  40 <data_abort>
  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的起始地址,查找对应的宏定义。

<path> 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;而在第二阶段并不需要重复初始化,所以定义了这个宏。

<path> /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的说明,
将中断向量表映射到低地址。

这里写图片描述

这里写图片描述

这里写图片描述

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,然后跳转该地址。

<path> 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
<path> 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 ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值