U-boot第一阶段汇编代码分析
一个可执行的 image 必须有一个入口点,并且只能有一个全局入口点,所以要通知编译器这个入口在哪里,入口点是通过有链接脚本来实现的,由此我们可以找到程序的入口点是在cpu/arm_cortexa8/u-boot.lds 中指定的,其中ENTRY(_start) 说明程序从_start 开始运行,而它指向的是cpu/arm_cortexa8/start.o 文件。
因为我们用的是 cortex-a8 的 cpu 架构,在CPU复位后从iROM地址0x00000000取它的第一条指令,执行iROM代码的功能是把flash中的前16K的代码加载到iRAM中,系统上电后将首先执行 u-boot 程序。
1.stage1:cpu/arm_cortexa8/start.S
2.当系统启动时, ARM CPU 会跳到 0x00000000去执行,一般 BootLoader 包括如下几个部分:
1. 建立异常向量表
2. 显示的切换到 SVC 且 32 指令模式
3. 设置异常向量表
4. 关闭 TLB,MMU,cache,刷新指令 cache 数据 cache
5. 关闭内部看门狗
6. 禁止所有的中断
7. 串口初始化
8. tzpc(TrustZone Protection Controller)
9. 配置系统时钟频率和总线频率
10. 设置内存区的控制寄存器
11. 设置堆栈
12. 跳到 C 代码部分执行
具体代码分析如下:
.globl _start
_start: b reset /*0x0,正常情况下,系统 reset 后进入的入口*/
ldr pc, _undefined_instruction /*0x4,未定义指令,系统出错处理的入口*/
ldr pc, _software_interrupt /* 0x8,软中断,monitor 程序的入口*/
ldr pc, _prefetch_abort /*0x0c,预取失败错误*/
ldr pc, _data_abort /*0x10,取数据失败错误(通常是保护现场,然后 do nothing)*/ ldr pc, _not_used /*0x14 保留*/
ldr pc, _irq /*0x18,快速中断请求 */
ldr pc, _fiq /*0x1c 快速中断 */
_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
_pad: .word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
这句话的功能是申请一部分内存,具体的解释百度直接搜索.balignl 16,0xdeadbeef 这条指令可以得到详细的解释
/*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
*
*************************************************************************/
_TEXT_BASE:
.word TEXT_BASE
/*TEXT_BASE这个标号的定义在如下文件中定义:
*390 F m TEXT_BASE board/samsung/smdkc100/config.mk
*在文件中的定义是:TEXT_BASE = 0x34800000
*/
.globl _armboot_start
_armboot_start:
.word _start
/* _start 是uboot的第一行代码的标号,代表的是第一行代码的地址*/
/*
These are defined in the board-specific linker script.在cpu/arm_cortexa8/u-boot.lds中定义
*/
.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:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
#if (CONFIG_OMAP34XX) // 这个宏没有定义,下面的代码不会预编译
/* Copy vectors to mask ROM indirect addr */
...
mov r3, #SRAM_OFFSET2
add r1, r1, r3
next:
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to target address [r1]
cmp r0, r2 @ until source end address [r2]
bne next @ loop until equal */
#if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT)
/* No need to copy/exec the clock code - DPLL adjust already done
* in NAND/oneNAND Boot.
*/
bl cpy_clk_code @ put dpll adjust code behind vectors
#endif /* NAND Boot */
#endif /* #if (CONFIG_OMAP34XX) * /
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT // 这个宏没有定义,条件成立,下面的代码需要执行
bl cpu_init_crit
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT // 这个宏没有定义,条件成立,下面的代码能够执行
relocate: @ relocate U-Boot to RAM
adr r0, _start @ r0 < - current position of code 装载_start的地址到r0中.
ldr r1, _TEXT_BASE @ 装载连接地址,这个地址是0x
/* _TEXT_BASE:
* .word TEXT_BASE
* TEXT_BASE这个标号的定义在如下文件中定义:
* 390 F m TEXT_BASE board/samsung/smdkc100/config.mk
* 在文件中的定义是:TEXT_BASE = 0x34800000
* 最后得出r1的值是0x34800000
*/
cmp r0, r1 @ don't reloc during debug
beq stack_setup
判断 当uboot在nand当中引导时,会把前16K的代码放到ram中,ram的地址和连接地址不一致, r0不等于r1的值,beq条件不成立. 当从usb引导是这个条件就成立.成立后后面的代码就不在执行了,后面的搬移代码就不在执行.
ldr r2, _armboot_start @功能是装载_start的地址
/ * .globl _armboot_start
* _armboot_start:
* .word _start
* /
ldr r3, _bss_start @ 功能是装载
/ * .globl _bss_start
* _bss_start:
* .word __bss_start
* __bss_start这个标号在cpu/arm_cortexa8/u-boot.lds 中定义,是bss段的开始也是bss段以前
* 的一个结束标志 因此r3的值是uboot的除去bss的末尾地址,在搬移的时候是不搬移bss
* 段的,bss段放的是未初始化的变量.
* /
sub r2, r3, r2 @ r2 < - size of armboot uboot的大小的偏移量
add r2, r0, r2 @ r2 < - source end address r0 是uboot的起始地址
copy_loop: @ copy 32 bytes at a time
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to target address [r1]
cmp r0, r2 @ until source end addreee [r2] 等到搬移完成后,r0和r2的值相等,
ble copy_loop @ 条件不成立,就向下执行代码
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
Set up the stack(内存规划)
/* Set up the stack */ 设置的堆栈,规划内存的使用的
stack_setup:
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
/*
*462 F d CONFIG_SYS_MALLOC_LEN include/configs/smdkc100.h
*中的的65行 #define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (1 << 20))
*环境变量大小在228行 #define CONFIG_ENV_SIZE (128 << 10) /* 128KiB, *0x20000 */
* 这句话的功能是r0 的值向低地址减去128K +1M的大小
*/
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo
/ * 438 F d CONFIG_SYS_GBL_DATA_SIZE include/configs/smdkc100.h
* #define CONFIG_SYS_GBL_DATA_SIZE 128 /* size in bytes for */
* 这句话是把地址继续减去128 bytes
* /
#ifdef CONFIG_USE_IRQ // 这个宏没有定义,下面的代码不会执行.
sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 @ leave 3 words for abort-stack
and sp, sp, #~7 @ 8 byte alinged for (ldr/str)d
/* Clear BSS (if any). 对bss段进行初始化 */
clear_bss:
ldr r0, _bss_start @ find start of bss segment
ldr r1, _bss_end @ stop here
mov r2, #0x00000000 @ clear value
clbss_l:
str r2, [r0] @ clear BSS location
cmp r0, r1 @ are we at the end yet
add r0, r0, #4 @ increment clear index pointer
bne clbss_l @ keep clearing till at end
ldr pc, _start_armboot @ 进入C代码
_start_armboot: .word start_armboot
/ * 会进入lib_arm/board.c文件中的 void start_armboot (void) * /
bl cpu_init_crit
/*************************************************************************
*
* CPU_init_critical registers 初始化关键的寄存器
*
* setup important registers
* setup memory timing
*
*************************************************************************/
cpu_init_crit:
/*
* 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
/*
* 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 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
/*
* 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.
*/
mov ip, lr @ persevere link reg across call
bl lowlevel_init @ go setup pll,mux,memory
/* lowlevel_init是底层初始化的代码,在下面的文件中定义
* 60 F l lowlevel_init board/samsung/smdkc100/lowlevel_init.S
* /
mov lr, ip @ restore link
mov pc, lr @ back to my caller
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
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.
*/
mov ip, lr @ persevere link reg across call
bl lowlevel_init @ go setup pll,mux,memory
/ *
*lowlevel_init 这个函数在的文件当中
*定义
* /
mov lr, ip @ restore link
mov pc, lr @ back to my caller
#endif
lowlevel_init
lowlevel_init的功能如下:
/ *
* 做一些层层硬件的初始化
*/
.globl lowlevel_init
lowlevel_init:
mov r9, lr
/* r5 has always zero */
mov r5, #0
ldr r8, =S5PC100_GPIO_BASE
/* Disable Watchdog */
ldr r0, =S5PC100_WATCHDOG_BASE @0xEA200000
orr r0, r0, #0x0
str r5, [r0]
#ifndef CONFIG_ONENAND_IPL
/* setting SRAM */
ldr r0, =S5PC100_SROMC_BASE
ldr r1, =0x9
str r1, [r0]
#endif
/* S5PC100 has 3 groups of interrupt sources */
ldr r0, =S5PC100_VIC0_BASE @0xE4000000
ldr r1, =S5PC100_VIC1_BASE @0xE4000000
ldr r2, =S5PC100_VIC2_BASE @0xE4000000
/* Disable all interrupts (VIC0, VIC1 and VIC2) */
mvn r3, #0x0
str r3, [r0, #0x14] @INTENCLEAR
str r3, [r1, #0x14] @INTENCLEAR
str r3, [r2, #0x14] @INTENCLEAR
#ifndef CONFIG_ONENAND_IPL
/* Set all interrupts as IRQ */
str r5, [r0, #0xc] @INTSELECT
str r5, [r1, #0xc] @INTSELECT
str r5, [r2, #0xc] @INTSELECT
/* Pending Interrupt Clear */
str r5, [r0, #0xf00] @INTADDRESS
str r5, [r1, #0xf00] @INTADDRESS
str r5, [r2, #0xf00] @INTADDRESS
#endif
#ifndef CONFIG_ONENAND_IPL
/* for UART */
bl uart_asm_init
/* for TZPC */
bl tzpc_asm_init
#endif
#ifdef CONFIG_ONENAND_IPL
/* init system clock */
bl system_clock_init
bl mem_ctrl_asm_init
/* Wakeup support. Don't know if it's going to be used, untested. */
ldr r0, =S5PC100_RST_STAT
ldr r1, [r0]
bic r1, r1, #0xfffffff7
cmp r1, #0x8
beq wakeup_reset
#endif
1:
mov lr, r9
mov pc, lr // 这句话的功能从bl lowlevel_init返回
U-boot第二阶段C代码分析
void start_armboot (void)
start_armboot的定义在lib_arm/board.c中定义的这个C函数中定义的这个函数的功能是去执行一系列的函数进行底层硬件的初始化,最要中的初始化是进行内存的初始化。
u-boot 之 gd_t 和 bd_t 数据结构简介
bd_t :这个结构体是board info 的缩写 用来保存板子的信息
gd_t :这个结构体是global data的缩写,用来保存全局数据的信息
bd_t 和 gd_t 是 u-boot 中两个重要的数据结构,在初始化操作很多都要靠这两个数据结构来保存或传递。
bd_t在 include/asm-arm/u-boot.h 中定义
gd_t 在 include/asm-arm/global_data.h中定义
bd_t :board info 数据结构定义,主要是用来保存板子参数。
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#if defined(CONFIG_VFD) || defined(CONFIG_LCD) // 这个条件不成立,下面的代码不执行
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/ * gd_t是一个包含了很多全局数据的结构体,gd是一个指向这个全局数据结构体的指针,该指针是通过DECLARE_GLOBAL_DATA_PTR来定义的。在文件在下面的文件中: gd_t include/asm-arm/global_data.h中的70行做了如下定义:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
指针gd存放在指定的寄存器r8中。这个声明可避免编译器把r8分配给其它的变量。
任何想要访问全局数据结构体的代码,代码开头加入DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。在使用gd指针前做了定义:lib_arm/board.c的64行做了如下宏定义:
* /
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
gd->flags |= GD_FLG_RELOC;
monitor_flash_len = _bss_start - _armboot_start;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/ * 这是一个函数的初始化的循环,用循环函数去执行一系列函数*/
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,CONFIG_SYS_MALLOC_LEN);
// 初始化malloc和env的内存
#ifndef CONFIG_SYS_NO_FLASH // 条件不成立,接下来的代码不执行.
/* 在include/configs/smdkc100.h中定义的宏为1 #define CONFIG_SYS_NO_FLASH 1
/* configure available FLASH banks */
display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */
#ifdef CONFIG_VFD //宏没有定义,接下来的代码不执行
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD // 宏没有定义,下面的代码不执行
/* board init may have inited fb_base */
if (!gd->fb_base) {
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
lcd_setmem (addr);
gd->fb_base = addr;
}
#endif /* CONFIG_LCD */
#if defined(CONFIG_CMD_NAND)
/* #undef CONFIG_CMD_NAND 这个宏没有定义,条件不成立,后面的代码不执行 */
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
#if defined(CONFIG_CMD_ONENAND) // 这个宏定义了,下面的代码能够执行
onenand_init();
#endif
#ifdef CONFIG_HAS_DATAFLASH // 这个宏没有定义
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* initialize environment */
env_relocate ();
#ifdef CONFIG_VFD // 宏没有定义,条件没有成立,下面的代码不能够执行
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
#ifdef CONFIG_SERIAL_MULTI // 宏定义了,条件成立,下面的代码能够执行
serial_initialize();
#endif
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
stdio_init (); /* get the devices list going. */
jumptable_init ();
#if defined(CONFIG_API) // 宏没有定义,条件不成立
/* Initialize API */
api_init ();
#endif
console_init_r (); /* fully init console as a device */
#if defined(CONFIG_ARCH_MISC_INIT) // 条件不成立,下面的代码不执行
/* miscellaneous arch dependent initialisations */
arch_misc_init ();
#endif
#if defined(CONFIG_MISC_INIT_R) // 条件不成立,下面的代码不成立
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions */
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC // 宏没有定义,不执行
/* XXX: this needs to be moved to board init */
extern void davinci_eth_set_mac_addr (const u_int8_t *addr);
if (getenv ("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
davinci_eth_set_mac_addr(enetaddr);
}
#endif
/* XXX: this needs to be moved to board init */
if (getenv ("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if defined(CONFIG_CMD_NET) // 宏没有定义,后面的代码不执行
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_LATE_INIT // 宏没有定义,代码不执行
board_late_init ();
#endif
#ifdef CONFIG_GENERIC_MMC // 宏没有定义,代码不执行
puts ("MMC: ");
mmc_initialize (gd->bd);
#endif
#ifdef CONFIG_BITBANGMII // 宏没有定义,代码不执行
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET) //#undef CONFIG_CMD_NET 故条件不成立
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}