这里说的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[32]; /* 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, 0, 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)() != 0) { 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 > 0) ? simple_strtoul((const char *)tmp, NULL, 10) : CONFIG_PRAM; addr -= (reg << 10); /* 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 -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug ("TLB table at: %08lx\n", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug ("Top of RAM usable for U-Boot at: %08lx\n", addr); #ifdef CONFIG_VFD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # 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 &= ~(4096 - 1); #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 >> 10, 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 >> 10, 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 -= 3; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* 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(0)); #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()) > 0) { # 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 (0, (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[6]; 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[6]; 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, 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_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(0)); #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[32]; #ifdef CONFIG_PRAM char *s; if ((s = getenv("pram")) != NULL) { pram = simple_strtoul(s, NULL, 10); } else { pram = CONFIG_PRAM; } #else pram=0; #endif #ifdef CONFIG_LOGBUFFER #ifndef CONFIG_ALT_LB_ADDR /* Also take the logbuffer into account (pram is in kB) */ pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/1024; #endif #endif sprintf((char *)memsz, "%ldk", (bd->bi_memsize / 1024) - 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(1); /* disable Control C checking */ /*关闭 ctrl + c 组合键*/ # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (p, 0); # 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*/
<完>