u-boot 1.1.6 start.S 代码学习

本文深入剖析U-Boot启动过程中的关键代码,包括start.S文件中的初始化、重定位、堆栈设置等内容,并解释了重要指令的功能及其实现机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

u-boot之前看过,不过是大体了解作用,但是感觉搞嵌入式的不清楚u-boot终究不好,加上最近项目没什么事,赶紧学习学习,

这篇文章对start.S的流程讲的很详细,看完了也就明白U-boot stage1干了什么事了

 

==================================================================================

u-boot 1.1.6  start.S 代码学习

/*

参考了别人的一些笔记,看完了启动代码。

本文档记录在看代码时碰到的困难,将这些曾经困扰的问题记录下来,以备今后之用。分析时不重要的代码被删除了。

*/

.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

 

    .balignl 16,0xdeadbeef

/*

_start 是整个u-boot 程序的入口点,即链接后,该处是整个程序的第一条指令。程序的入口点是由链接脚本所指定,比如对于smdk2410 的板子( 下面都以smdk241 为例) ,脚本文件位于board/smdk2140/u-boot.lds 。在该脚本文件中:ENTRY(_start)  即指定程序的入口地址。globl _start 定义一个外部可以引用的变量,比如说,在其它源代码文档中,就可以直指引用_start 这个变量。如int entry=_start; 那此处entry 值将是多少呢?因为_start 相当于一个变量,entry 的值就是_start 处存储的值,即 b reset 机器码值。关于globl 定义的变量得注意的地方,后面还有记录

 

这段代码处理中断向量表。

 

关于 balignal 16,0xdeadbeef 的网上资料:

.align 伪操作用于表示对齐方式:通过添加填充字节使当前位置满足一定的对齐方式。

.balign 的作用同.align

.align {alignment} {,fill} {,max}

其中:alignment 用于指定对齐方式,可能的取值为2 的次幂,缺省为4fill 是填充内容,

缺省用0 填充。max 是填充字节数最大值,如果填充字节数超过max, 就不进行对齐.

例如: .align 4, 指定对齐方式为字对齐

deadbeef 一般用来表示没用的或是已经被释放掉的内存

*/

 

_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

/*

备注这几个由.word 伪操作符定义变量的作用及其取值

_TEXT_BASE:

    .word   TEXT_BASE

 

_TEXT_BASE: 此处定义一汇编语言标签,更好的理解就是:告诉编译器,为_TEXT_BASE 分配存储空间,该空间的名字就叫_TEXT_BASE, 该空间中存储的值就是由.word 后面确定的TEXT_BASEC(0x33F80000) ,相当于C 语言中 long _TEXT_BASE=TEXT_BASE; TEXT_BASE 定义在board/smdk2410/config.mk 文件中。该值的作用是告诉链接器,本程序运行的基地址为TEXT_BASEU-boot 编译后,烧在FLASH 的第一个块中,CPU 复位上电后,PC 寄存器为0x0000 。怎么会跑到TEXT_BASE 处执行呢?

事实上,CPU 上电后,从地址0x0000 处执行,而U-BOOT 的最起始代码,即本文件中从_start 开始的代码是与地址不相关的,这段代码放在任何空间执行的结果都是一样(当然不是绝对,假设u-boot 代码段是100K ,则放在TEXT_BASE-80K 处,搬运时就会把u-boot 代码后面20K 部分覆盖为最前面的20K )。

 

.globl _armboot_start

_armboot_start:

    .word _start

定义外部可以引用的变量_armboot_start, 。即相当于C long _armboot_start=&_start; _armboot_start 值是多少?是TEXT_BASE,0x33F80000 !等价的那条C语句,取的是_start 变量地址,而不是_start 本身。在C语言中,定义一变量 int x=100; 就是告诉编译器。给我一个int 大小的存储空间,该空间存储的值就是100 ,这个空间在哪呢?即空间地址是多少呢?我们可以通过&x 知道。

在汇编语言中,理解上有点不一样。上面三行语句,

第一句,告诉编译器,向外面输出变量_armboot_start

第二句,_armboot_start 变量在此处,到底在哪,要到链接时才能确定,凡正现在知道有这么一个变量了。

第三句,_armboot_start 变量空间放的数据为_start 标签的值。这点与C语言的理解有点不一样了。此处引用的是_start 标签对应处的地址。在汇编语言中,标签代表的就是那行所在的地址。

图1是从u-boot 编译后生成的u-boot.map 截图的。从此文件中知道,_armboot_start 这个变量地址为0x33f80044

图一

/*

.globl _bss_start

_bss_start:

    .word __bss_start

此三句,特别要备注的是__bss_start 这个符号,它的值为多少?该值定义在board/smdk2410/u-boot.lds 文件中

. = ALIGN(4);

     __bss_start = .;

     .bss : { *(.bss) }

     _end = .;

上面的__bss_start=.; 表示__bss_start 值就是当前位置的值。当前位置是多少呢?从下面一句

.bss:{*(.ss)} 知道。紧接该位置后面马上就是放bss 段数据了。所以,当然就是bss 段的起始地址。_end 就是bss 段的结束地址。

参考:http://blog.chinaunix.net/u1/58780/showart_462971.html

bss 是这个链接脚本的最后一个段。start.S 就是以这个段的起始地址来计算要搬运u-boot 大小的代码的。即,这个段前面的所有数据都将被搬到TEXT_BASE 处。然后跑到start_armboot 处,即C语言的入口代码。__bss_start 这个值是多少? Smdk2410 我编译后值是0x33f96f20 。可以从图2的map 文件中查到,bss 段从0x33f96f20 开始分配的。

再次验证一下0x33f96f20 就是__bss_start 值,我们可以从图1处可以看到有个变量_bss_start 该变量就是由下面三条语句所定义,_bss_start 处保存的值就是__bss_start

.globl _bss_start

_bss_start:

    .word __bss_start

UltraEdit 打开生成的u-boot.bin ,定位到文件偏移0x48(0x48_bss_start 所在地址0x33f80048-TEXT_BASE 得到), 如图3,此处值确实是20 6F F9 33( 注意大小端)

*/

图二

图三

 

reset:

    /*

      * set the cpu to SVC32 mode

      */

    mrs  r0,cpsr

    bic  r0,r0,#0x1f

    orr  r0,r0,#0xd3

    msr  cpsr,r0

/*

上面几行进入SVC 模式,通过装入CPSRr0 ,修改相应位后再载入到CPSR

*/

/* turn off the watchdog 关闭WATCH DOG ,具体寄存器地址看芯片DATASHEET 即知*/

#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 ! */

    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

/*

下面是重定位代码,即将整个u-boot 搬到TEXT_BASE 地址处的代码

/*

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:                  /* relocate U-Boot to RAM       */

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

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

    cmp     r0, r1           /* don't reloc during debug       */

    beq     stack_setup

 

    ldr  r2, _armboot_start

    ldr  r3, _bss_start

    sub  r2, r3, r2         /* r2 <- size of armboot            */

    add  r2, r0, r2         /* r2 <- source end address         */

 

/*

adr r0, _start 这句代码是将_start 标签处运行时的地址值装进r0 ,这条指令到底取一个什么值呢?假设运行到该指令时pc 寄存器的值是X,start 标签相对此本指令有Y 的偏移,则r0=X+Y(Y 可能是负值).

针对该程序来说,当被装载到0x0000 处运行时,r0 就是0 ,假设装载到TEXT_BASE 处运行,则r0=TEXT_BASE.

几条指令分析如下:

求正在运行的程序_start 标签处地址到r0 ,将_TEXT_BASE 变量值装到r1 ,即将0x33f80000 装到r1, 比较r0/r1 是否相等,这个比较就是确定当前是不是从Flash 中执行的,还是这段代码已装载到0x33f80000 处执行的。如果不等于0x33f80000 ,则将FLASH 中的u-boot 代码搬到0x33f80000 处。

具体计算如下:

ldr r2,_armboot_start à r2=0x33f80000

ldr r3,_ss_start à r3=bss 段的起始地

*/

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

/*

上面几行将r0 地址处的代码复制到r1 处,即从0x0000 搬到0x33f80000( 假设CPU 复位从0x0000 执行的)

*/

#endif  /* CONFIG_SKIP_RELOCATE_UBOOT */

 

/* Set up the stack   设置堆栈 */就是使sp寄存器指向一段没有使用的内存即可;

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    */留出12字节的内存给abort异常;

/*

上面代码中, 指定从(TEXT_BASE - CFG_MALLOC_LEN) 开始地址,大小为CFG_MALLOC_LEN 的内存预留给由malloc 函数。因为u-boot 中其它地方调用了malloc 函数,该空间就是供其动态分配的。

再向低端又预留了CFG_GBL_DATA_SIZE 字节的数据(注:为global_data结构体预留的空间,看board.c)

然后为IRQFIQ 中断预留空间,

最后将预留空间的低端地址赋给spARM 堆栈由低到高增长的??

*/

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

/* 下面一句跳到C语言的入口处start_armboot (函数定义在board.c 中) */

    ldr  pc, _start_armboot

 

_start_armboot:   .word start_armboot

 

/* 关键代码分析结束*/

 

本文转自:http://blog.youkuaiyun.com/rockhard/archive/2009/05/11/4166642.aspx


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值