自己学驱动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 /*这是来判断是否重定位完成的一个判断语句*/

时间: 2025-01-02 18:03:26

自己学驱动7——uboot代码阅读二(start.S)的相关文章

自己学驱动8——uboot代码阅读三(start.S)

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

自己学驱动9——uboot代码阅读四(start_armboot函数)

前面分析过在start.S中执行完相关的一些操作之后,会跳转到C语言的部分来执行,跳转到的目标位置就是start_armboot函数,所以现在来看一下这个函数完成了一些什么工作.在这个函数的第一行定义了一个变量如下: init_fnc_t **init_fnc_ptr; 通过查找uboot源码可以得到下面的类型重定义: typedef int (init_fnc_t) (void); typedef的使用方法非常灵活,这里这种定义方式就是定义了init_fnc_t代表一个接收参数为void类型返

《代码阅读方法与实践》阅读笔记之二

时间过得真快,一转眼,10天就过去了,感觉上次写阅读笔记的场景仿佛还历历在目.<代码阅读方法与实践>这本书真的很难写笔记,本来我看这本书的名字还以为书里大概写的都是些代码阅读的简易方法,心想着这就好写笔记了,没想到竟然好多都是我们之前学过的东西,这倒让我有点无从下手了.大概像我们这些还没有太多经历的大学生,总是习惯于尽量避免自己的工作量,总是试图找到一些完成事情的捷径吧.总之,尽管我不想承认,但我自己心里很清楚,我就是这种人.下面开始言归正传,说说接下来的几章内容归纳. 这本书在前面已经分析了

梦断代码阅读笔记有感之二

08梦断代码阅读笔记有感之二 在梦断代码的一开始我们就学会了如何去写代码,如何成功的去做一个软件工程师. 在现在人的严重,也许软件工程师写出的代码只是让人在玩游戏,在用一些简单的用代码写出的软件.只是认为工程师在不断地重复着一个动作:写代码.但我只能说你们大错特错,就像在文章中说的那样,其实软件工程师是在:“改变世界”,他们利用他们的手用键盘在电脑上打出一行一行的代码,程序产生了,一个新的软件也就产生了.而且,众所周知的,工程师做一个软件,总是在无限制的更新他的内容,让我们的软件更加的先进化,就

uboot移植准备工作二

第一部分 2.3.1uboot配置编译实践 1)源头的源代码时uboot官网下载的.这个下载的源代码可能没有你当前使用的开发板的移植,甚至找不到当前开发板使用的SoC对应的移植版本. 2)SoC厂商在退出一款SoC后,厂商的工程师会去uboot官网下载一个uboot,根据自己Soc进行第一步移植,移植的目标是厂商推出的开发板.(譬如三星的S5PV210芯片厂商出的开发板就叫SMDKV210),所以三星的工程师移植的uboot是根据他们自己的SMDKV210开发板移植的. 3)具体的开发板提供商(

嵌入式linux开发uboot移植(二)——uboot工程源码目录分析

嵌入式linux开发uboot移植(二)--uboot工程源码目录分析 本文分析的uboot为uboot_smdkv210,是三星官方发布的基于S5PV210评估开发板对应的uboot. 一.uboot源码目录结构解析 1.cpu 本文件夹下的子文件与处理器相关,每个文件夹代表一种CPU系列.每个子目录中都包括cpu.c.interrupts.c.start.S文件. cpu.c主要用于初始化CPU.设置指令Cache和数据Cache等 interrupt.c主要用于设置系统的各种中断和异常 s

程序代码阅读技巧

一.代码阅读的必要性 阅读别人的代码作为研发人员是一件经常要做的事情.一个是学习新的编程语言的时候通过阅读别人的代码是个最佳的学习方法,另外是积累编程经验.如果你有机 会阅读一些操作系统的代码会帮助你理解一些基本的原理.更有就是在你作为一个质量确保人员或一个小领导的时候如果你要做白盒测试的时候没有阅读代码的能力 是不能完成相应的任务.最后一个就是如果你中途接手一个项目的时候或给一个项目做售后服务的时候是要有阅读代码的能力的. 二.收集所有可能收集的材料 阅读代码要做的第一件事情是收集所有和项目相

[Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串

本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsunlei/javascri

代码阅读方法与实践(三)

我们分析的许多系统都遵循一种简单的“主程序和子例程”结构.常见的.重要的结构可以归类为少数迥然相异的构架类型:集中式储存库.数据流.面向对象或分层构架.这些构架类型常常结合成一个层次结构用来控制大型系统的复杂性.接下来我们将独立的分析每种构架类型,但是一个系统可以同时展示出多种不同的构架类型.以不同的方式检查同一个系统.分许系统的不同部分.或使用不同级别的分解,都有可能发现不同的架构类型. 集中式储存库的构架模型依赖于一个中心过程或数据结构,他在系统中担任控制或信息的集线器.即使不需要数据库提供