本文主要探讨x210的uboot的start.S文件,也是uboot启动的第一阶段。
头文件
config.h
config.h == x210_sd.h,由mkconfig脚本生成,包含了开发板的配置宏
- root@kaxi-virtual-machine:~/qt_x210v3s_160307/uboot/include# cat config.h
- /* Automatically generated - do not edit */
- #include <configs/x210_sd.h>
version.h
- #ifndef __VERSION_H__
- #define __VERSION_H__
-
- #ifndef DO_DEPS_ONLY
- #include "version_autogenerated.h"
- #endif
-
- #endif /* __VERSION_H__ */
- root@kaxi-virtual-machine:~/qt_x210v3s_160307/uboot/include# cat version_autogenerated.h
- #define U_BOOT_VERSION "U-Boot 1.3.4"
定义uboot版本号,uboot启动时打印的版本信息
asm/proc/domain.h
asm/proc/domain.h == include/asm-arm/proc-armv/domain.h
regs.h
regs.h == s5pc110.h
16字节头
- #if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
- .word 0x2000
- .word 0x0
- .word 0x0
- .word 0x0
- #endif
定义16字节头,用于存放校验信息
异常向量表构建
- .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 */
- /*
- * the actual reset code
- */
-
- reset:
- /*
- * set the cpu to SVC32 mode and IRQ & FIQ disable
- */
- @;mrs r0,cpsr
- @;bic r0,r0,#0x1f
- @;orr r0,r0,#0xd3
- @;msr cpsr,r0
- msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
将CPU设置为禁止FIQ和IRQ,置于ARM状态设置SVC模式
CPU复位默认进入SVC模式,uboot工作于SVC模式
.balignl 16,0xdeadbeef. 对齐访问效率快,是硬件要求
TEXT_BASE
- _TEXT_BASE:
- .word TEXT_BASE
- root@kaxi-virtual-machine:~/qt_x210v3s_160307/uboot/board/samsung/x210# cat config.mk
- TEXT_BASE = 0xc3e00000
- x210_nand_config : unconfig
- @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
- @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
定义uboot链接地址(0xc3e00000)
make x210_sd_config(调用mkconfig脚本) ==> uboot/board/samsung/x210/config.mk
CFG_PHY_UBOOT_BASE
- _TEXT_PHY_BASE:
- .word CFG_PHY_UBOOT_BASE
CFG_PHY_UBOOT_BASE为0x33e0000是uboot在DDR中的物理地址
cpu_init_crit(设置L2、L1cache、MMU)
bl disable_l2cache // 禁止L2 cache
bl set_l2cache_auxctrl_cycle // l2 cache相关初始化
bl enable_l2cache // 使能l2 cache
刷新L1 cache、icache、dcache。
关闭MMU
- cpu_init_crit:
-
- bl disable_l2cache
-
- bl set_l2cache_auxctrl_cycle
-
- bl enable_l2cache
-
- /*
- * 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
启动介质选择
- /* Read booting information */
- ldr r0, =PRO_ID_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, #0x6 @ 4KB 5-cycle 16-bit ECC
- moveq r3, #BOOT_NAND
-
- cmp r2, #0x8 @ OneNAND Mux
- moveq r3, #BOOT_ONENAND
-
- /* SD/MMC BOOT */
- cmp r2, #0xc
- moveq r3, #BOOT_MMCSD
-
- /* NOR BOOT */
- cmp r2, #0x14
- moveq r3, #BOOT_NOR
-
- /* Uart BOOTONG failed */
- cmp r2, #(0x1<<4)
- moveq r3, #BOOT_SEC_DEV
-
- ldr r0, =INF_REG_BASE
- str r3, [r0, #INF_REG3_OFFSET]
寄存器(0xE0000004)存储OM引脚接法,用于判断启动介质是Nand还是SD等
设置栈(SRAM栈)并调用lowlevel_init
- /*
- * Go setup Memory and board specific bits prior to relocation.
- */
-
- ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */
- sub sp, sp, #12 /* set stack */
- mov fp, #0
-
- bl lowlevel_init /* go setup pll,mux,memory */
设置栈(0xd0036000,第一次),当前代码在SRAM运行,DDR未初始化
lowlevel_init.S
uboot/board/samsumg/x210/lowlevel_init.S
push {lr}
由于lowlevel_init.S还调用了其他函数,防止之前的lr被覆盖(bl lowlevel_init处的地址),需先入栈
检查复位状态
- /* check reset status */
-
- ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
- ldr r1, [r0]
- bic r1, r1, #0xfff6ffff
- cmp r1, #0x10000
- beq wakeup_reset_pre
- cmp r1, #0x80000
- beq wakeup_reset_from_didle
CPU允许复位情况:冷上电、热启动、睡眠唤醒等
依据复位状态初始化DDR:冷上电DDR需初始化,热启动或低功耗不需要
IO状态恢复
- /* IO Retention release */
- ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
- ldr r1, [r0]
- ldr r2, =IO_RET_REL
- orr r1, r1, r2
- str r1, [r0]
关看门狗
- /* Disable Watchdog */
- ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
- mov r1, #0
- str r1, [r0]
SRAM,SROM的GPIO设置
- /* SRAM(2MB) init for SMDKC110 */
- /* GPJ1 SROM_ADDR_16to21 */
- ldr r0, =ELFIN_GPIO_BASE
-
- ldr r1, [r0, #GPJ1CON_OFFSET]
- bic r1, r1, #0xFFFFFF
- ldr r2, =0x444444
- orr r1, r1, r2
- str r1, [r0, #GPJ1CON_OFFSET]
-
- ldr r1, [r0, #GPJ1PUD_OFFSET]
- ldr r2, =0x3ff
- bic r1, r1, r2
- str r1, [r0, #GPJ1PUD_OFFSET]
-
- /* GPJ4 SROM_ADDR_16to21 */
- ldr r1, [r0, #GPJ4CON_OFFSET]
- bic r1, r1, #(0xf<<16)
- ldr r2, =(0x4<<16)
- orr r1, r1, r2
- str r1, [r0, #GPJ4CON_OFFSET]
-
- ldr r1, [r0, #GPJ4PUD_OFFSET]
- ldr r2, =(0x3<<8)
- bic r1, r1, r2
- str r1, [r0, #GPJ4PUD_OFFSET]
-
-
- /* CS0 - 16bit sram, enable nBE, Byte base address */
- ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
- mov r1, #0x1
- str r1, [r0]
供电锁存
- /* PS_HOLD pin(GPH0_0) set to high */
- ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
- ldr r1, [r0]
- orr r1, r1, #0x300
- orr r1, r1, #0x1
- str r1, [r0]
判断执行代码位置
- /* 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 1f /* r0 == r1 then skip sdram init */
对比运行地址和链接地址来判断在SRAM中(不相等)还是DDR中(相等),从而决定是否要时钟和DDR初始化
system_clock_init,x210_sd.h有时钟相关配置值
mem_ctrl_asm_init(uboot/cpu/s5pc11x/s5pc110/cpu_init.S)初始化DDR
uboot中DMC0的256MB内存地址范围为0x30000000-0x3FFFFFFF
uboot物理地址范围为:0x30000000-0x4FFFFFFF共512MB,30000000-3FFFFFFF为DMC0,40000000-4FFFFFFF为DMC1
uart_asm_init初始化串口,发送'O',
tzpc_init,trust zone初始化
通过串口打印'K'
设置栈(33E00000,DDR栈,二次)和重定位
- /* get ready to call C functions */
- ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
- sub sp, sp, #12
- mov fp, #0 /* no previous frame, so fp=0 */
-
- /* 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 flash copy */
-
- #if defined(CONFIG_EVT1)
- /* If BL1 was copied from SD/MMC CH2 */
- ldr r0, =0xD0037488
- ldr r1, [r0]
- ldr r2, =0xEB200000
- cmp r1, r2
- beq mmcsd_boot
- #endif
-
- ldr r0, =INF_REG_BASE
- ldr r1, [r0, #INF_REG3_OFFSET]
- cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
- beq nand_boot
- cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */
- beq onenand_boot
- cmp r1, #BOOT_MMCSD
- beq mmcsd_boot
- cmp r1, #BOOT_NOR
- beq nor_boot
- cmp r1, #BOOT_SEC_DEV
- beq mmcsd_boot
-
- nand_boot:
- mov r0, #0x1000
- bl copy_from_nand
- b after_copy
-
- onenand_boot:
- bl onenand_bl2_copy
- b after_copy
-
- mmcsd_boot:
- #if DELETE
- ldr sp, _TEXT_PHY_BASE
- sub sp, sp, #12
- mov fp, #0
- #endif
- bl movi_bl2_copy
- b after_copy
-
- nor_boot:
- bl read_hword
- b after_copy
冷启动uboot的16kb从SD卡加载到SRAM中运行,要加载第二部分(SD卡)到DDR中(33e00000)
D0037488内存地址(SRAM)是硬件设置,根据SD启动的通道修改值,EB000000为SD1启动,EB200000为SD2启动
start.S的260确定MMCSD启动,278将BOOT_MMCSD写入INF_REG3寄存器,322读去并和和BOOT_MMCSD对比确定从MMCSD启动,跳转到mmcsd_boot
uboot/cpu/s5pc11x/movi.c中调用movi_bl2_copy函数完成重定位
- copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
- CFG_PHY_UBOOT_BASE, 0);
2表示通道2
MOVI_BL2_POS第二部分在SD卡中的开始扇区,和烧录uboot烧录位置相同
MOVI_BL2_BLKCNT是uboot长度占用的扇区数
CFG_PHY_UBOOT_BASE是重定位将第二部分复制到DDR中的起始地址(33E00000)
虚拟地址映射
- after_copy:
-
- #if defined(CONFIG_ENABLE_MMU)
- enable_mmu:
- /* enable domain access */
- ldr r5, =0x0000ffff
- mcr p15, 0, r5, c3, c0, 0 @load domain access register
-
- /* Set the TTB register */
- ldr r0, _mmu_table_base
- ldr r1, =CFG_PHY_UBOOT_BASE
- ldr r2, =0xfff00000
- bic r0, r0, r2
- orr r1, r0, r1
- mcr p15, 0, r1, c2, c0, 0
-
- /* Enable the MMU */
- mmu_on:
- mrc p15, 0, r0, c1, c0, 0
- orr r0, r0, #1
- mcr p15, 0, r0, c1, c0, 0
- nop
- nop
- nop
- nop
- #endif
MMU实现虚拟地址到物理地址映射,在CP15协处理器中进行控制
cp15的c3寄存器是使能域访问,及控制访问权限
cp15的c2寄存器是设置TTB(转换表基地址)
转换表是虚拟地址映射的关键包含表索引和表项,表索引对应虚拟地址,表项对应物理地址,表索引和表项构成转换表单元,可对内存块进行虚拟地址转换(ARM中包含3种块大小:细表1KB、粗表4KB、段1MB),转换表由转换表单元构成,每个单元对应1个内存块,整体对应整个内存空间(0-4G)的映射
转换表在内存中,将基地址TTB设置到cp15的c2寄存器中,MMU工作时自动调用
cp15的c1寄存器使能MMU单元
再次设置栈(三次,DDR)
- skip_hw_init:
- /* Set up the stack */
- stack_setup:
- #if defined(CONFIG_MEMORY_UPPER_CODE)
- ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
- #else
- ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
- sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
- sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
- #if defined(CONFIG_USE_IRQ)
- sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
- #endif
- sub sp, r0, #12 /* leave 3 words for abort-stack */
-
- #endif
栈设置在uboot起始地址上方2MB处,安全栈空间是:2MB减去uboot大小
清理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
执行二阶段代码
- ldr pc, _start_armboot
-
- _start_armboot:
- .word start_armboot
-
- #if defined(CONFIG_ENABLE_MMU)
- _mmu_table_base:
- .word mmu_table
- #endif
start_armboot在uboot/lib_arm/board.c
uboot一阶段
构建异常向量表,启动介质选择,设置CPU为SVC模式,关看门狗,开发板供电置锁,时钟初始化,DDR初始化,串口初始化并打印"OK",重定位,建立映射表并开启MMU,清理bss,跳转到第二阶段