做了近两年arm下的驱动开发,常用的各个设备驱动基本都碰过,不过boot由于任务安排的缘故(公司一直有专人在做),一直没有机会接触,从刚开始接触嵌入式的时候,就一直想弄清楚板子上电后,程序是怎么执行的,不过看了下公司boot源码,就很快放弃了,当时对汇编充满了畏惧,做了1年多的驱动后,再看汇编感觉就没那边痛苦了,最近把boot的资料整理下,把我觉得boot比较核心的部分,完整的看了一遍,现在做个记号。我把我觉得我之前比较困惑的难点整理出来,也许大家一起讨论下,也许和我一样的新手就可以少走些弯路。
boot的核心就是relocate,目前见到的典型嵌入式系统,除了处理器,至少都有rom(norflash,nandflash)ram(sdram),一般把bootloader**放在norflash里面,而nandflash因为本身硬件原因不能随机访问,一般只是用来放应用程序。在系统加电或复位后,cpu通常由cpu制造商预先安排上地址取指令,arm体系下一般都是0x0地址取它的第一条指令,即pc = 0开始。
和boot紧密相关的个人觉得就是一下几点。
remap比较简单,和mmu的功能可以看做是等价的,只是一般remap地址估定为0x0 ,网上有个帖子叫<文中提到了arm处理器remap的三种情况,如下
1)如果处理器有专门的寄存器可以完成remap。那么remap是通过remap寄存器的相应bit置1完成的。
如atmel at91xx
2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank的起始地址,那么只要把ram的起始地址编程为0x0,也可以完成remap。如samsung s3c4510 .
3)如果上面两种机制都没有,那么remap就不要做了。因为处理器实现决定了sdram对应的bank地址是不能改变的。如samsung s3c2410.
不过我的看法有点稍微不一样,如果上面两种机制都没有,那么remap就不要做了,它给的典型例子是samsung s3c2410 ,2410虽然sdram对应的bank地址不能改变,但它有mmu功能, mmu可以起到remap的作用,常用的最典型的应该是例子samsung s3c44b0,它既没有mmu,又sdram对应地址有没办法改变。顺便补充下除了4510可以改变每个bank的地址,还有华邦的w90p740(arm7),呵呵,我现在用的u就是这款u,可以把bank的地址随意的设置。
relocate (地址重定位),个人觉得这个是boot里面最麻烦也是最核心的部分,刚开始看boot**的时候,它简直是我的恶梦,不知道大家分析boot的源码流程是否这样,也可能我大学不是计算机的,没学过编译原理(现在也没看过)对链接和加载一无所知,有两个星期非常痛苦,就是不懂人家boot里面的链接脚本为什么要那样写。网上关于uboot的帖子很多,但对链接加载这块,始终写的不详细,不知道是不是太过于基础了,高手都不愿意讲,最后自己找资料,发现其实一切痛苦的根源都是对链接和加载不太清楚造成的,但个人感觉boot除了初始化以外就是搬运程序,如何搬运?为什么要那样搬运都需要对硬件板的地址分布很清楚?
而这些都是链接决定的,所以非弄清楚不可!
1.我们为什么需要relocate ? 经济方面,(nandflash和norflash 每兆**相差悬殊),把boot**放在norflash里面(为什么不放在nandflash里面,因为nandflash读需要驱动支持,norflash可以直接访问),boot通常很小,只需要占用几十k的空间,所以只需要很小的norflash芯片,这样很便宜,而把应用程序通常很大,所以用**低廉nandflash来储存,实际应用,通过执行boot程序,把nandflash里面**和数据搬运到内存中来执行,这样比程序直接放在norflash里执行,可以。
另外还有运行速度方面的差别,程序在norflash里执行的速度远远小于在sdram中执行的速度,为了追求更高的速度,也需要relocate,让程序在sdram里面执行 .
2.关于加载域(vma)和运行域(lma),杜春雷在它那本经典的<_armboot.因为在bin映象生成的时候,就已经把_start_armboot这个符号,和实际地址绑定在一起,当我们执行ldr pc,_start_armboot 程序就从在rom中执行跳入到ram中了,但前提是我们进行了**搬移,如果没有**搬运ldr pc,_start_armboot,ram中没有**程序就马上飞掉了,所有我们在在搬运之前不能寻址绝对地址有关**,必须执行**地址无关。
拿u-boot-1.1.4下的smdk2410来做例子,和smdk2410 board密切相关的就两个文件夹\board\smdk2410和\cpu\arm920t,里面核心文件就 ,
entry(_start)
sections0x00000000;//从0地址起始
= align(4);
text :
cpu/arm920t/ (text)
(.textalign(4);
rodata :
_u_boot_cmd_endalign(4);
_bss_start = 为搬运**提供的符号,来标明bss段地址,方便relocate
bss :
end = 定义整个image的结束地址
是链接脚本文件, 我刚开始看这个链接脚本文件时,我疑惑很久,不明白lds中vma= lma(资料上很多链接脚本包括我们公司项目里面自己写的lds脚本是通过at命令设置过lma,这样看起来地址空间分配更清晰),而且整个image 的vma按照lds为基址为0x0,而2410芯片不能remap,0x0地址是rom的区域,不是运行时ram的地址,我的理解是**段地址应该是指向该硬件板内存区域,设置 .text=text_base 而不是lds中的。text=0x0 ,这个疑点弄的我当时很郁闷,想了很久也没想没有搞清楚u-boot这样链接脚本都能让boot跑起来,当我把编译出来的bin烧到norflash中,uboot居然跑起来了,同时发现了一个问题, 中发现 .
text 是从 定义text_base =0x33f80000 ,而不是lds设置的0x0,这又让我吃惊,没清楚是怎么会事,手上有介绍移植uboot的资料,但都对uboot链接这部分,写的不够详细,知道事文件搞的鬼,但把makefile文件看了几遍都没找不到是怎么回事(还是对makefile不熟啊!),最后把编译uboot的过程看了隐藏了个机关是
arm-linux-ld –tu-boot-1.1.4\board\smdk2410\ –ttext 0x33f80000
arm-linux-objcopy --gap-fill =0xff –o binary uboot
不知道uboot设计者为什么要在这里加一个–ttext 而不是在lds就设置?而很多移植uboot的资料对lds文件都有所描述,但这个重要的细节似乎都漏掉了,不知道是不是因为太基础了,所以没有讲。
不过最后生成的bin 从上看arm-linux-objcopy --gap-fill =0xff –o binary uboot 没有对链接生成的elf文件进行重定位,因此它的运行地址是 定义text_base为基地址,顺序按照lds的顺序依次增加的,所以整个uboot最初运行的流程是
start reset ᠖
4; cpu_init_crit relocate
这个部分就是完成初始化,设svc32,关看门狗,关中断,设置时钟,初始化sdram(为**搬运到sdram做准备),这些都很简单
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!, copy from source address [r0] *
stmia r1!, copy to target address [r1] *
cmp r0, r2 /*until source end addreee [r2] *
ble copy_loop
看了下网上的帖子,adr指令,网上很多人被这这个指令弄郁闷,我看杜春雷的<方面的资料感觉太少了,没人可以指点,自己看这部分资料看的很痛苦。
cpu核相关初始化】 watchdog初始化】 gpio初始化】 系统时钟初始化】 内存初始化】 模式初始化】 中断向量初始化】 mmu初始化】 cache初始化】 总线初始化】 语言相关初始化】 设备相关初始化】
格式和bin格式
executable and linking format (elf)重定位,可以参与程序的链接(创建一个程序)和程序的执行(运行一个程序) ,主要链接,和执行,但介绍elf文件的资料很多,没时间仔细看和实际密切的就是调试程序时候都用elf格式调试,因为它包含了调试所需的各种符号, 固化的时候都是用的bin格式,是可执行映象,用objcopy 把elf 转换成bin ,不过网上介绍bin格式的资料很少,只是知道bin程序,只要把pc设置为bin映象的入口地址,就可以正确执行, objcopy 可以对elf 转换成bin再进行地址重定位,不过目前还没看见过这么干过,对于elf,和bin这些理解的都不系统,资料也很少,工作中,集成开发工具ide又把这些设置都给屏蔽起来,有没有那个强人能写一个文档,把这些都系统的讲清楚就好了!
顺便问下,论坛上上海的多不多,大家找工作都是在网上找的?有个mm拉我去上海,虽然对现在工作很满意,不过mm比工作更重要,要我做选择,只有去上海了,不过在51job上投了点简历,都石沉大海,按理说2年也不短了,至少也会冒一个泡的,有没有上海的能够指点下,你们在上海石怎么找相关工作的?
补充一个当时找资料看见对网上一个帖子,感觉写的很精辟的,关于地址无关的解释,网页地址被改成相当路径了,就没办法地址粘贴出来,现在把原文粘贴出来。
关键词: 地址无关术语地址无关: 编译地址不等于运行地址。
地址相关: 编译地址等于运行地址。
常见的一些boot(如, u-boot, vivi)和linux kernel**开始的一段是位置无关的, 意思就是说运行地址与编译地址无关。 如, kernel编译地址是0xc0008000, 而运行地址是0x30008000.
为什么? 为什么**的编译地址和运行地址会不相等呢? 原因主要有以下几种:
1) 对于boot, 用于存放boot**的存储器容量小于**量。 如, boot片有4k, 而**通常有50-60k. 这样, 通常会在前4k**里, 让boot把自己复制到ram, 再接着运行。
这里我们需要作出一个选择, 是让前面的**与地址相关, 还是让后面的**与地址相关呢? 显然我们会选择前面一段**量小的与地址无关。 2) 对于linux kernel, 它是运行在虚拟地址空间的, 如0xc0008000, 但在mmu打开之前, 通常这个地址是不存在的, 也就是说在mmu打开之前, kernel的**必须是地址无关的。
怎么办? 对。
位置无关的**, 寻址是基于pc值的, 在pc值上+/-一个偏移值, 得到运行地址。以arm为例, 用adr来寻址, adr的实际上是一个宏指令, 在**编译时, 会被编译器替换成对pc的+/-运算这里要注意, 对pc的+/-运行显然是有一个地址范围的, 所以我们在上面选择**量小的地址无关, 是很明智的。
而访问地址相关的**, 只需要使用其它的寻址指令就行了。 但在这之前, 必须保证**被放在正确的地址上, 所以通常都会有一个复制**的过程, 然后就是跳转到一个标号, 地址相关**就开始运行了。
阅读笔记格式阅读笔记格式
阅读笔记格式 阅读笔记格式4篇。读书笔记一则。文。题。每个人都是一扇窗。作。者。田。野。语文知识积累。好词。碧野飞花。伤天暮景。惨淡愁云。和风徐徐。春意盎然。热情洋溢。好句。和不同的人交往,就像打开一扇不同的窗子。然而,就像蜜蜂永远迷恋花丛,就像花儿永远期待春光。我们每个人都希望结识一个优秀的朋友,...
英语阅读笔记
考研英语阅读笔记。第一,扫描提干,划关键项。第二,通读全文,抓住中心。1.通读全文,抓两个重点 首段 中心句 核心概念常在第一段,常在首段出题 其他各段的段首和段尾句。其他部分略读,有重点的读 2.抓住中心,用一分半时间思考3个问题 文章叙述的主要内容是什么?文章中有无提到核心概念?作者的大致态度是...
阅读笔记格式
判断 文章结构类型,给出判断方法。标记 3.态度。4.事物之间的关系。5.事物的缺陷。句。7.列举。8.强对比。9.专有名词首次出现的句子。10.与文章密切相关的词汇。详读 1.标记的地方。2.让步句 强调句后面的转折。3.出现be driven to,purpose of等固定短语的句子。4.特殊...