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
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
_pad: .word 0x12345678
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
这句话的功能是申请一部分内存,具体的解释百度直接搜索.balignl 16,0xdeadbeef 这条指令可以得到详细的解释
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ // 这个宏没有定义,故不执行
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
reset:
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
#if (CONFIG_OMAP34XX) // 这个宏没有定义,下面的代码不会预编译
...
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)
bl cpy_clk_code @ put dpll adjust code behind vectors
#endif
#endif
#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
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
Set up the stack(内存规划)
设置的堆栈,规划内存的使用的
stack_setup:
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
* 这句话的功能是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
* 这句话是把地址继续减去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:
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_crit:
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
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
mov ip, lr @ persevere link reg across call
bl lowlevel_init @ go setup pll,mux,memory
cpu_init_crit:
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
mov r5, #0
ldr r8, =S5PC100_GPIO_BASE
ldr r0, =S5PC100_WATCHDOG_BASE @0xEA200000
orr r0, r0, #0x0
str r5, [r0]
#ifndef CONFIG_ONENAND_IPL
ldr r0, =S5PC100_SROMC_BASE
ldr r1, =0x9
str r1, [r0]
#endif
ldr r0, =S5PC100_VIC0_BASE @0xE4000000
ldr r1, =S5PC100_VIC1_BASE @0xE4000000
ldr r2, =S5PC100_VIC2_BASE @0xE4000000
mvn r3, #0x0
str r3, [r0, #0x14] @INTENCLEAR
str r3, [r1, #0x14] @INTENCLEAR
str r3, [r2, #0x14] @INTENCLEAR
#ifndef CONFIG_ONENAND_IPL
str r5, [r0, #0xc] @INTSELECT
str r5, [r1, #0xc] @INTSELECT
str r5, [r2, #0xc] @INTSELECT
str r5, [r0, #0xf00] @INTADDRESS
str r5, [r1, #0xf00] @INTADDRESS
str r5, [r2, #0xf00] @INTADDRESS
#endif
#ifndef CONFIG_ONENAND_IPL
bl uart_asm_init
bl tzpc_asm_init
#endif
#ifdef CONFIG_ONENAND_IPL
bl system_clock_init
bl mem_ctrl_asm_init
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
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行做了如下宏定义:
* /
__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 ();
}
}
/ * 这是一个函数的初始化的循环,用循环函数去执行一系列函数*/
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,CONFIG_SYS_MALLOC_LEN);
// 初始化malloc和env的内存
#ifndef CONFIG_SYS_NO_FLASH // 条件不成立,接下来的代码不执行.
display_flash_config (flash_init ());
#endif
#ifdef CONFIG_VFD //宏没有定义,接下来的代码不执行
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
vfd_setmem (addr);
gd->fb_base = addr;
#endif
#ifdef CONFIG_LCD // 宏没有定义,下面的代码不执行
if (!gd->fb_base) {
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
lcd_setmem (addr);
gd->fb_base = addr;
}
#endif
#if defined(CONFIG_CMD_NAND)
puts ("NAND: ");
nand_init();
#endif
#if defined(CONFIG_CMD_ONENAND) // 这个宏定义了,下面的代码能够执行
onenand_init();
#endif
#ifdef CONFIG_HAS_DATAFLASH // 这个宏没有定义
AT91F_DataflashInit();
dataflash_print_info();
#endif
env_relocate ();
#ifdef CONFIG_VFD // 宏没有定义,条件没有成立,下面的代码不能够执行
drv_vfd_init();
#endif
#ifdef CONFIG_SERIAL_MULTI // 宏定义了,条件成立,下面的代码能够执行
serial_initialize();
#endif
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
stdio_init ();
jumptable_init ();
#if defined(CONFIG_API) // 宏没有定义,条件不成立
api_init ();
#endif
console_init_r ();
#if defined(CONFIG_ARCH_MISC_INIT) // 条件不成立,下面的代码不执行
arch_misc_init ();
#endif
#if defined(CONFIG_MISC_INIT_R) // 条件不成立,下面的代码不成立
misc_init_r ();
#endif
enable_interrupts ();
#ifdef CONFIG_DRIVER_TI_EMAC // 宏没有定义,不执行
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
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96) // 宏没有定义,下面的代码不执行
if (getenv ("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif
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
for (;;) {
main_loop ();
}
}