这篇文章是tiny6410到手以后分析的第一个程序后写下的记录,由于各种原因整理一半以后就被搁置了,有些未解决问题,
U-Boot启动代码分析第一部分(汇编语言):
主要完成cpu初始化所涉及到寄存器设置,并将第二部分C语言代码复制到RAM中;
此部分内容起始于/cpu/s3c64xx/S3c6410/start.S;
涉及文件:
/cpu/s3c64xx/S3c6410/start.S
/board/Samsung/Mini6410/lowlevel_init.S
Note:此文件夹为FriendlyARM针对Mini6410修改代码,移植于Smdk6410
/include/s3c6410.h
定义中断向量表 |
/cpu/start.S |
_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 |
/* * exception handlers */ .align 5 undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction
.align 5 software_interrupt: get_bad_stack_swi bad_save_user_regs bl do_software_interrupt |
在跳转中断服务程序前,首先要有两个宏操作,一个是对stack的操作,一个是对用户reg保存; |
.align 5 prefetch_abort: get_bad_stack bad_save_user_regs bl do_prefetch_abort
.align 5 data_abort: get_bad_stack bad_save_user_regs bl do_data_abort
.align 5 not_used: get_bad_stack bad_save_user_regs bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5 irq: get_irq_stack irq_save_user_regs bl do_irq irq_restore_user_regs
.align 5 fiq: get_fiq_stack /* someone ought to write a more effiction fiq_save_user_regs */ irq_save_user_regs bl do_fiq irq_restore_user_regs
#else
.align 5 irq: get_bad_stack bad_save_user_regs bl do_irq
.align 5 fiq: get_bad_stack bad_save_user_regs bl do_fiq |
#else
.align 5 irq: get_bad_stack bad_save_user_regs bl do_irq
.align 5 fiq: get_bad_stack bad_save_user_regs bl do_fiq |
对应的中断子程序将在 /cpu/$(ARCH)/interrupts.c中执行 |
存储器映射定义:
_TEXT_PHY_BASE: .word CFG_PHY_UBOOT_BASE
.globl _armboot_start _armboot_start: .word _start
/* * These are defined in the board-specific linker script. */ .globl _bss_start _bss_start: .word __bss_start
.globl _bss_end _bss_end: .word _end
#ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START IRQ_STACK_START: .word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif |
|
上电后执行动作:
1. 上电后执行reset:设置CPU为SVC模式: |
reset: /* * set the cpu to SVC32 mode */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 |
2. CPU初始化循环: 2.1 无效caches,关闭caches和MMU: |
cpu_init_crit: /* * 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
/* Peri port setup bank0 and bank1 set to 1 as 16bits mode*/ ldr r0, =0x70000000 /*70000000-SROM bus width and wait control*/ orr r0, r0, #0x13 /*bank0 and bank1 setting 16-bit*/ mcr p15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff) |
注:此处无效掉I/D caches,关闭caches和MMU,同时设置用于bank0和bank1的数据总线宽度控制器为16-bit模式; |
S3c64xx与s3c24xx此处初始化有些诧异:在S3c64xx中强制进行了cpu_init_crit, 而s3c24xx中首先判断,若未定义CONFIG_SKIP_LOWLEVEL_INIT,则进入cpu_init_crit |
2.2 配置ONENAND控制器:
#ifdef CONFIG_BOOT_ONENAND
ldr r0, =0x70000000 @ onenand controller setup
orr r0, r0, #0x100000
ldr r1, =0x4000
orr r1, r1, #0xe0
str r1, [r0]
#if defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)
orr r0, r0, #300 @ disable watchdog
mov r1, #1
str r1, [r0]
mov r1, #0x23000000 @ start buffer register
orr r1, r1, #0x30000
orr r1, r1, #0xc800
#else
mov r1, =0x20000000 @ start buffer register
orr r1, r1, #0xc30000
orr r1, r1, #0xc800
#endif
sub r0, r1, #0x0400 @ start address1 register
ldr r2, [r1, #0x84] @ ecc bypass
orr r2, r2, #0x100
str r2, [r1, #0x84]
mov r3, #0x0 @ DFS, FBA
str r3, [r0, #0x00]
str r3, [r0, #0x04] @ select dataram for DDP as 0
mov r4, #0x104 @ interrupt register
mov r5, #0x0002 @ FPA, FSA
mov r6, #0x0800 @ BSA
onenand_bl1_load:
str r5, [r0, #0x1c] @ save FPA, FSA
orr r6, r6, #0x02 @ BSC
str r6, [r1, #0x00] @ save BSA, BSC
str r3, [r1, r4] @ clear interrupt
str r3, [r1, #0x80] @ write load command
mov r7, #0x100 @ need small delay
onenand_wait_loop1:
subs r7, r7, #0x1
bne onenand_wait_loop1
add r5, r5, #0x2 @ next FPA, FSA
sub r6, r6, #0x2
add r6, r6, #0x200 @ next BSA
cmp r5, #0x8
bne onenand_bl1_load
#endif
若启动定义了CONFIG_BOOT_ONENAND,对ONENAND flash初始化,此类型ROM暂时不涉及,内容跳过。
2.3 调用lowlevel_init对底层的初始化:
lowlevel_init:
mov r12, lr /*保存程序返回地址,待调用执行完成后返回原地址*/
/* LED on only #8 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x55540000
str r1, [r0, #GPNCON_OFFSET]
ldr r1, =0x55555555
str r1, [r0, #GPNPUD_OFFSET]
ldr r1, =0xf000
str r1, [r0, #GPNDAT_OFFSET]
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x1
str r1, [r0, #GPECON_OFFSET]
ldr r1, =0x0
str r1, [r0, #GPEDAT_OFFSET]
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x2A5AAAAA
str r1, [r0, #GPPCON_OFFSET]
ldr r1, =0x0
str r1, [r0, #GPPDAT_OFFSET]
ldr r1, =0x55555555
str r1, [r0, #MEM1DRVCON_OFFSET]
/*以上为配置GPNIO、GPEIO、GPPIO端口输出*/
/* Disable Watchdog关闭看门狗 */
ldr r0, =0x7e000000 @0x7e004000
orr r0, r0, #0x4000
mov r1, #0
str r1, [r0]
@ External interrupt pending clear清除外部中断标志
ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/
ldr r1, [r0]
str r1, [r0]
ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000
ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000
@ Disable all interrupts (VIC0 and VIC1)关闭中断
mvn r3, #0x0
str r3, [r0, #oINTMSK]
str r3, [r1, #oINTMSK]
@ Set all interrupts as IRQ设置所有中断类型为IRQ
mov r3, #0x0
str r3, [r0, #oINTMOD]
str r3, [r1, #oINTMOD]
@ Pending Interrupt Clear清除中断标志
mov r3, #0x0
str r3, [r0, #oVECTADDR]
str r3, [r1, #oVECTADDR]
/*
* 以下为系统时钟初始化、串口初始化、NAND flash初始化调用,均为
* 对寄存器相应位设置,参考用户手册
*/
/*
* init system clock :关闭看门狗、屏蔽中断、设置FCLK:HCLK:PCLK时*钟比例
*/
bl system_clock_init
/* for UART */
bl uart_asm_init
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif
/*
* 猜测:此部分已屏蔽判断初始化地址,转为跳转至电源唤醒模式处
*/
#if 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 1f /* r0 == r1 then skip sdram init */
#endif
bl mem_ctrl_asm_init
#if 1
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfffffff7
cmp r1, #0x8
beq wakeup_reset
#endif
/*
* UART channel 0 transmit buffer register setting 0x4b4b4b4b
*/
1:
ldr r0, =ELFIN_UART_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
mov lr, r12
mov pc, lr
此刻lowlevle_inti执行完成,返回start.S中line240
2.4 判断启动地址并复制程序至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 */
此处判断程序运行地址是否在RAM中,如果没有,复制启动程序否则调用after_copy;
2.5 不同ROM设备的程序复制工作
Line252-line364针对NOR FLASH/NAND FLASH/MOVINAND FLASH/ONE NAND FLASH设备启动的代码复制进行区分设置,此部分代码量较大,不再附上;
2.6 copy code 后进行的工作(line365-line393):
1. GPP输出0xc00(why??)
2. enable domain access(why??)
3. 设置TTB寄存器
4. 使能MMU
2.7 清除RAM空间(line397-line423):
1. 通过_TEXT_BASE地址推算出映像的起始地址;
2. 设置_bss_start、_bss_end地址;
3. 清除bss空间;
2.8 程序跳转到_start_armboot处。
U-Boot启动第二部分(C语言):
继续汇编程序后的_start_armboot衔接部分,此部分已复制到RAM中,转为C语言启动过程继续;
此部分内容起始于:/Lib_arm/Board.c
涉及文件:
/Lib_arm/Board.c
C语言的入口函数:/board/Lib_arm/board.c void start_armboot(void);
1. 定义所需变量并为全局变量指针gd初始化:
init_fnc_t **init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
#if defined(CONFIG_BOOT_MOVINAND)
uint *magic = (uint *) (PHYS_SDRAM_1);
#endif
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
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;
#else
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif
/* 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));
monitor_flash_len = _bss_start - _armboot_start;
此处定义init_fnc_ptr,s指针;
如果未定义CFG_NO_FLASH定义size;
如果定义CONFIG_VFD或CONFIG_LCD则定义addr;
如果定义CONFIG_BOOT_MOVI_NAND则定义magic并初始化为PHYS_SDRAM_1;
如果定义CONFIG_MEMORY_UPPER_ CODE则定义gd_base并初始化为SDRAM起始地址;如果定义CONFIG_USE_IRQ重新计算,减去IRQ和FIQ长度,并将gd_base计算值作为地址对gd初始化;
对gd指针初始化设置内存空间;
对gd->bd指针初始化地址,并设置内存空间;