Powerpc启动顺序分析。
摘要:本文致力于研究Powerpc的引导技术,其中包括U-boot启动代码分析,kernel for Powerpc 启动代码分析,以及U-boot加载kernel代码分析。以上三个部分属于体系结构相关的内容。
由于时间有限,只是对代码进行粗读。
一、Kernel 启动代码分析
如果由u-boot解压缩内核,则内核的入口点是arch/ppc/head.s,注意,如果使用新的bsp,则arch使用powerpc而不是ppc。Ppc主要针对32位系统,powerpc把32位系统与64位系统整合在一起。
Powerpc有四种子体系:PMAC——PowerMacintosh主要是对mac的支持;PReP主要支持IBM,motorola,我们的ppc 440,460,mpc8548应该属于这个体系;CHRP支持IBM RS/6000,Genesi;APUS Amiga Power-UP Systems (APUS)。每种架构的启动是不一样的。
head.s
1、找到_start,首先根据ABI规范保存r3~r7(保存到r27~r31)。主要是一些内核参数。此时mmu是打开的,当然物理内核和虚拟内存相同。然后跳到early_init中把bss清零,然后判断CPU类型,根据类型判断物理地址(在关掉mmu时需要定位代码所在的物理地址),最后通过r3返回物理地址。
2、Bl mmu_off 关mmu,默认启动mmu是开启的,使用cpu内部的tlb进行内存映射。
3、Bl clear_bats 清除block address table
4、BL Flush_tlbs 应该是刷新tlb//其实都是写寄存器的操作。
5、Bl initial_bats 将前16M内存映射给内核使用。
6、Bl reloc_offset 计算连接地址与实际地址的偏移
7、Call_setup_cpu 设置cpu的ctl寄存器
8、Reloc_kernel 如果有必要则reloc
9、B turn_on_mmu 这个函数通过rfi返回,将SPRN_SRR0设置成start_here地址,rfi中断返回时将跳到这个地址执行,并切换上下文。
10、 Start_here 真正的打开mmu,通过rfi跳转到start_kernel运行。
11、 Start_kernel在main.c中,后面的内容和所有架构下无异。
二、 U-boot启动代码分析
U-Boot启动代码(for ppc)主要关注如下几个文件:1、u-boot.lds,2、start.s,3、board.c。u-boot.lds是链接文件,一般在对应开发板目录下;
Start.s 是启动代码文件,一般在对应得cpu目录下;
Board.c 是板子初始化文件,一般在对应体系结构的lib下;
首先来看u-boot.lds,从这个文件可以找到整个程序的入口位置。开头代码如下:
OUTPUT_ARCH(powerpc) /*代码是for ppc的*/
/* Do we need any of these for elf
__DYNAMIC = 0; */
SECTIONS
{
.resetvec 0xFFFFFFFC : /*定义resetvec段在0xFFFFFFFC位置,这个位置是ppc上电后IP指向的位置*/
{
*(.resetvec) /*段内容*/
} = 0xffff /*用0xffff填充空余部分*/
.bootpg 0xFFFFF000 : /*定义.bootpg段在0xFFFFF000位置*/
{
cpu/ppc4xx/start.o (.bootpg)
} = 0xffff /*用0xffff填充空余部分*/
…………
现在找到resetvec.s 文件。
#include
.section .resetvec,"ax"
#if defined(CONFIG_440)
b _start_440 /*CPU上电后执行的第一条命令:b _start_440*/
#else
#if defined(CONFIG_BOOT_PCI) && defined(CONFIG_MIP405)
b _start_pci
#else
b _start
#endif
#endif
Start.S 代码分析。_start_440在start.s中定义。Ppc440启动时,只有最高位的4k地址被TLB映射,此时控制器将最高4k的内容拷贝到cache中运行(我猜想在启动时440将自己的cache作为ram来使用,这样在不初始化外部ram的时候也能跑代码)。Start.s正是位于这4k当中,因此start.s担负着初始化整个正常环境的重任。接下来:
1) 设置中断控制器寄存器
2) 设置调试寄存器
3) 设置cpu控制器寄存器
4) 安装中断向量
5) 配置cache
6) 初始化mmu控制器,在启动时使用I/D cache控制器中的tlb实现地址映射。
7) 删除所有的tlb entries
8) 建立新的tlb。包括16M启动flash,内存,PCI存储空间,BCSR空间等等。
9) B _start 跳转到_start。
10) 此时需要建立新的工作环境,因此首先需要disable异常和中断。
11) 设置intenal ram
12) 设置电源管理
13) 设置堆栈 (到这里为止代码应该是在cache中)
14) bl cpu_init_f(board.c) 跳到Flash中执行cpu初始化。此时内存并没有完全初始化,堆栈空间很小,BSS也没有初始化。这段代码主要是打开一个console以便于调试,以及初始化RAM。以便把代码全部搬到RAM中运行。还有一个重要工作就是获取全局数据——gd。
Cpu_init_f:
(1) gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET);全局gd指针。__asm__ __volatile__("": : :"memory"); 内存壁垒,防止编译器优化。清空gd。
(2) for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr) () != 0) {
hang ();
}
}/*cpu/board /console… 初始化*/
(3) 设置gd
(4) 初始化sram
(5) 计算需要保护的内存区域(board infor,kernel log buf ,monitor code等等),addr即为除去保护内存后用于存放relocate code的地址,addr_sp为堆栈可以使用的基地址
(6) 调用relocate_code (start.s文件中) ,r3 = a