本次Linux系统移植是基于三星公司的S5PV210(又名Hummingbird)处理器的移植;
采用的BootLoader为三星官方的uboot:android_uboot_smdkv210.tar.bz2
开发平台为Ubuntu12.04
交叉编译工具为arm-linux-gcc4.4.3
注:s5pv210属于s5pc11x系列芯片,uboot中使用的是s5pc11x文件
重要文件文件目录列表:
第一阶段:尝试烧录原版uboot
配置步骤:
1.在uboot根目录下执行
make smdkv210single_config
2.完成第一步后,执行make all 命令进行全编译,编译成功截图如下:
编译过程中可能会出现各种错误,这可能是Makefile中指定交叉编译链出错了,可以打开顶层Makefile进行修改。对于出现编译版本错误可尝试通过更改arm-Linux-gcc编译链的版本解决(亲测)。
3.烧录
对于三星厂方uboot,Samsung公司提供了非常方便的烧录脚本文件,可以直接进行SD卡(其他存储器件没试过)烧录,方法如下:
a)先将SD卡连接到虚拟机(不会的自己到CSDN博客上搜),然后“cd”进入sd_fushing/,可见里面有个sd_fusing.sh烧录脚本。不过,为了防止该脚本不匹配,我们先执行“make clean”清除编译信息,再执行“make”从新编译得到脚本文件
b)执行“sudo ./su_fushing /dev/sdb”将文件烧录到SD卡,
c)然后就可以将SD卡插进开发板尝试了,不过基本没戏,毕竟这是别人Samsung对于它自己的公版定制的uboot,和我们大多数开发板并不匹配,我们需要一步步自己更改。
第二阶段:uboot源代码分析
1.Makefile分析请移步U-boot主Makefile分析,这位大哥讲得非常详细具体(膜拜),
2 我们知道,ARM系列芯片上电后是从0x00000000地址的片内IRAM处开始执行代码,而有链接脚本board/samsung/smdkc110/u-boot.lds可知,代码段第一个链接文件是cpu/s5pc11x/start.o,start.o对应的源文件为cpu/s5pc11x/start.S,于是我们从start.S开始分析。
/*s5pv210处理器启动过程:
*上电后CPU执行内部0x00000000处的IROM位置代码(出厂固件),
*–>根据启动位置,从启动设备读取启动代码BL1到CPU内部IRAM,(动态内存DRAM 成本低于静态内存IRAM,故IRAM的size很小,不可能提供足够的空间给系统运行,系统需要运行在DRAM中)
–>执行BL1,(初始化外部DRAM…)–>将所有代码搬运到DRAM–>跳转到外部DRAM运行/
/s5pv210对BL1启动代码的格式做了规定,前16个字节有特殊含义(.word表示一个字)/
.word 0x2000 /* 第一个字指定BL1的大小,即CPU读0x2000(8K)大小的代码到IRAM */
.word 0x0 /* 保留为0 */
.word 0x0 /* 保留为0 */
.word 0x0 /* 校验和 */
.globl _start
_start: breset/* 1.跳转到reset */et位置处代码如下,可知其作用为将处理器设置为管理模式,并关闭所reset:
/*将处理器设置为管理模式,关闭中断
* set the cpu to SVC32 mode and IRQ & FIQ disable
*/
@;mrsr0,cpsr@ 注释
@;bicr0,r0,#0x1f
@;orrr0,r0,#0xd3
@;msrcpsr,r0
msrcpsr_c, #0xd3@ I & F disable, Mode: 0x13 - SVC
接下来是设置CPU重要寄存器和存储时序,完成CPU的设置后,是执行底层硬件的初始化lowlevel_init,
bllowlevel_init/* go setup pll,mux,memory 2. 跳转到lowlevel_init 进行初始化 */
/*lowlevel_init.S的路径为board/samsung/smdkc110/lowlevel_init.S*此文件做了非常重要的底层硬件初始化,包括检查复位状态、关闭看门狗、初始化SDRAM、初始化电源管理芯片PMIC(若开发板没有使用PMIC,可将该代码段注释掉)。*/
/* 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
/* Disable Watchdog *//*关闭看门狗*/
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #0
str r1, [r0]
/* SRAM(2MB) init for SMDKC110 初始化 SRAM*/
/* GPJ1 SROM_ADDR_16to21 */
ldr r0, =ELFIN_GPIO_BASE
/* init PMIC chip *//* 电源管理芯片初始化,
*GEC210开发板没有使用到PMIC,可将此段代码注释掉
*/
bl PMIC_InitIp
/* init system clock 初始化系统时钟 */
bl system_clock_init
/* Memory initialize 外部DRAM初始化 */
bl mem_ctrl_asm_init
1:
/* for UART 串口初始化 */
bl uart_asm_init
bl tzpc_init
bl onenandcon_init
/* simple init for NAND NAND初始化 */
bl nand_asm_init
……
pop {pc}
/最后,将文件一开始入栈的ld寄存器保持的地址出栈赋值给程序计数器pc,以返回到start.s中的lowlevel_init,/
至此已完成了底层硬件的初始化,可将代码拷贝到外部DRAM上运行了。
----------
回到cpu/s5pc11x/start.S文件,
1.设置堆栈,为调用C函数做准备
/* To hold max8698 output before releasing power on switch,
* set PS_HOLD signal to high
*/
ldr r0, =0xE010E81C /* PS_HOLD_CONTROL register */
ldr r1, =0x00005301 /* PS_HOLD output high */
str r1, [r0]
/* 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 将当前代码基地址0xff000fff赋值给r0 */
ldr r2, _TEXT_BASE /* r2 <- original base addr in ram 将RAM中原有的基地址赋值到r2 */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 r1和r2的值如果相等,
表示已经完成过uboot的复制到外部DRAM的操作,若不相等,执行复制 */
beq after_copy /* r0 == r1 then skip flash copy */
/* 选择系统启动方式(从哪里加载) */
/* If BL1 was copied from SD/MMC CH2 */
ldr r0, =0xD0037488
ldr r1, [r0]
ldr r2, =0xEB200000
cmp r1, r2
beq mmcsd_boot
ldr r0, =INF_REG_BASE
ldr r1, [r0, #INF_REG3_OFFSET]
cmp r1, #BOOT_NAND /* 0x0 => boot device is nand *//*从NAND启动 */
beq nand_boot
cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand *//*从ONENAND启动 */
beq onenand_boot
cmp r1, #BOOT_MMCSD /*从MMCSD启动 */
beq mmcsd_boot
cmp r1, #BOOT_NOR /*从NOR启动 */
beq nor_boot
cmp r1, #BOOT_SEC_DEV /*从SEC_DEV启动 */
beq mmcsd_boot
----------
我们以从nand_flash启动为例进行分析:
nand_boot:
mov r0, #0x1000
bl copy_from_nand /调用copy_flam_nand函数将uboot/
b after_copy
----------
根据跳转,我们查看copy_from_nand
.globl copy_from_n
copy_from_nand:
push{lr} /* save return address */
mov r9, r0
mov r9, #0x100 /* Compare about 8KB */
bl copy_uboot_to_ram /再调用copy_uboot_to_ram函数将uboot拷贝到RAM中,(copy_uboot_to_ram代码不再累赘)/
tst r0, #0x0
bne copy_failed
----------
成功执行copy_from_nand后,返回“nand_boot:”代码段继续执行“b after_copy”以跳转到“after_copy”代码段,完成:启动MMU、设置堆栈、清除bss段
after_copy:
/启动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
/设置堆栈/
skip_hw_init:
/* Set up the stack */
stack_setup:
……
/清除bss段/
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
----------
之后,使用绝对跳转指令ldr使得程序跳转到_start_armboot(即BL2)处执行。
ldr pc, _start_armboot
“`
至此BL1启动完成,内部SRAM工作完成,为程序在外部DRAM执行设置好了环境
uboot的BL1启动阶段总结
`