第一阶段:主要是SOC内部的初始化,板级的初始化比较少,所以移植的修改量比较小。此阶段由汇编语言编写,代码主体分布在start.S和lowlevel_init.S中。
其中start.S作为主干,其主要流程为:
注:加粗的比较重要,和板级有点关系
1. 填充16字节的校验位
2. 设置异常向量表
3. 设置cpu为SVC模式
4. 禁用cach和mmu
5. 判断启动介质
6. 在SRAM中设置栈
7. 跳入 lowlevel_init.S
关看门狗
设置供电锁存
判断当前执行位置
初始化DDR
初始化串口(打印‘OK’)
8. 再次供电锁存
9. 在DDR中设置栈
10. 通过引脚判断启动介质
11. 跳到movi.c
重定位
12. 跳入 lowlevel_init.S设置虚拟地址映射
13. 在DDR中合理的地方设置栈
14. 清bss段
15. 跳入board.c,到DDR中执行程序
第二阶段:主要是板级的初始化,SOC内部的初始化比较小,移植的修改量主要在此。此阶段由c语言编写,代码主体分布在board.c中
执行完初始化的两个阶段后就进入了uboot的命令行循环部分
个人感觉U-boot的初始化相对于其他部分来说,是真正和板级相关的,所以对于移植来讲是比较重要的,毕竟上层的逻辑代码都是不用修改的…..
下面是简要的代码流程节选分析
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif
.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
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_init_crit:
一大堆无用的代码,就不贴了
bl disable_l2cache
bl set_l2cache_auxctrl_cycle
bl enable_l2cache
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
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
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */
sub sp, sp, #12 /* set stack */
mov fp, #0
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]
/* 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]
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE
bic r2, r2, r0
cmp r1, r2
beq 1f
bl system_clock_init
/* Memory initialize */
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 */
bl nand_asm_init
#endif
/* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
pop {pc}
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 */
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 BL1 was copied from SD/MMC CH2 */
ldr r0, =0xD0037488
ldr r1, [r0]
ldr r2, =0xEB200000
cmp r1, r2
beq mmcsd_boot
bl movi_bl2_copy
b after_copy
虽然movinand是mmc的一种,但是个人认为三星把文件名字取成movi.c还是不太妥当…
typedef u32(*copy_sd_mmc_to_mem)
(u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);
void movi_bl2_copy(void)
{
ulong ch;
#if defined(CONFIG_EVT1)
ch = *(volatile u32 *)(0xD0037488);
copy_sd_mmc_to_mem copy_bl2 =
(copy_sd_mmc_to_mem) (*(u32 *) (0xD0037F98));
u32 ret;
if (ch == 0xEB000000) {
ret = copy_bl2(0, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
CFG_PHY_UBOOT_BASE, 0);
#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
详细的建表步骤(在lowlevel_init中)
#ifdef CONFIG_MCP_SINGLE /*这里开始设置虚拟映射转换表,这里使用的是段式映射模式,即以段(1MB)为单位进行映射*/
/*因此表中的一个单元只能管1MB,整个4G内存需要创建4096个表单元,所以后面采用了循环的方法来创建*/
/*查表的方法是:例如虚拟地址的高12位是x,则去查第x个表单元,该表单元的高12位就是物理地址的高12位,其实这一部分只要知道一些大概的原理即可,不必深究,等用到了再去看*/
/* form a first-level section entry */
.macro FL_SECTION_ENTRY base,ap,d,c,b /*.macro指令是汇编中宏定义的意思,此带参宏将FL_SECTION_ENTRY base,ap,d,c,b定义成一个word大小的特定值*/
.word (\base << 20) | (\ap << 10) | \ /*这个特定值就是转换表的填充量,其中,参数base是映射出来的段地址的基地址,从第20位开始。*/
(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)/*20位的大小正好是1MB,由此可知转换表中未保留前20位,映射出来的地址之间间隔为1MB*/
/*故这里采用的是段式映射;继续来分析参数,ap是访问控制位,从第10位开始。d、c、b都是一些权限位*/
.endm /*.endm,即结束宏定义*/
.section .mmudata, "a"
.align 14
// the following alignment creates the mmu table at address 0x4000.
.globl mmu_table
mmu_table:
.set __base,0 /*设置变量__base值为0*/
// Access for iRAM
.rept 0x100 /*.rept 0x100相当于for循环,一共循环0x100次,所以这一块代码创建了0x100(256)个转换表单元*/
FL_SECTION_ENTRY __base,3,0,0,0 /*利用刚才定义的带参宏创建转换表的内容,变量__base和3,0,0,0作为参数*/
.set __base,__base+1 /*相当于base=base+1*/
.endr /*.endr对应.rept,表示循环体的结束*/
// Not Allowed
.rept 0x200 - 0x100 /*把.rept重设为0x100,这里写成0x200 - 0x100是为了好理解,表示这里要填充第0x200到0x100的表单元*/
.word 0x00000000 /*把全0填充进了这256个转换表单元.....*/
.endr
.set __base,0x200 /*这里开始填充第0x600 - 0x200的表单元,原理和前0x100个一模一样*/
// should be accessed
.rept 0x600 - 0x200
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
.rept 0x800 - 0x600 /*把0x800 - 0x600的表单元填充全0,即禁止使用这些内存空间*/
.word 0x00000000
.endr
.set __base,0x800 /*后面的填充方法都和前面一样*/
// should be accessed
.rept 0xb00 - 0x800
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
由此可见整张转换表的设定
输入虚拟地址 | 输出的物理地址 | 长度 |
---|---|---|
0-10000000 | 0-10000000 | 256MB |
20000000-60000000 | 20000000-60000000 | 1GB |
60000000-80000000 | 0 | 512MB |
80000000-b0000000 | 80000000-b0000000 | 768MB |
b0000000-c0000000 | b0000000-c0000000 | 256MB |
c0000000-d0000000 | 30000000-40000000 | 256MB |
d-完 | d-完 | 768MB |
此表仅仅将c0000000开头的256MB映射到了DMC0的30000000开头的256MB物理内存上去了
其他的虚拟地址空间根本没动,还是原样映射的。所以uboot的链接地址(c3e开头的)其实是33e开头的地址
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
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
typedef int (init_fnc_t) (void);
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
reloc_init, /* Set the relocation done flag, must do this AFTER cpu_init(), but as soon as possible */
#endif
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
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 */
#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)
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
ulong gd_base;
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
gd = (gd_t*)gd_base;
typedef struct global_data {
bd_t *bd;//bd是一个指向bd_t类型链表的指针,bd_t链表的内容是和板级有关的全局变量
unsigned long flags;//某个标志位
unsigned long baudrate;//串口波特率
unsigned long have_console; /* serial_init() was called */ //标志位,表示是否使用了控制台的
unsigned long reloc_off; /* Relocation Offset *///重定位有关的偏移量
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
#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;
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif
#if defined(CONFIG_X210)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
env_relocate ();
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* MAC Address */ //网卡的MAC(以太网)地址
{
int i;
ulong reg;
char *s, *e;
char tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
然后是一些杂七杂八的函数
devices_init (); /* get the devices list going. *///设备初始化,这里的设备指的是设备驱动,从linux中移植而来
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
jumptable_init ();//跳转表初始化,其实没用到...
#if !defined(CONFIG_SMDK6442)
console_init_r (); /* fully init console as a device *///控制台的第二部分的初始化,有实质性的功能
#endif
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions */
enable_interrupts ();//中断初始化,其实这是个空函数,u-boot一般用不到中断
board_late_init ();//开发板上比较晚期的初始化,实际是空函数
eth_initialize(gd->bd);//利用驱动初始化网卡
extern void update_all(void);//这里开始做升级uboot相关的内容....没用到...