bootloader中最难懂的就是代码的重定向,各种教程都是用的arm-linux工具链或者老套的ads来讲解,我通过对重定向在mdk上的实现,搞懂了很多之前的困惑。
想明白重定向,首先要明白程序有两种状态:存储静止状态、加载运行状态(就是执行不执行的区别)。
mdk的scatter文件(分散加载文件)可以设置两种程序地址:
加载地址和运行地址(
arm-linux工具链的链接地址)。
其
中加载地址就是程序在存储静止状态的地址,这个地址指示mdk自身的download功能把程序烧到什么位置。运行地址说明了把程序加载之后放在哪里运
行。而且arm映像文件(就是编译好的程序)的入口点所在的域必须是固定域(加载地址等于运行地址),程序刚开始不可能瞬间完成代码从加载地址复制到运行
地址所以只能是固定域,这一点要想明白。
程序运行时也有两个地址:
相对地址,绝对地址。相对地址就是相对于当前正在——取址的程序的地址——的地址,绝对地址就是在运行地址范围内的真实地址。
这两个地址主要和程序跳转有关,和相对地址有关的BL、adr,和绝对地址有关的ldr。
想要实现代码的重定向一定要理解映像文件的组成(域、段)、各种地址之间的关系。我实现的代码的重定向就是从上电的在flash中运行重定位到sdram中,通过led灯的变化目测程序运行
最少快了五倍。
PRESERVE8
AREA RESET,CODE,READONLY
ARM
IMPORT main
START
SDRAM_BASE EQU 0x30000000
B Reset_Addr
Reset_Addr
ADR R0, START ;复制代码到sdram
LDR R1, =SDRAM_BASE
CMP R0, R1
BEQ STACKSET
MOV R2, #0x1000
LOOP
LDMIA R0!,{R3-R6}
STMIA R1!,{R3-R6}
SUBS R2, #1
BNE LOOP
STACKSET
MSR CPSR_C, #0xd3
LDR SP, =0x34000000 ;set supervisor_sp
LDR LR, =HALT
LDR PC, =main
HALT
B HALT
END
重定位部分的汇编代码如上,异常向量表的第一条reset(reset也是一种异常) 指令只能用相对跳转,绝对跳转就去运行地址的头地址了,在这个时候别说头地址里什么都没有,连头地址指向的sdram都没有初始化无法访问。
完成代码重定向之前只能用相对跳转。
ADR R0, START;
LDR R1, =SDRAM_BASE;
CMP
R0, R1
可以判断程序运行在flash还是sdram。这段代码在 LDR PC,
=main之前都在flash中运行,之后就在sdram中运行了 。
为了便于在mdk中进行硬件调试,将加载地址和运行地址都设
为adram的头地址0x30000000,以这段代码作为启动代码的一部分的程序肯定不能用mdk的自带下载功能,选择用jlink下载程序到0地址。
当然也可以选择加载地址和运行地址都设为0是用mdk下载程序,跳转到sdram用ldr pc
0x30000000,但是这之后就没有办法用标号完成大于32m的跳转了(如果这句话不明白为什么就说明关于地址这个问题还有很多盲点)。第三种方法是
单独写一个程序完成重定向其加载和运行地址为0,被重定位的程序加载地址紧接着上一个地址,运行地址为sdram(scatter文件需要两个域)。三种
方法13都可以,但3在编程时要考量的细节多容易出错,最不好的是不能进行硬件调试,所以第一种方法比较好(第一种方法程序在没跳转前其实相当于骗程序在
flash中运行)。
有了以上积累就可以在mdk中自己编写bootloader了
flash中0x60000开始存放linux
uimage镜像文件,抛去前64字节的头信息,内核实际加载地址为0x60040.把flash中0x60000开始的0x500000大小的数据
(uimage镜像为2.2M,读5m是为了取个整数)读到sdram0x30008000开始的空间,从0x30008040开始执行。
大体上的思路很好理解,难点为驱动flash,设置内核加载信息供内核读取。加载信息按照内核要求的数据结构存放在连续空间,引导内核时把首地址传递给内核,内核就自动按照双方约定好的数据结构去找信息。
收获:彻底吃透arm最小系统架构。驱动flash过程中,按照K9F2G08U0C-SCB0数据手册和s3c2440数据手册进行配置,培养了自主开发的能力
moved.rar(scatter文件和ini文件是配合硬件调试的)
阅读(1835) | 评论(0) | 转发(0) |