首页 > 代码库 > 自己学驱动7——uboot代码阅读二(start.S)
自己学驱动7——uboot代码阅读二(start.S)
#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
在uboot的start.S中上面的这一段程序是对IRQ栈起始地址的一个初始化,这里往IRQ_STACK_START和FIQ_STACK_START地址处的内容都写上了0x0badc0de这个值,这个值可以看做是“bad code”,因为这个值现在是没有意义的,只是随意填充的一个值,真正需要设置这个值的时候会是在cpu_init(void)函数里面(这个函数的路径为:cpu\arm920t/Cpu.c)。
/* 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]
这一小段代码中,首先根据CPU的类型,定义了相应的寄存器的地址,然后使用ldr和str指令对该寄存器进行设置(这里只列出了pWTCON看门狗寄存器的设置语句)。
# define pWTCON 0x15300000 /*定义了看门狗寄存器的地址*/
ldr r0, =pWTCON /*ldr指令在使用=号的时候,是一个伪指令,会将后面标号本身的值
而不是标号所代表的地址处的内容赋给目标,所以这个语句完成的
功能就是把0x15300000这个值赋给了pWTCON*/
str r1, [r0] /*这个是把r0寄存器里面的值当做一个地址值,将r1的值赋值到这个内寸*/
#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 */
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 */
这一段代码非常有意义,是一段重定位代码的程序。在重定位之前首先会判断一下是否需要重定位,判断的方法如下:
adr r0, _start /*这是一个地址读取的位置无关指令,将_start的地址读取到r0中
如果当前的start.S被放在Flash中,那么这个得到的_start为0
而如果当前的start.S被加载到了内存中,那么这时候得到的值
将会是一个不为0的值,因为2440外接的Flash和RAM是在起始地
址不同的bank处*/
ldr r1, _TEXT_BASE /*这条指令会将_TEXT_BASE代表的地址处的值读取到r1中,然而
根据_TEXT_BASE: .word TEXT_BASE这个地址存放的值来自于
链接脚本中,代表的是其运行地址*/
后面的工作就是比较r0和r1的值,如果是相等的,那么证明这个时候代码已经位于了其应该位于的地方,这个时候是不需要重定位代码的。但是有一点使人比较迷惑的就是,现在我们分析的start.S代码是系统上电就会执行的一段代码,前面根本没有重定位的代码,代码怎么还会可能就位于其应该位于的地方?答案是这种情况发生在使用仿真器仿真的时候,这种情况下,仿真器帮我们做了重定位的工作。如果r1和r0的值相等,那么将会是一个跳转,直接跳转到刚好重定位代码结束的地方继续往下执行。
再继续往下看:
_armboot_start: .word _start
r2寄存器中存放的是_start标号的值,也就是所有代码的开始地址。而r3寄存器里面的值稍微复杂一些。下面就来分析r3的值:
_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段的起始地址。然而根据整个链接脚本来看,编译完之后的代码会包含代码段和.bss段等,而代码段之后紧接着的就是.bss段,所以.bss端的起始地址减去代码段的起始地址就可以得到代码段的大小!
上面已经算出了代码段的大小,现在就来看搬移的具体代码。
ldmia r0!, {r3-r10} /*这个是内存加载指令,会把r0代表的地址处的内存的内容加载到后
面的寄存器列表中,ia表示Increment After即先将4字节的数据搬
移到一个寄存器,然后再将内存地址和寄存器都往后跳往下一个*/
stmia r1!, {r3-r10} /*这个跟上一条语句类似,只不过变成了从寄存器到内存的搬移*/
cmp r0, r2 /*这是来判断是否重定位完成的一个判断语句*/
/* 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
在uboot的start.S中上面的这一段程序是对IRQ栈起始地址的一个初始化,这里往IRQ_STACK_START和FIQ_STACK_START地址处的内容都写上了0x0badc0de这个值,这个值可以看做是“bad code”,因为这个值现在是没有意义的,只是随意填充的一个值,真正需要设置这个值的时候会是在cpu_init(void)函数里面(这个函数的路径为:cpu\arm920t/Cpu.c)。
/* 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]
这一小段代码中,首先根据CPU的类型,定义了相应的寄存器的地址,然后使用ldr和str指令对该寄存器进行设置(这里只列出了pWTCON看门狗寄存器的设置语句)。
# define pWTCON 0x15300000 /*定义了看门狗寄存器的地址*/
ldr r0, =pWTCON /*ldr指令在使用=号的时候,是一个伪指令,会将后面标号本身的值
而不是标号所代表的地址处的内容赋给目标,所以这个语句完成的
功能就是把0x15300000这个值赋给了pWTCON*/
str r1, [r0] /*这个是把r0寄存器里面的值当做一个地址值,将r1的值赋值到这个内寸*/
#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 */
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 */
这一段代码非常有意义,是一段重定位代码的程序。在重定位之前首先会判断一下是否需要重定位,判断的方法如下:
adr r0, _start /*这是一个地址读取的位置无关指令,将_start的地址读取到r0中
如果当前的start.S被放在Flash中,那么这个得到的_start为0
而如果当前的start.S被加载到了内存中,那么这时候得到的值
将会是一个不为0的值,因为2440外接的Flash和RAM是在起始地
址不同的bank处*/
ldr r1, _TEXT_BASE /*这条指令会将_TEXT_BASE代表的地址处的值读取到r1中,然而
根据_TEXT_BASE: .word TEXT_BASE这个地址存放的值来自于
链接脚本中,代表的是其运行地址*/
后面的工作就是比较r0和r1的值,如果是相等的,那么证明这个时候代码已经位于了其应该位于的地方,这个时候是不需要重定位代码的。但是有一点使人比较迷惑的就是,现在我们分析的start.S代码是系统上电就会执行的一段代码,前面根本没有重定位的代码,代码怎么还会可能就位于其应该位于的地方?答案是这种情况发生在使用仿真器仿真的时候,这种情况下,仿真器帮我们做了重定位的工作。如果r1和r0的值相等,那么将会是一个跳转,直接跳转到刚好重定位代码结束的地方继续往下执行。
再继续往下看:
_armboot_start: .word _start
r2寄存器中存放的是_start标号的值,也就是所有代码的开始地址。而r3寄存器里面的值稍微复杂一些。下面就来分析r3的值:
_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段的起始地址。然而根据整个链接脚本来看,编译完之后的代码会包含代码段和.bss段等,而代码段之后紧接着的就是.bss段,所以.bss端的起始地址减去代码段的起始地址就可以得到代码段的大小!
上面已经算出了代码段的大小,现在就来看搬移的具体代码。
ldmia r0!, {r3-r10} /*这个是内存加载指令,会把r0代表的地址处的内存的内容加载到后
面的寄存器列表中,ia表示Increment After即先将4字节的数据搬
移到一个寄存器,然后再将内存地址和寄存器都往后跳往下一个*/
stmia r1!, {r3-r10} /*这个跟上一条语句类似,只不过变成了从寄存器到内存的搬移*/
cmp r0, r2 /*这是来判断是否重定位完成的一个判断语句*/
自己学驱动7——uboot代码阅读二(start.S)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。