天天看点

基于S3C6410的U-boot启动代码分析(一)

预备知识

可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init 数据、bss等。这些对象文件都是由一个称为 链接器脚本(*lds)的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。 因此在分析u-boot代码是,首先应关注的是u-boot.lds文件,它位于$(U-BOOT_SRC_ROOT)/board/$(BOARD_NAME)目录下。

U-Boot启动过程分析

     U-Boot的启动过程可以分为两个阶段,两个阶段分别完成如下功能:

     (1)第一阶段的功能

Ø  硬件设备初始化

Ø  加载U-Boot第二阶段代码到RAM空间

Ø  设置好栈

Ø  跳转到第二阶段代码入口

      (2)第二阶段的功能

Ø  初始化本阶段使用的硬件设备

Ø  检测系统内存映射

Ø  将内核从Flash读取到RAM中    

Ø  为内核设置启动参数

    第一阶段对应的文件是start.S和lowlevel_init.S。

    U-Boot启动第一阶段流程如下:

首先分析u-boot.lds文件

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm) 

ENTRY(_start)

SECTIONS

{

 . = 0x00000000;

 . = ALIGN(4);

 .text      :

 {

   cpu/s3c64xx/start.o (.text)  

   cpu/s3c64xx/s3c6410/cpu_init.o (.text)

   cpu/s3c64xx/onenand_cp.o (.text)

   cpu/s3c64xx/nand_cp.o (.text)

   cpu/s3c64xx/movi.o (.text)

   *(.text)

   lib_arm/div0.o

 }

 . = ALIGN(4);

 .rodata : { *(.rodata) } 

 . = ALIGN(4);

 .data : { *(.data) }  

 . = ALIGN(4);

 .got : { *(.got) }  

 __u_boot_cmd_start = .;  

 .u_boot_cmd : { *(.u_boot_cmd) } 

 __u_boot_cmd_end = .;    

 . = ALIGN(4);

 .mmudata : { *(.mmudata) }  

 . = ALIGN(4);

 __bss_start = .;  

 .bss : { *(.bss) }  

 _end = .;    

}

        由u-boot.lds文件可知:第一个链接的是start.o文件,因此u-boot.bin的入口代码在start.o中,其源代码为start.S,u-boot.bin执行的入口点为ENTRY(_start)。

       下面就来分析start.s文件:(start.s位于目录$(U-BOOT_SRC_ROOT)/cpu/$(CPU_NAME)/目录下)

 -----------------------------------------------------------------------------------------------------------------------------------------------

#include <config.h>

#include <version.h>

#ifdef CONFIG_ENABLE_MMU

#include <asm/proc/domain.h>

#endif

#include <regs.h>

#ifndef CONFIG_ENABLE_MMU

#ifndef CFG_PHY_UBOOT_BASE

#define CFG_PHY_UBOOT_BASE CFG_UBOOT_BASE

#endif

#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

_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

.global _end_vect

_end_vect:

 .balignl 16,0xdeadbeef

_TEXT_BASE:

 .word TEXT_BASE

_TEXT_PHY_BASE:

 .word CFG_PHY_UBOOT_BASE

.globl _armboot_start   

_armboot_start:

 .word _start     

.globl _bss_start      

_bss_start:

 .word __bss_start    

.globl _bss_end

_bss_end:

 .word _end

#ifdef CONFIG_USE_IRQ

.globl IRQ_STACK_START    

IRQ_STACK_START:

 .word 0x0badc0de

.globl FIQ_STACK_START

FIQ_STACK_START:

 .word 0x0badc0de

#endif

reset:

 mrs r0,cpsr   

 bic r0,r0,#0x1f 

 orr r0,r0,#0xd3 

 msr cpsr,r0  

cpu_init_crit:

 mov r0, #0  

 * <MRC/MCR> {<cond>} cp, opcode1, Rd, Cn, Cm{,opcode2}

 * <LDC/STC> {<cod>} cp, Cd, addressing 

 * 其中:cp域代表协处理器的编号,为p0~P15. opcode域描述要在协处理器中进行的操作。Cn, Cm及Cd描述在协处理器中的寄存器。

 * 协处理器15(CP15)是为系统控制预留的,如内存管理,写缓冲控制,cache控制及寄存器识别等。

 * MRC p15,0,r10,c0,c0,0 //把协处理器15寄存器c0的内容拷贝到r10中,cp15寄存器c0中包含处理器标识,其内容拷贝到通用寄存器r10

 */

 mcr p15, 0, r0, c7, c7, 0 

 mcr p15, 0, r0, c8, c7, 0 

 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

 ldr r0, =0x70000000

 orr r0, r0, #0x13 

     mcr p15,0,r0,c15,c2,4       @ 256M(0x70000000-0x7fffffff)

#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 //r0 = r1 -0x0400

 ldr r2, [r1, #0x84]  @ ecc bypass  // equals to ldr r2 r1+0x84

 orr r2, r2, #0x100  @ 按位求或,将r2寄存器的Bit8置1,其他位保持不变。

 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

 bl lowlevel_init 

 ldr r0, =0xff000fff

 bic r1, pc, r0  

 ldr r2, _TEXT_BASE  

 bic r2, r2, r0  

 cmp     r1, r2                 

 beq     after_copy  

#ifdef CONFIG_BOOT_NOR   

 adr r0, _start  

 ldr r1, _TEXT_PHY_BASE 

 ldr r2, _armboot_start

 ldr r3, _bss_start

 sub r2, r3, r2  

 add r2, r0, r2  

nor_copy_loop:

 ldmia r0!, {r3-r10}  

 stmia r1!, {r3-r10}  

 cmp r0, r2   

 ble nor_copy_loop

 b after_copy

#endif

#ifdef CONFIG_BOOT_NAND

 mov r0, #0x1000

 bl copy_from_nand

#endif

#ifdef CONFIG_BOOT_MOVINAND

 ldr sp, _TEXT_PHY_BASE

 bl movi_bl2_copy

 b after_copy

#endif

#ifdef CONFIG_BOOT_ONENAND

 ldr sp, =0x50000000  @ temporary stack

#ifdef CONFIG_S3C6400

 mov r1, =0x20000000  @ start buffer register

 orr r1, r1, #0xc30000

 orr r1, r1, #0xc800

#else

 mov r1, #0x23000000  @ start buffer register

 orr r1, r1, #0x30000

 orr r1, r1, #0xc800

#endif

 ldr r2, [r1, #0x84]  @ ecc bypass

 orr r2, r2, #0x100

 str r2, [r1, #0x84]

 sub r0, r1, #0x0400  @ start address1 register

 str r3, [r0, #0x00]

 str r3, [r0, #0x04]  @ select dataram for DDP as 0

 mov r4, #0x104  @ interrupt register

 mov r6, #0x0c00  @ fixed dataram1 sector number

 str r6, [r1, #0x00]

 mov r3, #0x0  @ DFS, FBA

 mov r5, #0x0000  @ FPA, FSA

 ldr r9, =CFG_PHY_UBOOT_BASE @ destination

onenand_bl2_load:

 str r3, [r0, #0x00]  @ save DFS, FBA

 str r5, [r0, #0x1c]  @ save FPA, FSA

 mov r7, #0x0  @ clear interrupt

 str r7, [r1, r4]

 str r7, [r1, #0x80]  @ write load command

 mov r8, #0x1000

onenand_wait_loop2:

 subs r8, r8, #0x1

 bne onenand_wait_loop2

onenand_wait_int:   @ wait INT and RI

 ldr r7, [r1, r4]

 mov r8, #0x8000

 orr r8, r8, #0x80

 tst r7, r8

 beq onenand_wait_int

 mov r7, #0x0  @ clear interrupt

 str r7, [r1, r4]

 mov r8, #0xc00  @ source address (dataram1)

 mov r10, #0x40  @ copy loop count (64 = 2048 / 32)

 stmia sp, {r0-r7}  @ backup

onenand_copy_to_ram:

 ldmia r8!, {r0-r7}

 stmia r9!, {r0-r7}

 subs r10, r10, #0x1

 bne onenand_copy_to_ram

 ldmia sp, {r0-r7}  @ restore

 add r5, r5, #0x4  @ next FPA

 cmp r5, #0x100  @ last FPA?

 bne onenand_bl2_load

 mov r5, #0x0  @ reset FPA

 add r3, r3, #0x1  @ next FBA

 cmp r3, #0x2  @ last FBA?

 bne onenand_bl2_load

 b after_copy

#endif

#ifdef CONFIG_BOOT_ONENAND_IROM

 ldr sp, _TEXT_PHY_BASE

 bl onenand_bl2_copy

 b after_copy

#endif

after_copy:

 ldr r0, =ELFIN_GPIO_BASE

 ldr r1, =0xC00

 str r1, [r0, #GPPDAT_OFFSET]

#ifdef CONFIG_ENABLE_MMU

enable_mmu:

 ldr r5, =0x0000ffff

 mcr p15, 0, r5, c3, c0, 0  @ load domain access 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

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:

stack_setup:

#ifdef CONFIG_MEMORY_UPPER_CODE

 ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc)

#else

 ldr r0, _TEXT_BASE  

 sub r0, r0, #CFG_MALLOC_LEN 

 sub r0, r0, #CFG_GBL_DATA_SIZE

#ifdef CONFIG_USE_IRQ

 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

 sub sp, r0, #12  

#endif

clear_bss:

 ldr r0, _bss_start  

 ldr r1, _bss_end  

 mov  r2, #0x00000000  

clbss_l:

 str r2, [r0]  

 add r0, r0, #4

 cmp r0, r1

 ble clbss_l

 ldr pc, _start_armboot

_start_armboot:

 .word start_armboot

#ifdef CONFIG_ENABLE_MMU

_mmu_table_base:

 .word mmu_table

#endif

 .globl copy_from_nand

copy_from_nand:

 mov r10, lr  

 mov r9, r0

 ldr sp, _TEXT_PHY_BASE 

 sub sp, sp, #12

 mov fp, #0   

 mov r9, #0x1000

 bl copy_uboot_to_ram

3: tst  r0, #0x0

 bne copy_failed

 ldr r0, =0x0c000000

 ldr r1, _TEXT_PHY_BASE

1: ldr r3, [r0], #4

 ldr r4, [r1], #4

 teq r3, r4

 bne compare_failed 

 subs r9, r9, #4

 bne 1b

4: mov lr, r10  

 mov pc, lr

copy_failed:

 nop   

 b copy_failed

compare_failed:

 nop   

 b compare_failed

#ifdef CONFIG_ENABLE_MMU

 .globl theLastJump

theLastJump:

 mov r9, r0

 ldr r3, =0xfff00000

 ldr r4, _TEXT_PHY_BASE

 adr r5, phy_last_jump

 bic r5, r5, r3

 orr r5, r5, r4

 mov pc, r5

phy_last_jump:

 mrc p15, 0, r0, c1, c0, 0

 bic r0, r0, #0x00002300 

 bic r0, r0, #0x00000087 

 orr r0, r0, #0x00000002 

 orr r0, r0, #0x00001000 

 mcr p15, 0, r0, c1, c0, 0

 mcr p15, 0, r0, c8, c7, 0 

 mov r0, #0

 mov pc, r9

#endif

@

@ IRQ stack frame.

@

#define S_FRAME_SIZE 72

#define S_OLD_R0 68

#define S_PSR  64

#define S_PC  60

#define S_LR  56

#define S_SP  52

#define S_IP  48

#define S_FP  44

#define S_R10  40

#define S_R9  36

#define S_R8  32

#define S_R7  28

#define S_R6  24

#define S_R5  20

#define S_R4  16

#define S_R3  12

#define S_R2  8

#define S_R1  4

#define S_R0  0

#define MODE_SVC 0x13

#define I_BIT  0x80

 .macro bad_save_user_regs    

 sub sp, sp, #S_FRAME_SIZE  @ carve out a frame on current user stack

 stmia sp, {r0 - r12}   @ Save user registers (now in svc mode) r0-r12

 ldr r2, _armboot_start

 sub r2, r2, #(CFG_MALLOC_LEN)

 sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack

 ldmia r2, {r2 - r3}   @ get values for "aborted" pc and cpsr (into parm regs)

 add r0, sp, #S_FRAME_SIZE  @ grab pointer to old stack

 add r5, sp, #S_SP

 mov r1, lr

 stmia r5, {r0 - r3}   @ save sp_SVC, lr_SVC, pc, cpsr

 mov r0, sp      @ save current stack into r0 (param register)

 .endm

 .macro irq_save_user_regs

 sub sp, sp, #S_FRAME_SIZE

 stmia sp, {r0 - r12}   @ Calling r0-r12

 add r8, sp, #S_PC   @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.

 stmdb r8, {sp, lr}^   @ Calling SP, LR

 str lr, [r8, #0]   @ Save calling PC

 mrs r6, spsr

 str r6, [r8, #4]   @ Save CPSR

 str r0, [r8, #8]   @ Save OLD_R0

 mov r0, sp

 .endm

 .macro irq_restore_user_regs

 ldmia sp, {r0 - lr}^   @ Calling r0 - lr

 mov r0, r0

 ldr lr, [sp, #S_PC]   @ Get PC

 add sp, sp, #S_FRAME_SIZE

 subs pc, lr, #4   @ return & move spsr_svc into cpsr

 .endm

 .macro get_bad_stack   

 ldr r13, _armboot_start  @ setup our mode stack (enter in banked mode)

 sub r13, r13, #(CFG_MALLOC_LEN) @ move past malloc pool

 sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ move to reserved a couple spots for abort stack

 str lr, [r13]   @ save caller lr in position 0 of saved stack

 mrs lr, spsr   @ get the spsr

 str lr, [r13, #4]   @ save spsr in position 1 of saved stack

 mov r13, #MODE_SVC   @ prepare SVC-Mode

 @ msr spsr_c, r13

 msr spsr, r13   @ switch modes, make sure moves will execute

 mov lr, pc    @ capture return pc

 movs pc, lr    @ jump to next instruction & switch modes.

 .endm

 .macro get_bad_stack_swi

 sub r13, r13, #4   @ space on current stack for scratch reg.

 str r0, [r13]   @ save R0's value.

 ldr r0, _armboot_start  @ get data regions start

 sub r0, r0, #(CFG_MALLOC_LEN) @ move past malloc pool

 sub r0, r0, #(CFG_GBL_DATA_SIZE+8) @ move past gbl and a couple spots for abort stack

 str lr, [r0]   @ save caller lr in position 0 of saved stack

 mrs r0, spsr   @ get the spsr

 str lr, [r0, #4]   @ save spsr in position 1 of saved stack

 ldr r0, [r13]   @ restore r0

 add r13, r13, #4   @ pop stack entry

 .endm

 .macro get_irq_stack   @ setup IRQ stack

 ldr sp, IRQ_STACK_START

 .endm

 .macro get_fiq_stack   @ setup FIQ stack

 ldr sp, FIQ_STACK_START

 .endm

 .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

 .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

 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

#endif

 .align 5

.global arm1136_cache_flush

arm1136_cache_flush:

  mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache

  mov pc, lr   @ back to caller

#if defined(CONFIG_INTEGRATOR) && defined(CONFIG_ARCH_CINTEGRATOR)

#elif defined(CONFIG_S3C64XX)

#else

 .align 5

.globl reset_cpu

reset_cpu:

 ldr r1, rstctl 

 mov r3, #0x2 

 str r3, [r1] 

 mov r0, r0

_loop_forever:

 b _loop_forever

rstctl:

 .word PM_RSTCTRL_WKUP

#endif

在start.s中还调用到lowlevel_init.S的 lowlevel_init,等看完芯片手册的相关部分在继续分析吧!

【参考文献】

[1]. 嵌入式设备上的 Linux 系统开发  http://www.ibm.com/developerworks/cn/linux/embed/embdev/

[2]. ARM嵌入式系统开发  -- 软件设计与优化   [美]Andrew N.Sloss [英] Dominic Symes 著 沈建华[译]    北京航空航天大学出版社  2009-05

[3].  网络其他人的博客,表示感谢!

继续阅读