这里说的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*/
<完>