Uboot 代码剖析
黄雪莉
代码重定位
编译器在编译一段程序链接过程中,要对所有目标文件进行重定位,建立符号引用规则,同时为变量,函数等分配地址。程序执行时,把代码加载到链接时指定的地址空间,以保证程序在执行过程中对变量,函数等符号的正确引用,是程序正常运行。
但是在操作系统中,一个进程通常从硬盘等二级存储设备拷贝到内存中去执行,这两者的地址是不同的,因此操作系统要对这个进程进行重定位,才能正确运行该进程。
位置不相关代码:
在
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
系统引导程序如bootloader时,一般为了提高速度,需要将bootloader从ROM拷贝到RAM中去执行,这两者的地址也不同。如果这些代码即使不在链接时指定的地址空间也能正确运行,这就是位置无关代码(position independent code)。PIC的特点是,它被加载到任意地址空间都可以正确的执行。其原理是PIC对常量和函数入口地址的操作都是基于PC+偏移量的寻址方式。即使程序被移动,但是PC也变化了,而偏移量是不变的,所以程序仍然可以找到正确的入口地址或者常量。
位置代码无关在U-boot中的实现:
U-Boot中用GOT
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
(Global Offset Table 全局偏移量表)实现PIC代码位置无关,总的来讲,U-Boot依靠维护GOT表来实现,在GOT表中存放一些全局label的表项,这些表项
记录
混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载
重要的地址; 运行在Flash时,GOT表中存放的是编译时全局label的值(地址);当U-Boot运行时检测RAM大小进行代码搬运之后, 利用代码搬运前后产生的地址偏移对(相对偏移)GOT表中的各个表项值进行更新,使其记录RAM中的相应的地址。 这样代码运行时不会出现代码/变量地址出错的问题。
主要代码剖析
1.关于GOT的主要宏定义(include/ppc_asm.tmpl)
#define START_GOT \ //定义 .got2段,GOT起始地址为 当前地址+0x8000
.section ".got2","aw"; \
.LCTOC1 = .+32768
#define END_GOT \ //got2段结束,text开始
.text
//通过GET_GOT获得.got2段的起始地址
#define GET_GOT \
bl 1f ; \
.text 2 ; \
0: .long .LCTOC1-1f ; \
.text ; \
1: mflr r14 ; \ //r14存放的是标签1的地址
lwz r0,0b-1b(r14) ; \ //r0=0b-1b+1b=0b
add r14,r0,r14 ;//r14= 0b+1b=.LCTOC1-1f +1f=.LCTOC1
//.L_NAME为相对于.got2的偏移,
//NAME为 表项NAME的存储地址
// GOT_ENTRY的真正作用是将编译时全局lable (NAME)的值存储到GOT表中
#define GOT_ENTRY(NAME) .L_ ## NAME = . - .LCTOC1 ; .long NAME
// GOT(NAME)获得NAME的存储地址
#define GOT(NAME) .L_ ## NAME (r14)
2. .got2段的声明
/*定义got2段,起始地址(.LCTOC1),段中各表项的起始地址以及相对GOT的地址*/
START_GOT
GOT_ENTRY(_GOT2_TABLE_)
GOT_ENTRY(_FIXUP_TABLE_)
GOT_ENTRY(_start)
GOT_ENTRY(_start_of_vectors)
GOT_ENTRY(_end_of_vectors)
GOT_ENTRY(transfer_to_handler)
GOT_ENTRY(__init_end)
GOT_ENTRY(_end)
GOT_ENTRY(__bss_start)
END_GOT//got2段结束
3.上电复位,从flash的起始地址读取硬件复位配置字HRCW(Hard Reset Configuration Word),每次都8位,每四次组成一个32位配置字,分别组成低32位配置字和高32位配置字,分别存放在CFG_HRCW_LOW 和CFG_HRCW_HIGH寄存器。
#define _HRCW_TABLE_ENTRY(w) \
.fill 8,1,(((w)>>24)&0xff); \
.fill 8,1,(((w)>>16)&0xff); \
.fill 8,1,(((w)>> 8)&0xff); \
.fill 8,1,(((w) )&0xff)
_HRCW_TABLE_ENTRY(CFG_HRCW_LOW)
_HRCW_TABLE_ENTRY(CFG_HRCW_HIGH)
HRCW控制时钟及其他硬件的功能,如PCI host和Agent模式,启动位置和大小端。详细配置信息见MPC8313ERM.pdf第四章第三节
4.入口启动,u-boou.lds指定程序入口,ENTRY(_start)
.globl _start
_start:
r21 < = BOOTFLAG_COLD // 从flash启动
r4<= CONFIG_DEFAULT_IMMR // =0xff40 0000,是immr base offset
r5<= machine status register
IMMRBAR(r4)<= CFG_IMMR //0xe00 0000,IMMRBAR:IMMR基地址寄存器
bl init_e300_core //初始化e300 processor core
bl map_flash_by_law1 //通过Local Access Window 将整个boot ROM进行映射,以确保在跳转之后代码可以顺利到 text base
r4<=CFG_MONITOR_BASE
bl remap_flash_by_law0
bl setup_bats//将所有的BAT(data&instruction)都设置成初始状态
sync
bl enable_addr_trans// 通过修改MSR的DR/IR使能address translation
sync
bl dcache_enable//通过修改硬件启动寄存器HID0使能data cache,同时是data cache flush操作无效
sync
bl lock_ram_in_cache//data cach中分配以RAMCFG_INIT_RAM_ADDR为起始地址,以CFG_INIT_RAM_END为结束的ram,同时对这段空间进行清零,然后锁定
sync
r1<= CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET//建立ram栈指针并将栈顶清零
GET_GOT /* initialize GOT access */
r3<=CFG_IMMR
bl cpu_init_f /* run low-level CPU init code (in Flash)*/
mr r3, r21 // r3<= BOOTFLAG_COLD
bl board_init_f // run 1st part of board init code (in Flash)
5.init_e300_core(start.S):
( 初始化机器状态寄存器; 使能机器检查中断machine check interrupt
( 跟据MSR更新 SRR1(save/restore register)参考7.7.1.3.3
( 初始化Watchdog—修改SWCRR(system watchdog control register),并重置。参考5.4.4.1
( 初始化硬件执行依赖寄存器(HID),设置HID0,详见表 7-2
( 所有的BAT置零
( 关闭所有的TLB(Translation lookaside buffer) entry
6.cpu_init_f(cpu/mpc83xx/cpu_ini.c)
( 对IMMR的一些寄存器进行设置,实现TSEC,复位寄存器,窗口映射和时钟模块等的设置。r3存放的是IMMR的值,作为cpu_int_f的传递参数(PowerPC中,r3-r10作为函数传递参数),结构类型为immap_t(定义在文件include/asm_ppc/immap_83xx.h).
( 设置对全局数据指针gd,并对其进行初始化.gd主要用来保存开发板信息、终端存在标志位、环境变量结构体起始地址、环境变量校验标志位、frame buffer 基地址等。它是指向gd_t 结构体的指针(gd_t定义在include/asm_ppc/global_data.h)。这里都初始化为0。
7.Board_init_f(lib_ppc/board.c)
( 初始化init_sequence中的各个函数
✓ board_early_init_f:基本的板级相关配置,主要包括设置处理器类型和启动参数地址
✓ get_clocks:获得并设置CPU 和BUS 时钟
✓ init_timebase:初始化计数器;
✓ env_init:设置环境变量,初始化环境;
✓ init_baudrate:指定串口的波特率;
✓ serial_init:串口初始化设置;选择通讯端口,设置串口波特率和工作方式.
✓ console_init_f:设置gd->have_console=1,表示可以使用串口通讯控制台
✓ display_banner:在控制台输出 U-boot 信息
✓ checkcpu:检查CPU 版本信息
✓ checkboard:检查board 信息
✓ init_func_i2c:初始化i2c 接口
✓ init_func_ram:初始化SDRAM
( 对gd进行设置,即对开发板信息(结构体bd_t定义在文件include/asm_ppc /u-boot.h 中)、终端存在标志位、环境变量结构体起始地址、环境变量校验标志位、frame buffer 基地址等进行赋值。
( relocate_code (addr_sp, id, addr),将代码加载到的目的地址,addr_sp是相应的栈顶指针,id为全局数据变量gd.
8.relocate_code函数定义在start.S中
.globl relocate_code
relocate_code:
r4<= CFG_MONITOR_BASE=0xFE000000,源代码地址,text_base
r5<=GOT(__init_end) –r4 , GOT获得_init_end,源代码结束地址,定义在u-boot.lds,故差是u-boot代码长度
r6<=CFG_CACHELINE_SIZE /* Cache Line Size,每次搬运32bit*/
// 更新 GOT pointer: New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE)+Destination Address
r14<= r14+r10-r4
r0<=(r5+3)/4,每次搬运32bit,即4个字节,r0为将代码copy到ram的次数
mtctr r0 //将搬运次数,存放只计数器
若r0=0,则不需搬运代码,否则需要进行代码搬运
la r8,-4(r4) //r8<=r4-4
la r7,-4(r3)
1: lwzu r0,4(r8)
stwu r0,4(r7)//代码搬运
bdnz 1b
然后在进行第二次搬运,并比较,如果发现不同,则表示搬运出错,退出
(注:当目的地址>源地址,则逆序搬运代码,而且不需要进行二次搬运和比较。不知道为什么这么做)
//Flush the cache
mr r4,r3
5: dcbst 0,r4 //有效地址EA=r4+0,将EA所在的cache行与 内存同步
add r4,r4,r6
cmplw r4,r5
blt 5b
sync /* Wait for all dcbst to complete on bus */
mr r4,r3
6: icbi 0,r4 //有效地址EA=r4+0,将EA所在的指令cache行无效
add r4,r4,r6
cmplw r4,r5
blt 6b
7: sync /* Wait for all icbi to complete on bus */
isync
/*此时 并不返回,而是跳转到board的第二部分的初始化,即in_ram(start.S), 此后代码在 RAM中运行 */
addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET //r10为目的地址
mtlr r0
blr
1. .globl relocate_code
2. relocate_code:sss
serial_init:串口初始化设置;选择通讯端口,设置串口波特率和工作方式,越早开通串口,对后面的工作越有好处;
9.in_ram(位于start.S中)
in_ram:
//r0<=GOT表项的个数in_ram:
li r0,__got2_entries@sectoff@l//sectoff段中的__got2_entries的低半字,即GOT表项的个数
la r3, GOT(_GOT2_TABLE_) //r3<=.L_GOT2_TABLE_ + r14,GOT表在ram中的起始地址
lwz r11,GOT(_GOT2_TABLE_) //r11<=.L_GOT2_TABLE_(旧) + r14,GOT表在flash中的起始地址
mtctr r0//表项个数存入计数器
sub r11,r3,r11//偏移,注:r11=r11-r3
addi r3,r3,-4
1: lwzu r0,4(r3)
add r0,r0,r11
stw r0,0(r3)//将flash中的GOT2表copy到ram
bdnz 1b
2: li r0,__fixup_entries@sectoff@l//FIXUP表项个数
lwz r3,GOT(_FIXUP_TABLE_)
cmpwi r0,0
mtctr r0
addi r3,r3,-4
beq 4f
3: lwzu r4,4(r3)
lwzux r0,r4,r11 //fixup的偏移于got2的相同,可以根据他来计算fixup的flash的起始地址
add r0,r0,r11
stw r10,0(r3)
stw r0,0(r4)//fixup表copy
bdnz 3b
4:
clear_bss:
代码略
mr r3, r9 /* Global Data pointer */
mr r4, r10 /* Destination Address */
bl board_init_r
10 .Board_init_r(lib_ppc/board.c):
调用一系列的初始化函数,初始化高速缓存器和本阶段所用到的外围硬件设备,主要调用了函数:
( watchdog_reset():复位看门狗;
( icache_enable():初始化I-Cache;
( unlock_ram_in_cache():解锁D-Cache;
( flash_init():初始化Nor Flash,使之支持写入、擦除功能;
( cpu_init_r ();对cpu的hiher level部分进行初始化
( nand_init():初始化Nand Flash;
( console_init_r():再次初始化控制台串口;
( eth_initialize():初始化以太网口;
( main_loop():检查delay时间,若在期间没有按键,则启动操作系统内核