1. UBoot启动流程可以分为两个阶段:
(1)第一阶段
① 硬件设备初始化
② 加载UBoot第二阶段代码到RAM空间
③ 设置好栈
④ 跳转到第二阶段代码入口
(2)第二阶段
① 初始化本阶段使用的硬件设备
② 检测系统内存映射
③ 将内核从Flash读取到RAM中
④ 为内核设置启动参数
⑤ 调用内核
2. UBoot启动第一阶段
根据链接器脚本cpu/arm920t/u-boot.lds中指定的链接方式,可以看出,u-boot代码段的第一个链接的是cpu/arm920t/start.o,入口标号是_start,因此u-boot的入口代码在对应源文件cpu/arm920t/start.S中
(1)设置异常向量表:
.globl _start _start: b start_code 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 .balignl 16,0xdeadbeef
(2)CPU进入SVC模式
/* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0
(3)关闭MMU和Cache
/* * flush v4 I/D caches */ mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /* * disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0
(4)关闭看门狗
/* *turn off the watchdog */ #define pWTCON 0x53000000 #define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ #define INTSUBMSK 0x4A00001C ldr r0, =pWTCON mov r1, #0x0 str r1, [r0]
(5)屏蔽中断
/* * mask all IRQs by setting all bits in the INTMR - default */ mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0] ldr r1, =0x7fff ldr r0, =INTSUBMSK str r1, [r0]
(6)设置MPLLCON、UPLLCON、CLKDIVN
#define CLKDIVN 0x4C000014 /* Clock divisor register */ #define CLK_CTL_BASE 0x4C000000 #define MDIV_405 (0x7f << 12) /* Default FCLK is 405 MHz! */ #define PSDIV_405 0x21 #define MDIV MDIV_405 #define PSDIV PSDIV_405 #define CLKDIV 0x05 /* FCLK:HCLK:PCLK = 1:4:8 */ ldr r0, =CLKDIVN mov r1, #CLKDIV str r1, [r0] /* Set arm920t to Asynchronous Clock Mode (405 MHz)*/ mrc p15, 0, r1, c1, c0, 0 orr r1, r1, #0xc0000000 mcr p15, 0, r1, c1, c0, 0 mov r1, #CLK_CTL_BASE mov r2, #MDIV add r2, r2, #PSDIV str r2, [r1, #0x04] /* MPLLCON */
(7)Nand初始化(Nand启动)
ldr r0, =S3C2440_NAND_BASE ldr r1, =0x001210 str r1, [r0, #NFCONF_OFFSET] mov r1, #0x03 str r1, [r0, #NFCONT_OFFSET] mov pc, lr
(8)存储控制器初始化(Memeory)
/* memory control configuration */ /* make r0 relative the current location so that it */ /* reads SMRDATA out of FLASH rather than memory ! */ ldr r0, =SMRDATA ldr r1, _TEXT_BASE sub r0, r0, r1 ldr r1, =BWSCON /* Bus Width Status Controller */ add r2, r0, #13*4 0: ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne 0b mov pc, lr .ltorg /* the literal pools origin */ SMRDATA: .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) .word 0x32 .word 0x30 .word 0x30
(9)复制u-boot第二阶段的代码到RAM
relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq stack_setup mov r2, #0 str r2, [r2] ldr r2, [r2] cmp r2, #0 bne copy_to_ram_from_nor mov sp, #0x1000 bl copy_to_ram_from_nand b stack_setup copy_to_ram_from_nor: ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ add r2, r0, r2 /* r2 <- source end address */ copy_loop: 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] */ ble copy_loop
(10)设置栈
stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
(11)清除BSS段
clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l
(12)跳转到第二阶段代码入口
ldr pc, _start_armboot
(13)UBoot内存分配图
3. UBoot启动第二阶段
(1)gd_t结构体:存储全局数据区的数据
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 #if 0 unsigned long cpu_clk; /* CPU clock in Hz! */ unsigned long bus_clk; phys_size_t ram_size; /* RAM size */ unsigned long reset_status; /* reset status register at boot */ #endif void **jt; /* jump table */ } gd_t;
① u-boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址(指针存放在寄存器r8中)
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
② 根据u-boot内存分布图,可以计算gd的值
gd = TEXT_BASE - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)
与start_armboot()函数中的计算方法一致
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
(2)bd_t结构:存放板级相关的全局数据
typedef struct bd_info { int bi_baudrate; /* serial console baudrate */ unsigned long bi_ip_addr; /* IP Address */ struct environment_s *bi_env; 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;
(3)init_sequence数组
① u-boot使用一个数组init_sequence来存储大多数开发板都要执行的初始化函数的函数指针
init_fnc_t *init_sequence[] = { board_init, /* basic board dependent setup */ timer_init, /* initialize timer */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ dram_init, /* configure available RAM banks */ display_dram_config, NULL, };
② board_init函数配置了MPLLCON、UPLLCON和部分GPIO寄存器的值,还设置了u-boot的机器码和内核启动参数地址
gd->bd->bi_arch_number = MACH_TYPE_MINI2440 ; gd->bd->bi_boot_params = 0x30000100;
③ dram_init函数
int dram_init (void) { gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0; }
(4)start_armboot()顺序分析
void start_armboot (void) { init_fnc_t **init_fnc_ptr; char *s; ... /* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)); /* 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); #ifndef CONFIG_SYS_NO_FLASH /* configure available FLASH banks */ display_flash_config (flash_init ()); #endif /* CONFIG_SYS_NO_FLASH */ ... #if defined(CONFIG_CMD_NAND) puts ("NAND: "); nand_init(); /* go init the NAND */ #endif ... /* initialize environment */ env_relocate (); ... /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); stdio_init (); /* get the devices list going. */ jumptable_init (); ... console_init_r (); /* fully init console as a device */ ... /* enable exceptions */ enable_interrupts (); ... #if defined(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 */ }
(5)main_loop函数分析
① 设置启动次数有关参数
② 启动Modem功能
③ 设置版本号、初始化命令自动完成功能
④ 启动延时和启动菜单
⑤ 执行命令循环