自己学驱动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                        */

#ifdef CONFIG_USE_IRQ

sub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

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

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段。从代码中也可以看出,代码段是在高地址处,代码段前面是堆(也就是给动态内存分配预留的空间)。CFG_GBL_DATA_SIZE在config文件中定义,start.S中会根据这个值分配栈空间用于保存board的有关信息。bdinfo之前的就是IRQ和FIQ的栈空间,当然这个是否分配是取决于是否定义了CONFIG_USE_IRQ这个宏。后面紧跟着的一条语句

sub    sp, r0, #1

这里没有直接将r0的值赋值给栈指针sp,而是将r0-12的值赋值给了sp,当然必须清楚的一点是这里的栈是向下增长的,也就是相当于sp会从当前的地址往地址更小的地方生长。这里留出来的这多余的12个字节是为了给FIQ或者IRQ中断栈预留的空间(防止其栈溢出而破坏sp栈中的数据)。

后面的代码是用了一段循环来将.bss段全部清零,因为逻辑比较清晰,也就不再赘述了。

ldr    pc, _start_armboot

_start_armboot:    .word start_armboot

这是比较关键的一个地方,因为程序执行到这里,将会跳转到到C语言部分去执行。这个start_armboot函数的路径为lib_arm\Board.c。

cpu_init_crit:

/*

* flush v4 I/D caches

*/

mov    r0, #0

mcr    p15, 0, r0, c7, c7, 0    /* flush v3/v4 cache */

mcr    p15, 0, r0, c8, c7, 0    /* flush v4 TLB */

/*

* disable MMU stuff and caches

*/

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

* 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

#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

程序mov    r0, #0开始的这三行刷新cache和TLB。cache是一种高速缓存存储器,用于保存CPU频繁使用的数据。在使用Cache技术的处理器上,当一条指令要访问内存的数据时,首先查询cache缓存中是否有数据以及数据是否过期,如果数据未过期则从cache读出数据。处理器会定期回写cache中的数据到内存。根据程序的局部性原理,使用cache后可以大大加快处理器访问内存数据的速度。

TLB的作用是在处理器访问内存数据的时候做地址转换。TLB的全称是Translation Lookaside Buffer,可以翻译做旁路缓冲。TLB中存放了一些页表文件,文件中记录了虚拟地址和物理地址的映射关系。当应用程序访问一个虚拟地址的时候,会从TLB中查询出对应的物理地址,然后访问物理地址。TLB通常是一个分层结构,使用与Cache类似的原理。处理器使用一定的算法把最常用的页表放在最先访问的层次。提示:ARM处理器Cache和TLB的配置寄存器可以参考ARM体系结构手册。

程序以mrc    p15, 0, r0, c1, c0, 0开始的这6行关闭MMU。MMU是内存管理单元(Memory Management Unit)的缩写。在现代计算机体系结构上,MMU被广泛应用。使用MMU技术可以向应用程序提供一个巨大的虚拟地址空间。在U-Boot初始化的时候,程序看到的地址都是物理地址,无须使用MMU。

程序bl    lowlevel_init跳转到lowlevel_init标号,执行与开发板相关的初始化配置。需要注意的是bl是一个会返回的跳转,程序执行完与开发板相关的操作之后会跳转回来继续往下执行。

mov    ip, lr

bl    lowlevel_init

mov    lr, ip

mov    pc, lr

ip是r12寄存器的别名,它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复r12。

r14是链接寄存器lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复。

这一段代码需要说明的是,相当于在子程序内部还要调用一个子程序,但是这里是汇编代码,第一次被调用的子程序(也就是cpu_init_crit这个标号)时返回地址被硬件自动的存储在了lr寄存器中,但是当在cpu_init_crit还要调用一个子程序的时候这是lr会被重新赋值,那么以前的lr的值就丢了!所以程序在这里用了ip来保存lr最开始的值,子程序内部的子程序调用结束后再将其复原回来,这样通过mov pc, lr就可以返回最初的调用点后面继续执行了。

#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

这一部分宏看起来很让人迷惑,感觉像是定义的地址。其实这就是pt_regs结构体的偏移值,该结构体定义在include\asm-arm\proc-armv\ptrace.h中。

/*

* 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

这一部分是中断处理的中间部分,这里会完成保存中断现场并跳转到中断处理函数的功能,使用了宏代码段来使这部分的逻辑更为清晰。

时间: 2024-10-25 09:50:14

自己学驱动8——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的s

自己学驱动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类型返

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

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

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

之前已经看完了<代码阅读方法与实践>的前六章,基本上也就是看得比较粗略,没有很精细的阅读,上节课听到老师说的“学术交流会”还是很紧张的,挺害怕被问到问题,结果回答不出来可怎么办啊,不仅丢人,分也送给别人了啊,这可怎么破啊.所以呢,我打算近期再看一遍,不管有没有用,算是给自己加点自信吧. 第七章,讲的是编程规范和约定,主要就是文件的命名及组织.缩进.编排.命名约定.编程实践.过程规范之类的,其实这一章也不用我做过多的介绍,因为大家应该都有听各科老师讲过好几遍了,道理大家都懂,但是大家除了在理论上

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击

实验三:按键模块② - 点击与长点击 实验二我们学过按键功能模块的基础内容,其中我们知道按键功能模块有如下操作: l 电平变化检测: l 过滤抖动: l 产生有效按键. 实验三我们也会z执行同样的事情,不过却是产生不一样的有效按键: l 按下有效(点击): l 长按下有效(长点击). 图3.1 按下有效,时序示意图. 图3.2 长按下有效,时序示意图. 如图3.1所示,按下有效既是"点击",当按键被按下并且消抖完毕以后,isSClick信号就有被拉高一个时钟(Short Click).

程序代码阅读技巧

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

Python3练习题系列(08)——代码阅读方法及字典跳转表理解

问题:分析下面代码 cities['_find'] = find_city city_found = cities['_find'](cities, state) 分析过程: 一个函数也可以作为一个变量,def find_city比如这一句创建了一个你可以在任何地方都能使用的变量.在这段代码里,我们首先把函数find_city放到叫做cities的字典中,并将其标记为'_find'. 第二行代码可以分解成如下步骤: 1. Python 看到city_found = 于是知道了需要创建一个变量.

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

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

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

阅读代码是程序员的基本技能,同时也是软件开发.维护.演进.审查和重用过程中不可或缺的组成部分.<代码阅读方法与实践之读书笔记之一>这本书围绕代码阅读,详细论述了相关的知识与技能.我希望通过仔细阅读并学习本书,可以快速地提高我的代码阅读的技能与技巧,进而从现有的优秀代码.算法.构架.设计中汲取营养,提高自身的开发与设计能力.此次读了此书的前四章,以下是我从中汲取到的宝贵养分: 从第一章<导论>一节中我体会到了我们要养成一个经常花时间阅读别人编写的高品质代码的习惯,因为阅读高品质的代码