系统移植之uboot源代码简要分析(1)

本次Linux系统移植是基于三星公司的S5PV210(又名Hummingbird)处理器的移植;

采用的BootLoader为三星官方的uboot:android_uboot_smdkv210.tar.bz2
开发平台为Ubuntu12.04
交叉编译工具为arm-linux-gcc4.4.3

注:s5pv210属于s5pc11x系列芯片,uboot中使用的是s5pc11x文件

重要文件文件目录列表:

  1. Makefile
  2. cpu/s5pc11x/
  3. board/samsung/smdkc110/
  4. lib_arm/
  5. include/configs/smdkv210single.h
  6. board/samsung/smdkc110/u-boot.lds mkconfig.h

第一阶段:尝试烧录原版uboot
配置步骤:
1.在uboot根目录下执行
make smdkv210single_config
这里写图片描述
2.完成第一步后,执行make all 命令进行全编译,编译成功截图如下:
系统移植之uboot源代码简要分析(1)_第1张图片
编译过程中可能会出现各种错误,这可能是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表示一个字)/

if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)

.word 0x2000    /* 第一个字指定BL1的大小,即CPU读0x2000(8K)大小的代码到IRAM */
.word 0x0       /* 保留为0 */
.word 0x0       /* 保留为0 */
.word 0x0       /* 校验和 */

endif


.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

if defined(CONFIG_ONENAND)

bl onenandcon_init

endif

if defined(CONFIG_NAND)

/* simple init for NAND   NAND初始化 */
bl nand_asm_init

endif

……

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 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 *//*从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:

if defined(CONFIG_ENABLE_MMU)

/启动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

/设置堆栈/
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启动阶段总结

  1. 关中断,设置为管理模式(SVC);
  2. 调用lowlevel_init初始化底层硬件(看门狗、系统时钟、串口、内存、nand等)
  3. 将uboot搬运到外部DRAM
  4. 开启MMU(至此,内存物理地址不能再访问,只能访问虚拟地址)
  5. 初始化堆栈,为调用C函数做准备
  6. 清空bss段
  7. 使用“ldr pc, _start_armboot”跳转到外部DRAM中运行BL2阶段代码。

`

你可能感兴趣的:(uboot,Linux)