1. U-Boot工作过程
U-Boot启动内核的过程可以分为两个Stage,两个Stage的功能如下:
(1)Stage1的功能
硬件设备初始化
加载U-Boot第二阶段代码到RAM空间
设置好栈
跳转到第二阶段代码入口
(2)Stage2的功能
初始化本阶段使用的硬件设备
检测系统内存映射
将内核从Flash读取到RAM中
为内核设置启动参数
调用内核
依赖于CPU体系结构的代码常放在Stage1且常用汇编语言实现,在u-boot中功能代码集中在cpu/ppc4xx/start.S中。
Stage2则用于实现复杂的应用,用C也有更好的可读性和移植性,主要功能代码集中在lib_ppc/board.c中,通过指定一系列的初始化函数表,实现对系统的初始化工作。
一般情况下,u-boot编译后的程序不超过100k,且Stage1的代码编译后的大小一般不超过10k,。
U-Boot启动代码(for ppc)主要关注如下几个文件u-boot.lds、start.s、board.c。
u-boot.lds是链接文件,在arch/powerpc/cpu/ppc4xx目录下;
Start.s是启动代码文件,在arch/powerpc/cpu/ppc4xx目录下;
Board.c 是板子初始化文件,一般在对应体系结构的lib下;
2. u-boot.lds
#include "config.h" /* CONFIG_BOARDDIR */
#ifndef RESET_VECTOR_ADDRESS
#ifdef CONFIG_RESET_VECTOR_ADDRESS
#define RESET_VECTOR_ADDRESS CONFIG_RESET_VECTOR_ADDRESS
#else
#define RESET_VECTOR_ADDRESS 0xfffffffc
#endif
#endif
OUTPUT_ARCH(powerpc)
PHDRS
{
text PT_LOAD;
bss PT_LOAD;
}
SECTIONS
{
/* Read-only sections, merged into text segment: */
. = + SIZEOF_HEADERS;
.text :
{
*(.text*)
} :text
_etext = .;
PROVIDE (etext = .);
.rodata :
{
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
} :text
/* Read-write section, merged into data segment: */
. = (. + 0x00FF) & 0xFFFFFF00;
_erotext = .;
PROVIDE (erotext = .);
.reloc :
{
_GOT2_TABLE_ = .;
KEEP(*(.got2))
KEEP(*(.got))
PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4);
_FIXUP_TABLE_ = .;
KEEP(*(.fixup))
}
__got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1;
__fixup_entries = (. - _FIXUP_TABLE_) >> 2;
.data :
{
*(.data*)
*(.sdata*)
}
_edata = .;
PROVIDE (edata = .);
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = .;
__start___ex_table = .;
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
. = ALIGN(256);
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
. = ALIGN(256);
__init_end = .;
#ifdef CONFIG_440
.bootpg RESET_VECTOR_ADDRESS - 0xffc : /*定义.bootpg段在RESET_VECTOR_ADDRESS - 0xffc位置*/
{
arch/powerpc/cpu/ppc4xx/start.o (.bootpg)/*.bootpg段包含的第一个程序*/
/*
* PPC440 board need a board specific object with the
* TLB definitions. This needs to get included right after
* start.o, since the first shadow TLB only covers 4k
* of address space.
*/
#ifdef CONFIG_INIT_TLB /*旁路转换缓冲(Translation lookaside buffer, TLB)*/
CONFIG_INIT_TLB (.bootpg)
#else
CONFIG_BOARDDIR/init.o (.bootpg)/*.bootpg段包含的第二个程序,不是所有开发板都需要*/
#endif
} :text = 0xffff /*用0xffff填充空余部分*/
#endif
.resetvec RESET_VECTOR_ADDRESS : /*定义resetvec段在RESET_VECTOR_ADDRESS位置,这个位置是ppc上电后IP指向的位置*/
{
KEEP(*(.resetvec)) /*段内容*/
} :text = 0xffff /*用0xffff填充空余部分*/
. = RESET_VECTOR_ADDRESS + 0x4;
/*
* Make sure that the bss segment isn't linked at 0x0, otherwise its
* address won't be updated during relocation fixups. Note that
* this is a temporary fix. Code to dynamically the fixup the bss
* location will be added in the future. When the bss relocation
* fixup code is present this workaround should be removed.
*/
#if (RESET_VECTOR_ADDRESS == 0xfffffffc)
. |= 0x10;
#endif
__bss_start = .;
.bss (NOLOAD) :
{
*(.bss*)
*(.sbss*)
*(COMMON)
} :bss
. = ALIGN(4);
__bss_end__ = . ;
PROVIDE (end = .);
}
下面来看看resetvec.S文件,位于arch\powerpc\cpu\ppc4xx目录下
/* Copyright MontaVista Software Incorporated, 2000 */
#include <config.h>
.section .resetvec,"ax"
#if defined(CONFIG_440)
b _start_440 /*440系列CPU上电后执行的第一条命令:b _start_440*/
#else
#if defined(CONFIG_BOOT_PCI) && defined(CONFIG_MIP405)
b _start_pci
#else
b _start /*405 CPU上电后执行的第一条命令:b _start*/
#endif
#endif
编译器根据lds文件来选择程序入口。
3. start.s分析
ppc440启动时,只有最高位的4k地址被TLB映射,此时控制器将最高4k的内容拷贝到cache中运行(可能在启动时440将自己的cache作为ram来使用,这样在不初始化外部ram的时候也能跑代码)。Start.s正是位于这4k当中,因此start.s担负着初始化整个正常环境的重任。
流程如下:
Stage1:
1) Core bug fix. Clear the esr
Clear and set up some registers
CCR0 init
2) Initialize debug 设置调试寄存器
3) 设置cpu控制器寄存器
4) Setup interrupt vectors 安装中断向量
5) Configure cache regions 配置cache
6) Initialize MMUCR[STID] = 0 初始化mmu控制器,在启动时使用I/D cache控制器中的tlb实现地址映射。
7) Clear all TLB entries -- TID = 0, TS = 0 删除所有的tlb entries
8) TLB entry setup -- step thru tlbtab 建立新的tlb。包括16M启动flash,内存,PCI存储空间,BCSR空间等等。
9) Continue from 'normal' start B _start跳转到_start。
10) Clear and set up some register 此时需要建立新的工作环境,因此首先需要disable异常和中断。
11) Setup the internal SRAM 设置intenal ram
12) 设置电源管理
13) Setup the stack in internal SRAM 设置堆栈 (到这里为止代码应该是在cache中)
14) bl cpu_init_f/* run low-level CPU init code (from Flash) */ cpu/ppc4xx/cpu_init.c
15) bl board_init_f arch/powerpc/lib/board.c
(1)/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET);全局gd指针
/* compiler optimization barrier needed for GCC >= 3.4 */
__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)reserve protected RAM
计算需要保护的内存区域(board infor,kernel log buf ,monitor code等等),addr即为除去保护内存后用于存放relocate code的地址,addr_sp为堆栈可以使用的基地址
(5)reserve memory for malloc() arena
(6)调用relocate_code (start.s文件中)
/*
* void relocate_code (addr_sp, gd, addr_moni)
*
* This "function" does not return, instead it continues in RAM
* after relocating the monitor code.
*
* r3 = Relocated stack pointer
* r4 = Relocated global data pointer
* r5 = Relocated text pointer
*/
Relocate_code:
(1)使能I/D cache
(2)使用新的stack:r1=addr_sp。将gd(global data)保存在 r9中,将relocate code地址保存在r10中
mr r1, r3/* Set new stack pointer */
mr r9, r4/* Save copy of Init Data pointer */
mr r10, r5/* Save copy of Destination Address */
(3)重定位GOT
* Fix GOT pointer:
* New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address
(4)Relocate code
* Now relocate code
(5)Flush D cache
/*
* Now flush the cache: note that we must start from a cache aligned
* address. Otherwise we might miss one cache line.
*/
(6)跳转到in_ram代码中,r10中保存着RAM中代码起始位置,加上in_ram的相对位置即为in_ram在ram中的位置:
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
addi r0, r10, in_ram - _start + _START_OFFSET
mtlr r0
blr /* NEVER RETURNS! */
in_ram:
(1)重定位GOT2
* Adjust got2 pointers, no need to check for 0, this code
* already puts a few entries in the table
(2)fixups
* Now adjust the fixups and the pointers to the fixups
* in case we need to move ourselves again.
(3)Clear bss
clear_bss:
/*
* Now clear BSS segment
*/
(4)将gd以及代码在内存中的起始位置作为参数,调用board_init_r
mr r3, r9/* Init Data pointer */
mr r4, r10/* Destination Address */
bl board_init_r
Stage2:
下面再次进入board.c文件(arch/powerpc/lib/board.c)
board_init_r:
这部分代码初始化一些列外设,这里只提几个感兴趣的地方。
(1)建立Command table(2011.06版本中没找到这部分代码)
U-boot支持N多命令,通过一个cmdtbl来管理,这个cmdtbl是在运行时被负值的
for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++)
{……}
u-boot的命令都被定义在.u_boot.cmd段,并在链接时确定期位置:在__u_boot_cmd_start和__u_boot_cmd_end之间。
(2)Trap的安装是通过调用start.s中的代码完成的。其实就是将原有的trap重定位到dest_addr,也就是RAM中的起始位置。
(3)茫茫多设备的初始化 …………
(4)进入main_loop()等待用户输入命令,如bootm命令。