Chinaunix首页 | 论坛 | 博客
  • 博客访问: 29053
  • 博文数量: 8
  • 博客积分: 70
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-22 17:48
文章分类
文章存档

2013年(8)

我的朋友

分类: LINUX

2013-12-12 09:40:41

原文地址:u-boot stage1分析 作者:strongerII

u-boot stage 1分析 第一阶段的代码位于cpu/arm920t/start.S中,依次完成以下功能:
1、系统上电,进入svc模式
2、关闭看门狗,禁止所有中断
3、进行初级硬件初始化
4、将自身代码拷贝到SDRAM中
5、设置堆栈
6、清空bss段
7、跳转到C语言实现的stage2中

 本文主要是基于大家比较熟悉的 s3c2410 ,对移植 u-boot  stage1 过程进行一个分析,网上关于之方面的资料很多,但是几乎都只是对代码作注解,容易让人产生知其一不知其二的感觉,在这里,我主要结合 u-boot  stage1 时的内存布局和 stage1 的具体指令来做个分析,这样看起来比较直观,更容易理解一些 .

一、 全局看 u-boot

我们要深入理解 u-boot 如何工作的,以及跟硬件都有什么依赖,我们需要先对 u-boot 及硬件有一个全局的认识。

在这之前,为了更多的人不会迷糊一些问题,先澄清几个概念:

_start : 这是 u-boot 的第一条指令入口地址 , 如果从 flash 启动,就是 0x0, 如果直接下载到 SDRAM 中执行,则是 TEXT_BASE=0x33F80000.

_TEXT_BASE :本身是一个地址,但是地址处放的内容是 TEXT_BASE,

s3c2410 中我们通常设为 0x33F80000, 通过 config.mk 中的 -Ttext $(TEXT_BASE) 来指定链接选项,从而更新链接脚本中的入口地址,不明白的去查查 linker and loader.

 cpu/arm920t/start.S

_TEXT_BASE:

.word TEXT_BASE

    _armboot_start : 本身也是一个地址,但是地址处放的内容是 _start ,如果 _start  0x33F80000,  _artboot_start 放的内容就是 0x33F80000 ,见 cpu/arm920t/start.S

  _armboot_start:

       .word _start

知道这三个地址之后,再看两条指令:

adr r0,_start    /*r0 <- current position of code*/

这条指令网上讲得也很多,翻译过来就是 add r0,r0,[PC+#offset], 就是把通过一个地址来知道 _start 处的地址,注意是地址,即 TEXT_BASE=0x33F80000, 这步在链接的时候就已经确定了,或者你不用管那么多,你知道链接完成之后,这条指令相当于 mov r0,0x33F80000(sdram) 或者 mov r0,0x0(flash) 就行了。

ldr r1,_TEXT_BASE    /* test if we run from flash or RAM */

注意,这里的 ldr 不是伪指令,伪指令表示时, ldr r1,=_TEXT_BASE

这两个的区别在于,伪指令是直接把 _TEXT_BASE 写入到 r1 中,这里 _TEXT_BASE 就代表一个地址,而 ldr r1,_TEXT_BASE, 是把 _TEXT_BASE 中存放的内容,也就是 TEXT_BASE=0x33F80000 写入到了 r1.

ldr    r2, _armboot_start

结合上面讲的,应该知道,这条语句实际上是将 _armboot_start 中的内容,也就是 _start 的地址写入到了 r2 中,而非网上很多人问的是 _armboot_start 的地址 .

ldr    r3, _bss_start

这跟上面一样分析了,定义见 cpu/arm920t/start.S

.globl _bss_start

_bss_start:

  .word __bss_start

下面这两条语句也就好理解了:

  sub       r2, r3, r2         /* r2 <- armboot 大小   */

  add r2, r0, r2          /* r2 <- 代码结束地址   */

到底 armboot 的大小都包含了哪些东西,结合 u-boot.lds ,见下图:

 

 

    

    关于链接脚本,讲起来又很多了,不清楚的,建议看看 linker and loader ,清楚代码的编译链接及加载过程,是我们更深入的理解底层机制的根本。

 

在上面,我只是讲了几个平时可能遇到的,又不太理解的问题,关于 stage1 中代码的注释网上一搜就有一大把。看了注释,再结合,我讲的这几点,应该能弄清 stage1中是如何拷贝代码的了。接下来,我们来看看为什么 TEXT_BASE 的值是 0x33F80000 呢?

二、 TEXT_BASE=0x33F80000 的由来?

先看一个 SDRAM 的内存映射图:同样结合上面的 uboot.lds

TEXT_BASE = 0x33F80000

 

TEXT_BASE是代码执行的起始地址.编译产生的二进制文件必需下载到该地址,因为所有的函数,全局变量等等定位都是以这个地址为参照的.
如果uboot中是TEXT_BASE就是设的0x33F80000, 那么必需download到这个地址的ram中才能正常运行.
那么这个地址如何确定的呢? 是这样的如果你的板子上RAM地址从0x3000_0000开始的,那么你可以把bootload分配在任意的地方运行. 但是我们往往要保留一些内存空间作为备用(比如download大文件系统的时候,我们必需先保存到临时内存,可能几十兆大小的连续空间) 那么我们可以把bootloader放在起始或者末尾的地方
0x3000_0000____________________
         |
         |   保留的内存空间
.
.
.
         |
0x33f80000|____________________
         |      bootloader(128KByte
0x40000000|____________________
转自http://blog.sina.com.cn/s/blog_5064c382010099m2.html

映射前

映射后

 


bss 段、 u-boot cmd 段、 .data  .rodata 

 .text 段及中断向量表

malloc 区域,见 start.S  sub r0,r0, #CFG_MALLOC_LEN

全局变量,见 start.S sub  r0, r0, #CFG_GBL_DATA_SIZE

IRQ:sub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

sub   sp, r0, #12                 /* leave 3 words for abort-stack    */

.

.

 

bss 

u-boot cmd 

.data 数据段

.rodata 只读数据段,

入口 20 字节中断向量表 .text(start.o  *(.text))

  在 S3C2410 中,查看 datasheet  64M SDRAM 地址空间即为 0x30000000  0x33ffffff ,在 bank6 中,而 flash 映射地址为 0x0 开始。

  TEXT_BASE=0x33F80000 即为程序加载起始地址,可以使用的空间大小即为 0x33F80000  0x33FFFFFF  512K ,如果你 u-boot 包含的功能太多,觉得不够用,你可以把 0x33F80000 调小一点,即和往低地址移一些,移的过程中注意 memory page 对齐就行了,一般是 4KB

 

#include
#include

/*
 * 全局入口为.globl_start
 * 设置异常向量
*/
.globl _start
_start: b       reset                 /* 0x00000000*/复位
 ldr pc, _undefined_instruction    /* 0x00000004*/未定义指令
 ldr pc, _software_interrupt       /* 0x00000008*/软件中断
 ldr pc, _prefetch_abort           /* 0x0000000c*/预取指中止
 ldr pc, _data_abort               /* 0x00000010*/访问数据存储器中止
 ldr pc, _not_used                 /* 0x00000014*/该向量没有使用
 ldr pc, _irq                      /* 0x00000018*/正常中断
 ldr pc, _fiq                      /* 0x0000001c*/快速中断

_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

 .balignl 16,0xdeadbeef/*该伪操作指定对齐方式,16字节对齐*/

/////////////////////////////////////上面用于定义异常向量//////////////////////////////
 *************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************
 */

_TEXT_BASE:
 .word TEXT_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


/*
 * the actual reset code,这边才是真正的开始,开机后直接跳到这边执行
 */

reset:
 /*
  * set the cpu to SVC32 mode
  * cpsr的低八位是:I F T M M M M M
  *  中断禁止位:I F  Thumb位 模式位  svc为10011
  */
 mrs r0,cpsr       /*将处理器状态传送到到 ARM 寄存器的指令*/
 bic r0,r0,#0x1f   /*位清除指令*/
 orr r0,r0,#0xd3   /*置位指令,禁止中断,进入SVC模式*/
 msr cpsr,r0

/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON  0x15300000
# define INTMSK  0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON  0x53000000     /*看门狗定时器控制寄存器*/
# define INTMSK  0x4A000008 /* Interupt-Controller base addresses */中断控制器基地址
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */时钟分频器寄存器
#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
 ldr     r0, =pWTCON
 mov     r1, #0x0
 str     r1, [r0]/*关掉看门狗*/

 /*
  * mask all IRQs by setting all bits in the INTMR - default,关掉所有中断
  */
 mov r1, #0xffffffff
 ldr r0, =INTMSK
 str r1, [r0]
# if defined(CONFIG_S3C2410)
 ldr r1, =0x3ff
 ldr r0, =INTSUBMSK
 str r1, [r0]
# endif

 /* FCLK:HCLK:PCLK = 1:2:4 */
 /* default FCLK is 120 MHz ! */设置CPU的频率,默认为120MHz
 ldr r0, =CLKDIVN
 mov r1, #3
 str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */

 /*
  * we do sys-critical inits only at reboot,
  * not when booting from ram!
  */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit
#endif

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:    /* relocate U-Boot to RAM     */把自身拷贝到RAM中
 adr r0, _start  /* r0 <- current position of code   */
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM *//* 把_TEXT_BASE地址,就是BOOT在RAM中运行地址 */
 cmp     r0, r1                  /* don't reloc during debug         */ /* 比较两个地址是否相同,如果相同,就已经在RAM运行,否则就是FLASH中运行。*/

 beq     stack_setup/*如果已经是在RAM中,则跳转*/

 ldr r2, _armboot_start
 ldr r3, _bss_start
 sub r2, r3, r2  /* r2 <- size of armboot            */
 add r2, r0, r2  /* r2 <- source end address         *//*r0为基地址,r2为末地址*/

copy_loop:
 ldmia r0!, {r3-r10}  /* copy from source address [r0]    */
 stmia r1!, {r3-r10}  /* copy to   target address [r1]    */
 cmp r0, r2   /* until source end addreee [r2]    */
 ble copy_loop//循环拷贝
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

 /* Set up the stack          */栈空间设置
stack_setup:
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */
 sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 sub sp, r0, #12  /* leave 3 words for abort-stack    */设置sp,留下3个字为Abort

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/*循环清除BSS段*/

#if 0
 /* try doing this stuff after the relocation */
 ldr     r0, =pWTCON
 mov     r1, #0x0
 str     r1, [r0]

 /*
  * mask all IRQs by setting all bits in the INTMR - default
  */
 mov r1, #0xffffffff
 ldr r0, =INTMR
 str r1, [r0]

 /* FCLK:HCLK:PCLK = 1:2:4 */
 /* default FCLK is 120 MHz ! */
 ldr r0, =CLKDIVN
 mov r1, #3
 str r1, [r0]
 /* END stuff after relocation */
#endif

#ifdef CONFIG_S3C2410_NAND_BOOT
    bl    copy_myself
 
      @ jump to ram
    ldr   r1, =on_the_ram
    add  pc, r1, #0
    nop
    nop
    1:    b     1b          @ infinite loop/*无限循环*/
 
on_the_ram:
#endif

 ldr pc, _start_armboot

_start_armboot: .word start_armboot/*开始进入C代码部分*/

#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
    mov r10, lr
@ reset NAND
    mov r1, #NAND_CTL_BASE
     ldr   r2, =0xf830           @ initial value
    str   r2, [r1, #oNFCONF]
    ldr   r2, [r1, #oNFCONF]
    bic  r2, r2, #0x800              @ enable chip
    str   r2, [r1, #oNFCONF]
    mov r2, #0xff         @ RESET command
    strb r2, [r1, #oNFCMD]
    mov r3, #0                   @ wait

1:add  r3, r3, #0x1
    cmp r3, #0xa
    blt   1b
2:ldr   r2, [r1, #oNFSTAT]      @ wait ready
    tst    r2, #0x1
    beq  2b
    ldr   r2, [r1, #oNFCONF]
    orr  r2, r2, #0x800              @ disable chip
    str   r2, [r1, #oNFCONF]

@ get read to call C functions (for nand_read())
    ldr   sp, DW_STACK_START       @ setup stack pointer
    mov fp, #0                    @ no previous frame, so fp=0

@ copy vivi to RAM
    ldr   r0, =UBOOT_RAM_BASE
    mov     r1, #0x0
    mov r2, #0x20000
    bl    nand_read_ll
    tst    r0, #0x0
    beq  ok_nand_read

#ifdef CONFIG_DEBUG_LL
    bad_nand_read:
    ldr   r0, STR_FAIL
    ldr   r1, SerBase
    bl    PrintWord
1:b     1b          @ infinite loop
#endif

ok_nand_read:
#ifdef CONFIG_DEBUG_LL
    ldr   r0, STR_OK
    ldr   r1, SerBase
    bl    PrintWord
#endif

@ verify
    mov r0, #0
    ldr   r1, =UBOOT_RAM_BASE
    mov r2, #0x400     @ 4 bytes * 1024 = 4K-bytes
go_next:
    ldr   r3, [r0], #4
    ldr   r4, [r1], #4
    teq   r3, r4
    bne  notmatch
    subs r2, r2, #4
    beq  done_nand_read
    bne  go_next

notmatch:
#ifdef CONFIG_DEBUG_LL
    sub  r0, r0, #4
    ldr   r1, SerBase
    bl    PrintHexWord
    ldr   r0, STR_FAIL
    ldr   r1, SerBase
    mov r2, #0
     mov r3, r2
    mov r4, r2
    mov r5, r2
    mov r6, r2
    mov r7, r2
    mov r8, r2
    mov r9, r2

clear_loop:
    stmia      r0!, {r2-r9}
    subs r1, r1, #(8 * 4)
    bne  clear_loop
    mov pc, lr

#endif

1:b     1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL
    ldr   r0, STR_OK
    ldr   r1, SerBase
    bl    PrintWord
#endif
    mov pc, r10
@ clear memory
@ r0: start address
@ r1: length
    mem_clear:
    mov r2, #0
     mov r3, r2
    mov r4, r2
    mov r5, r2
    mov r6, r2
    mov r7, r2
    mov r8, r2
    mov r9, r2

clear_loop:
    stmia      r0!, {r2-r9}
    subs r1, r1, #(8 * 4)
    bne  clear_loop
    mov pc, lr

#endif @ CONFIG_S3C2410_NAND_BOOT

/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */


cpu_init_crit:
 /*
  * flush v4 I/D caches
  */
 mov r0, #0
 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */使cache无效
 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */使TLB无效

 /*
  * disable MMU stuff and caches禁止MMU和cache
  */
 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

 /*
  * before relocating, we have to setup RAM timing重定位之前,应该先进行RAM时序的设置
  * because memory timing is board-dependend, you will
  * find a lowlevel_init.S in your board directory.
  */
 mov ip, lr
 bl lowlevel_init/*设置存储控制器的相关参数*/
 mov lr, ip
 mov pc, lr/*cpu_init_crit返回*/


/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */

@
@ 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

/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */

 .macro bad_save_user_regs
 sub sp, sp, #S_FRAME_SIZE
 stmia sp, {r0 - r12}   @ Calling r0-r12
 ldr r2, _armboot_start
 sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub r2, r2, #(CFG_GBL_DATA_SIZE+8)  @ set base 2 words into abort stack
 ldmia r2, {r2 - r3}   @ get pc, cpsr
 add r0, sp, #S_FRAME_SIZE  @ restore sp_SVC

 add r5, sp, #S_SP
 mov r1, lr
 stmia r5, {r0 - r3}   @ save sp_SVC, lr_SVC, pc, cpsr
 mov r0, sp
 .endm

 .macro irq_save_user_regs
 sub sp, sp, #S_FRAME_SIZE
 stmia sp, {r0 - r12}   @ Calling r0-r12
 add     r8, sp, #S_PC
 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
 sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack

 str lr, [r13]   @ save caller lr / spsr
 mrs lr, spsr
 str     lr, [r13, #4]

 mov r13, #MODE_SVC   @ prepare SVC-Mode
 @ msr spsr_c, r13
 msr spsr, r13
 mov lr, pc
 movs pc, lr
 .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

/*
 * exception handlers
 */
 .align  5
undefined_instruction:
 get_bad_stack
 bad_save_user_regs
 bl  do_undefined_instruction

 .align 5
software_interrupt:
 get_bad_stack
 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
 /* 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

#endif

 .align     2
DW_STACK_START:
 .word      STACK_BASE+STACK_SIZE-4

阅读(1635) | 评论(0) | 转发(0) |
0

上一篇:U-boot源码整体框架

下一篇:没有了

给主人留下些什么吧!~~