这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程。这一过程可以分为两个过程,各个阶段的功能如下。
第一阶段的功能:
- 硬件设备初始化。
- 加载u-boot第二阶段代码到RAM空间。
- 设置好栈。
- 跳转到第二阶段代码入口。
第二阶段的功能:
- 初始化本阶段使用的硬件设备。
- 检查系统内存映射。
- 将内核从Flash读取到RAM中。
- 为内核设置启动参数。
- 调用内核。
CPU有7种模式
ARM中处理器模式
说明 | 备注 | |
---|---|---|
用户(usr) | 正常程序工作模式 | 此模式下程序不能够访问一些受操作系统保护的系统资源,应用程序也不能直接进行处理器模式的切换。 |
系统(sys) | 用于支持操作系统的特权任务等 | 与用户模式类似,但具有可以直接切换到其它模式等特权 |
快中断(fiq) | 支持高速数据传输及通道处理 | FIQ异常响应时进入此模式 |
中断(irq) | 用于通用中断处理 | IRQ异常响应时进入此模式 |
管理(svc) | 操作系统保护代码 | 系统复位和软件中断响应时进入此模式 |
中止(abt) | 用于支持虚拟内存和/或存储器保护 | 在ARM7TDMI没有大用处 |
未定义(und) | 支持硬件协处理器的软件仿真 | 未定义指令异常响应时进入此模式 |
u-boot启动第一阶段流程
根据连接器脚本 board/samsung/$(BOARD)/u-boot.lds中指定的链接方式,u-boot代码段第一个链接的是arch/arm/cpu/armv7/start.o
,入口是_start,因此u-boot的入口代码在对应的源文件 arch/arm/cpu/armv7/start.
S中。
下面分析start.S的执行
设置异常向量表
当一个异常或中断发生时,处理器会把pc指针设置为一个特定的存储器地址。这一地址放在一个被称为向量表(vector table)的特定地址范围内。
ARM异常向量表
地 址 | 异常类型 | 进入模式 | 说明 |
0x00000000 | 复位 | 管理模式 | 复位电平有效时产生 |
0x00000004 | 未定义指令 | 未定义指令模式 | 遇到ARM处理器无法识别的指令时产生 |
0x00000008 | 软件中断 | 管理模式 | SWI指令产生 |
0x0000000c | 预取指令 | 中止模式 | 当获取的指令不存在时产生 |
0x00000010 | 数据访问 | 中止模式 | 当获取的数据不存在时产生 |
0x00000014 | 保留 | 保留 | 保留 |
0x00000018 | IRQ | IRQ模式 | 中断请求有效,并且CRSR中的1位为0 |
0x0000001c | FIQ | FIQ模式 | 快读中断请求有效,并且CRSR中的F位为0 |
其中,复位异常向量的指令b reset决定u-boot启动后到teset处执行。
.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 /* now 16*4=64 */
CPU进入SVC模式(管理模式)
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
注:uboot作为一个bootloader来说,最终目的是为了启动Linux的kernel,在做好准备工作(即初始化硬件,准备好kernel和rootfs等)跳转到kernel之前,本身就要满足一些条件,其中一个条件,就是要求CPU处于SVC模式的。
(关于满足哪些条件,详情请参考:ARM Linux Kernel Boot Requirements http://www.arm.linux.org.uk/developer/booting.php
或者Linux内核文档: kernel_source_root\documentation\arm\booting
中也是同样的解释:“The CPU must be in SVC mode”)
所以,uboot在最初的初始化阶段,就将CPU设置为SVC模式,也是最合适的。
Copy vectors to mask ROM indirect addr(拷贝载体掩模ROM间接地址)
#if (CONFIG_OMAP34XX)
/* Copy vectors to mask ROM indirect addr */
adr r0, _start @ r0 <- current position of code
add r0, r0, #4 @ skip reset vector
mov r2, #64 @ r2 <- size to copy
add r2, r0, r2 @ r2 <- source end address
mov r1, #SRAM_OFFSET0 @ build vect addr
mov r3, #SRAM_OFFSET1
add r1, r1, r3
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.
*/
@这里不需要复制/执行时钟代码数字锁相环调整已经完成在Nand / onenand启动
bl cpy_clk_code @ put dpll adjust code behind vectors
#endif /* NAND Boot */
#endif
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
关闭MMU和Cache
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
cpu_init_crit: bl cache_init //空函数 直接返回 /*
* 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
mov lr, ip @ restore link
mov pc, lr @ back to my caller
板级初始化
.globl lowlevel_init
lowlevel_init: /* use iROM stack in bl2 */
ldr sp, =0x02060000 /*设置栈指针*/
push {lr} /* check reset status 检查系统状态 */
ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET)
ldr r1, [r0] /* Sleep wakeup reset */
ldr r2, =S5P_CHECK_SLEEP
cmp r1, r2
beq wakeup_reset /* set CP reset to low */
ldr r0, =0x11000C60
ldr r1, [r0]
ldr r2, =0xFFFFFF0F
and r1, r1, r2
orr r1, r1, #0x10
str r1, [r0]
ldr r0, =0x11000C68
ldr r1, [r0]
ldr r2, =0xFFFFFFF3
and r1, r1, r2
orr r1, r1, #0x4
str r1, [r0]
ldr r0, =0x11000C64
ldr r1, [r0]
ldr r2, =0xFFFFFFFD
and r1, r1, r2
str r1, [r0] /* led (GPM4_0~3) on */ /*点亮LED灯*/
ldr r0, =0x110002E0
ldr r1, =0x00001111
str r1, [r0]
ldr r1, =0x0e
str r1, [r0, #0x04] /* During sleep/wakeup or AFTR mode, pmic_init function is not available
* and it causes delays. So except for sleep/wakeup and AFTR mode,
* the below function is needed
*/
/*判断启动方式*/
#if defined(CONFIG_HAS_PMIC) //未定义
bl pmic_init
#endif #if defined(CONFIG_ONENAND) //未定义
bl onenandcon_init
#endif #if defined(NAND_BOOTING) //未定义
bl nand_asm_init
#endif bl read_om //读取启动设备
设置栈指针、检查系统状态、点亮LED灯。
判断启动位置
read_om: /*读取启动设备*/
/* Read booting information */
ldr r0, =S5PV310_POWER_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1 /* NAND BOOT */
@ cmp r2, #0x0 @ 512B 4-cycle
@ moveq r3, #BOOT_NAND @ cmp r2, #0x2 @ 2KB 5-cycle
@ moveq r3, #BOOT_NAND @ cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC
@ moveq r3, #BOOT_NAND cmp r2, #0xA
moveq r3, #BOOT_ONENAND cmp r2, #0x10 @ 2KB 5-cycle 16-bit ECC
moveq r3, #BOOT_NAND /*真正觉得启动方式的一个开关。而启动开关只会影响XOM中的值*/ /* SD/MMC BOOT */
cmp r2, #0x4
moveq r3, #BOOT_MMCSD /* eMMC BOOT */
cmp r2, #0x6
moveq r3, #BOOT_EMMC /* eMMC 4.4 BOOT */
cmp r2, #0x8
moveq r3, #BOOT_EMMC_4_4
cmp r2, #0x28
moveq r3, #BOOT_EMMC_4_4 ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET] /*将读取到的启动设备结果写入到INFREG3寄存器*/ mov pc, lr
将读取到的启动设备结果写入到INFREG3寄存器 。
判断程序是否运行在RAM中
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip sdram init and u-boot.bin loading */
会比较PC与0xc3e00000中间3位的值,如果相等则代表已经运行在SDRAM,就会跳过SDRAM的初始化。
初始化时钟、内存、串口初始化
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init /* init uart for debug */
bl uart_asm_init
至此,PLL、SDRAM、uart已全部初始化,启动时要用的最基本的硬件已经准备就绪。
下面是测试一段代码:
#if CONFIG_LL_DEBUG
mov r4, #0x4000
.L0:
sub r4, r4, #1
cmp r4, #0
bne .L0 mov r0, #'\r'
bl uart_asm_putc
mov r0, #'\n'
bl uart_asm_putc ldr r1, =0x40000000
ldr r2, =0x87654321
str r2, [r1]
str r2, [r1, #0x04]
str r2, [r1, #0x08]
ldr r2, =0x55aaaa55
str r2, [r1, #0x10]
nop mov r4, #0xC0000
.L1:
subs r4, r4, #1
bne .L1 ldr r0, [r1]
bl uart_asm_putx
mov r0, #'.'
bl uart_asm_putc ldr r0, [r1, #0x04]
bl uart_asm_putx
mov r0, #'.'
bl uart_asm_putc ldr r0, [r1, #0x08]
bl uart_asm_putx
mov r0, #'.'
bl uart_asm_putc ldr r0, [r1, #0x10]
bl uart_asm_putx
mov r0, #'>'
bl uart_asm_putc
#endif /* CONFIG_LL_DEBUG */ b 1f
v310_1:
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init 1:
bl tzpc_init
b load_uboot /*将u-boot的完整代码负责到SDRAM中*/
将u-boot拷贝到内存中运行
load_uboot:
ldr r0, =INF_REG_BASE
ldr r1, [r0, #INF_REG3_OFFSET]
cmp r1, #BOOT_NAND
beq nand_boot
cmp r1, #BOOT_ONENAND
beq onenand_boot
cmp r1, #BOOT_MMCSD
beq mmcsd_boot
cmp r1, #BOOT_EMMC
beq emmc_boot
cmp r1, #BOOT_EMMC_4_4
beq emmc_boot_4_4
cmp r1, #BOOT_NOR
beq nor_boot
cmp r1, #BOOT_SEC_DEV
beq mmcsd_boot
在判断启动位置那一节中已经获取到了启动类型并存储到 INF_REG3_OFFSET 寄存器中, 这里从获取该寄存器中获取启动类型后直接跳转到相应的节点。我这里是以SD卡启动的。
mmcsd_boot: #ifdef CONFIG_SMDKC220
//#ifdef CONFIG_CLK_BUS_DMC_200_400
ldr r0, =ELFIN_CLOCK_BASE
ldr r2, =CLK_DIV_FSYS2_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
//#endif
#else
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldr r0, =ELFIN_CLOCK_BASE
ldr r2, =CLK_DIV_FSYS2_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
#endif
#endif
bl movi_uboot_copy //拷贝u-boot到SDRAM
b after_copy
进入到第二阶段
after_copy: /* led (GPM4_0~3) on */
ldr r0, =0x110002E0
ldr r1, =0x0c
str r1, [r0, #0x04] #ifdef CONFIG_SMDKC220
/* set up C2C */
ldr r0, =S5PV310_SYSREG_BASE
ldr r2, =GENERAL_CTRL_C2C_OFFSET
ldr r1, [r0, r2]
ldr r3, =0x4000
orr r1, r1, r3
str r1, [r0, r2]
#endif #ifdef CONFIG_ENABLE_MMU
bl enable_mmu
#endif /* store second boot information in u-boot C level variable */
ldr r0, =CONFIG_PHY_UBOOT_BASE
sub r0, r0, #8
ldr r1, [r0]
ldr r0, _second_boot_info
str r1, [r0] /* Print 'K' */
ldr r0, =S5PV310_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET] ldr r0, _board_init_f
mov pc, r0 _board_init_f:
.word board_init_f _second_boot_info:
.word second_boot_info
取函数 _board_init_f 的地址。该函数在 arch/arm/lib/board.c中实现。
第一阶段结束。
u-boot启动第二阶段流程
board_init_f 在 arch/arm/lib/board.c 文件中定义。在分析board_init_f函数前先来介绍一些重要的数据结构。
gd_t结构体
u-boot使用一个结构体gd_t来存储全局数据区的数据。其定义的文件是arch\arm\include\asm\Global_data.h。
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
unsigned long sdhc_clk;
#endif
#ifdef CONFIG_AT91FAMILY
/* "static data" needed by at91's clock.c */
unsigned long cpu_clk_rate_hz;
unsigned long main_clk_rate_hz;
unsigned long mck_rate_hz;
unsigned long plla_rate_hz;
unsigned long pllb_rate_hz;
unsigned long at91_pllb_usb_init;
#endif
#ifdef CONFIG_ARM
/* "static data" needed by most of timer.c on ARM platforms */
unsigned long timer_rate_hz;
unsigned long tbl;
unsigned long tbu;
unsigned long long timer_reset_value;
unsigned long lastinc;
#endif
unsigned long relocaddr; /* Start address of U-Boot in RAM */
phys_size_t ram_size; /* RAM size */
unsigned long mon_len; /* monitor len */
unsigned long irq_sp; /* irq stack pointer */
unsigned long start_addr_sp; /* start_addr_stackpointer */
unsigned long reloc_off;
#if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
unsigned long tlb_addr;
#endif
void **jt; /* jump table */
char env_buf[]; /* buffer for getenv() before reloc. */
} gd_t;
u-boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址。
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
这个指针被放在指定的r8中,这个声明也避免编译器把r8分配给其他变量。
对于任意想访问全局数据区的代码,只要在其开头加入 DECLARE_GLOBAL_DATA_PTR 一行代码就可以了。
bd_t结构体
bd_t存放板级相关的全局数据,是gd_t中结构指针成员bd的结构体类型。在 arch/arm/include/u-boot.h 中定义如下:
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
u-boot启动内核时要给内核传递参数,这时需要使用gd_t、bd_t结构体中的信息来设置标记列表。
init_sequence数组
u-boot使用一个数组init_sequence来存储大多数开发板都要执行的初始化函数的函数指针。
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
#if defined(CONFIG_S5P6450) && !defined(CONFIG_S5P6460_IP_TEST)
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
#endif
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init,
#endif
NULL,
};
.
board_init_f()顺序分析
gd_t 数据结构空间分配、回调一组初始化函数、对gd_t数据结构进行初始化、relocate_code(UBOOT重定义代码,即自搬移)。
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp; /* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); /*得到全局数据结构的地址*/ /* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); memset((void*)gd, , sizeof (gd_t)); gd->mon_len = _bss_end_ofs; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != ) {
hang();
}
} debug ("monitor len: %08lX\n", gd->mon_len);
/*
* Ram is setup, size stored in gd !!
*/
debug ("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This should work
* for arch/ppc and arch/powerpc. Only Linux board ports in
* arch/powerpc with bootwrapper support, that recalculate the
* memory size from the SDRAM controller setup will have to
* get fixed.
*/
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; #ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr);
#endif
#endif #ifdef CONFIG_PRAM
/*
* reserve protected RAM
*/
i = getenv_r("pram", (char *)tmp, sizeof (tmp));
reg = (i > ) ? simple_strtoul((const char *)tmp, NULL, ) : CONFIG_PRAM;
addr -= (reg << ); /* size is in kB */
debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
/* reserve TLB table */
addr -= ( * ); /* round down to next 64 kB limit */
addr &= ~(0x10000 - ); gd->tlb_addr = addr;
debug ("TLB table at: %08lx\n", addr);
#endif /* round down to next 4 kB limit */
addr &= ~( - );
debug ("Top of RAM usable for U-Boot at: %08lx\n", addr); #ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE
# endif
/*
* reserve memory for VFD display (always full pages)
*/
addr -= vfd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */ #ifdef CONFIG_LCD
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */ /*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~( - ); #if defined(CONFIG_S5P) || defined(CONFIG_S5P6450)
addr = CONFIG_SYS_LOAD_ADDR;
#endif debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> , addr); #ifndef CONFIG_PRELOADER
/*
* reserve memory for malloc() arena
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
debug ("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> , addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug ("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp);
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
debug ("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp); /* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;
#ifdef CONFIG_USE_IRQ
addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
debug ("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif /* leave 3 words for abort-stack */
addr_sp -= ; /* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
#else
addr_sp += ; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif debug ("New Stack Pointer is: %08lx\n", addr_sp); #ifdef CONFIG_POST
post_bootmode_init();
post_run(NULL, POST_ROM | post_bootmode_get());
#endif gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */ gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug ("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof (gd_t)); relocate_code(addr_sp, id, addr);
/* NOTREACHED - relocate_code() does not return */
}
在relocate_code中会调用board_init_r()函数
board_init_f()顺序分析
/************************************************************************
*
* This is the next part if the initialization sequence: we are now
* running from RAM and have a "normal" C environment, i. e. global
* data can be written, BSS has been cleared, the stack size in not
* that critical any more, etc.
*
************************************************************************
*/ void board_init_r(gd_t *id, ulong dest_addr)
{
char *s;
bd_t *bd;
ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
ulong flash_size;
#endif gd = id;
bd = gd->bd; gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ monitor_flash_len = _bss_start_ofs;
debug ("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */ #ifdef CONFIG_SERIAL_MULTI
//serial_initialize();
#endif debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr); #ifdef CONFIG_LOGBUFFER
logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
post_output_backlog();
#endif /* The Malloc area is immediately below the monitor copy in DRAM */
malloc_start = dest_addr - TOTAL_MALLOC_LEN;
mem_malloc_init(malloc_start, TOTAL_MALLOC_LEN); #if !defined(CONFIG_SYS_NO_FLASH)
puts("FLASH:\t"); if ((flash_size = flash_init()) > ) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
s = getenv("flashchecksum");
if (s && (*s == 'y')) {
printf(" CRC: %08X",
crc32 (, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)
);
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#endif #if defined(CONFIG_CMD_NAND)
puts("NAND:\t");
nand_init(); /* go init the NAND */
#endif #if defined(CONFIG_CMD_ONENAND)
onenand_init();
#endif #ifdef CONFIG_GENERIC_MMC
mmc_initialize(bd);
#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 */ /* 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 /* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts(); /* Perform network card initialisation if necessary */
#if defined(CONFIG_DRIVER_SMC91111) || defined(CONFIG_DRIVER_LAN91C96)
/* XXX: this needs to be moved to board init */
if (getenv("ethaddr")) {
uchar enetaddr[];
eth_getenv_enetaddr("ethaddr", enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ #if defined(CONFIG_DRIVER_DM9000)
/* XXX: this needs to be moved to board init */
if (getenv("ethaddr")) {
uchar enetaddr[];
eth_getenv_enetaddr("ethaddr", enetaddr);
dm9000_set_mac_addr(enetaddr);
}
#endif /* Initialize from environment */
if ((s = getenv("loadaddr")) != NULL) {
load_addr = simple_strtoul(s, NULL, );
}
#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_BITBANGMII
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts("Net:\t");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif #ifdef CONFIG_POST
post_run(NULL, POST_RAM | post_bootmode_get());
#endif #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
/*
* Export available size of memory for Linux,
* taking into account the protected RAM at top of memory
*/
{
ulong pram;
uchar memsz[];
#ifdef CONFIG_PRAM
char *s; if ((s = getenv("pram")) != NULL) {
pram = simple_strtoul(s, NULL, );
} else {
pram = CONFIG_PRAM;
}
#else
pram=;
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* Also take the logbuffer into account (pram is in kB) */
pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/;
#endif
#endif
sprintf((char *)memsz, "%ldk", (bd->bi_memsize / ) - pram);
setenv("mem", (char *)memsz);
}
#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 */
}
完成的功能:使能Cache、 板子初始化、 串口初始化、 外存初始化、 环境变量初始化、 控制台初始哗、 中断使能、 以太网初始化、 进入main_loop(),等待命令或自动加载内核。
main_loop函数分析
main_loop函数在 common/main.c中定义,做的都是与平台无关的工作,主要是包含初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。
设置启动次数有关参数
在进入main_loop()函数后,首先根据配置加载已经保留的启动次数,并且根据配置判断是否超过启动次数,代码如下:
void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
#endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = 0;
unsigned long bootlimit = 0;
char *bcs;
char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
ulong bmp = 0; /* default bitmap */
extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT
if (do_mdm_init)
bmp = 1; /* alternate bitmap */
#endif
trab_vfd (bmp);
#endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMIT
bootcount = bootcount_load(); //加载保存的启动次数至变量bootcount
bootcount++;
bootcount_store (bootcount); //将启动次数加1后重新保存。
sprintf (bcs_set, "%lu", bootcount); //打印启动次数
setenv ("bootcount", bcs_set);
bcs = getenv ("bootlimit"); //读出启动次数现在变量
bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; //实现启动限制功能
#endif /* CONFIG_BOOTCOUNT_LIMIT */
启动Modern功能
如果系统有modern,打开此功能可以接受其他用户通过电话网络的拨号请求。
#ifdef CONFIG_MODEM_SUPPORT
debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
if (do_mdm_init) {
char *str = strdup(getenv("mdm_cmd"));
setenv ("preboot", str); /* set or delete definition */
if (str != NULL)
free (str);
mdm_init(); /* wait for modem connection */
}
#endif /* CONFIG_MODEM_SUPPORT */
设置版本号、初始化命令自动完成等功能
设置u-boot版本号、初始化命令自动化完成功能等。代码如下:
#ifdef CONFIG_VERSION_VARIABLE
{
extern char version_string[]; setenv ("ver", version_string); /* set version variable */ /*设置版本号*/
}
#endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start ();
#endif #if defined(CONFIG_HUSH_INIT_VAR)
hush_init_var ();
#endif #ifdef CONFIG_AUTO_COMPLETE
install_auto_complete(); /*初始化命令自动完成*/
#endif #ifdef CONFIG_PREBOOT
if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */ /*关闭 ctrl + c 组合键*/
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (p, );
# else
parse_string_outer(p, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */ /* 恢复 ctrl + c 组合键*/
# endif
}
#endif /* CONFIG_PREBOOT */
设置启动延时和启动菜单
进入主循环之前,如果配置了启动延时功能,需要等待用户从串口或网络接口输入。如果用户按下任意键打断启动流程,则向终端打印一个启动菜单。
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); # ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_POST
if (gd->flags & GD_FLG_POSTFAIL) {
s = getenv("failbootcmd");
}
else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT /* 检查是否超出启动次数限制 */
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd"); /* 获取启动命令参数 */ debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */ /* 关闭 ctrl + c 组合键 */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, 0); /* 运行启动命令 */
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */ /* 打开ctrl + c 组合键 */
# endif
} # ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s) {
# ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, 0);
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
}
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */
.
执行命令循环
/*
* Main Loop for Monitor Command Processing
*/
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= 0) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT); flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -2) {
/* -2 means timed out, retry autoboot
*/
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, 0, 0, NULL);
# else
return; /* retry autoboot */
# endif
}
#endif if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag); if (rc <= 0) {
/* invalid command or not repeatable, forget it */ /* 无效命令或者不可重复执行 */
lastcommand[0] = 0;
}
}
#endif /*CONFIG_SYS_HUSH_PARSER*/
<完>