最近手头有一块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文件
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的说明,
将中断向量表映射到低地址。
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 ...