【本文导读】:本文基于20M项目开发中的一些心得和体会,主要描述UBOOT是如何运转起来的。重点详细的描述uboot的启动流程,为uboot移植做详细的分析笔录及后续调试查找问题提供依据。
【keywords】:配置字,uboot,启动,移植
1 启动配置
1.1启动配置字
CPU根据CFG_RESET_SOURCE[0:3]引脚信号判别从哪处读取重启配置字。根据RRU8B10电路图中的 CFG_RESET_SOURCE[0:3]四根线接,即CFG_RESET_SOURCE[0:3]= 0000,可知重启配置字从NOR Flash中加载。根据TEXT_BASE = 0xFE000000定义.text的起始地址。
.text
\\
#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(CONFIG_SYS_HRCW_LOW) _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_HIGH)
CPU读完启动配置字后,主要配置PLL及启动地址。下面是对启动配置字的详细描述。 图1是RCWLR与RCWHR格式。 RCWLR:主要用于PLL比例
RCWHR:主要用于决定从哪里启动及其他
1
图1:重启配置字格式
下面是本项目中对启动配置字的配置
配置字段 BITs Name Value meaning 0 LBIUCM 0 LBC controller clock: csb_clk【1:1】 1 DDRCM 1 DDR SDRAM memory controller clock:csb_clk【2;1】 2-3 SVCOD 0 System PLL VCO division 4-7 RCWLR 8 SPMF 0100 System PLL multiplication factor(csb_clk: SYS_CLK_IN=4:1) Reserved 0 9-15 COREPLL 0000110 core_pll:csb_pll=3:1 16-31 Reserved 0 2
0-3 Reserved 4 COREDIS 0 Core disable mode 5 BMS 1 Boot memory space. 6-7 BOOTSEQ 0 Boot sequencer configuration. 8 SWEN 0 Software watchdog enable 9-11 ROMLOC 110 Local bus GPCM—16-bit ROM RCWHR 12-13 RLEXT 00 14-15 Reserved 16-18 TSEC1M 000 19-21 TSEC2M 000 22-27 Reserved 28 TLE 0 29-31 Reserved
3
1.1.1 PLL
图二:时钟子系统
(1) csb_clk = [SYS_CLK_IN] × SPMF其中SYS_CLK_IN 24-66MHZ and 33.3平均 (2) qe_clk = (QE_CLK_IN × CEPMF) ÷ (1 + CEPDF) (3) ddr_clk = 2*csb_clk; (4) lbc_clk = csb_clk;
4
csb_clk = 33.3*4=133Mhz ddr_clk = 2* csb_clk=266Mhz
以及其他模块可通过SCCR寄存器配置时钟
1.1.2 RCW关键字段
下面重点描述RCWHR的配置某个字段:
RCWHR[ROMLOC]、RCWHR[RLEXT]共同决定设备启动。 RCWHR[BMS]:
从相应的地方读取硬件配置字(RCWL,RCWH)后,会设置相应的寄存器。其中RCWH中的BMS位值为1,定义了e300核心的MSR[IP]位初始值,如上图所示,MSR[IP]为1决定中断向量的前缀为0xFFF,启动存储空间的位置为0xFF80_0000~0xFFFF_FFFF;SWEN位为0,禁止软件看门狗;ROMLOC位为0b110,RLEXT位为0b00确定了选择local bus GPCM-16bit ROM为启动ROM。复位向量和本地地址映射的默认启动ROM访问将直接指向ROMLOC指定的接口。选中的启动ROM的本地访问窗口(LBLAW0)将被使能,CPU根据LBLAWBAR0, BR0, OR0确定启动后第一条指定从哪处取,这点在 powerpc上电复位过程.docx中有所描述。 中断向量的前缀为0x000,复位向量为100,决定了系统复位后的第一条指令从0x0000_0100处获得。
5
/cpu/mpc83xx/u-boot.lds文件时连接器脚本文件,其中
.text : {
cpu/mpc83xx/start.o (.text) ……………………. }
……………… ENTRY(_start)
规定了代码段从/cpu/mpc83xx/start.s开始。而ENTRY(_start)这一句告诉编译器uboot.bin的镜像入口点为start.s中的_start标号。 UBOOT内存映射:
Range Start Range End Definition Size 0x0000_0000 0x07ff_ffff DDR 128M 0xa000_0000 0xafff_ffff PCI Express 1 Mem 256M 0xb000_0000 0xb0ff_ffff PCI Express 1 Config 16M 0xb100_0000 0xb17f_ffff PCI Express 1 IO 8M 0xe000_0000 0xe00f_ffff IMMR 1M 0xfe00_0000 0xffff_ffff NOR Flash (CS0) 32M The 32M NOR flash starts at address 0xfe000000:
Range Start Range End Definition 0xfe00_0000 0xfe05_ffff U-Boot 0xfe0a_0000
0xfe2f_ffff Kernel 0xfe30_0000 0xfe7f_ffff
Ramdisk file system 0xfe80_0000
0xfe81_0000
DTB
6
下面通过uboot源码实现看看这些实现过程;
2 U-Boot 启动流程
2.1 总体启动流程 1.
/cpu/mpc83xx/u-boot.lds文件时连接器脚本文件,其中
.text : {
cpu/mpc83xx/start.o (.text) ………….. } ………
ENTRY(_start)
规定了代码段从/cpu/mpc83xx/start.s开始。而ENTRY(_start)这一句告诉编译器uboot.bin的镜像入口点为start.s中的_start标号。
2. CPU入口函数_start(FLASH中运行): 主要功能:
(1) 设置IMMRBAR
(2) 初始化E300处理器内核 (3) 初始化并使能MMU (4) 使能数据cache
(5) 调用CPU初始化函数cpu_init_f (6) 调用主板初始化函数board_init_f
3. CPU初始化函数cpu_init_f(FLASH中运行): 主要功能:
7
/cpu/mpc83xx/start.S
/cpu/mpc83xx/cpu_init.c
(1) 初始化CPU相关寄存器 (2) 建立系统内存映射
4. 主板初始化函数一board_init_f(FLASH中运行):
主要功能:
(1) 初始化Time Base (2) 初始化串口 (3) 初始化console (4) 检查CPU型号 (5) 初始化DDR内存
(6) 将U-Boot重定位到内存中
5. 主板初始化函数二board_init_r(内存中运行):
主要功能:
(1) 使能指令cache (2) 初始化FLASH驱动 (3) 初始化中断 (4) 初始化以太网
(5) 进入main主循环处理用户命令
(6) 从FLASH中加载Linux内核、文件系统以及DTB
/lib_ppc/board.c
/lib_ppc/board.c
8
2.2 init_e300_core 函数
初始化e300核心,禁止中断响应,只允许machine check中断和system reset中断,禁止指令和数据地址转换,即关闭MMU,进行实地址转换,设置为supervisor级别,禁止看门狗,无效指令和数据cache,等,为系统创建一个干净可靠的初始环境。 2.3 窗口重映射
从前面MPC8308上电流程可以看出,上电之后,第一条代码是从0xFFF0_0100的地方开始执行的,但是flash并不一定会分配在0xFF00_0000到0xFFFF_FFFF的地方(以32M的为例)。在本系统中,Flash的地址就被分配到了0xFE00_0000到0xFFFF_FFFF的地方。所以这其中需要做一个跳转,这正是这段代码中map_flash_by_law1,remap_flash_by_law0等函数要做的,具体的流程可以由下面的五张图来说明:
9
1 开始时,BR0,OR0为全零,4G全是重复的Flash,CPU通过LBLAW0访问Flash空间 FF80_0000到 FFFF_FFFF。
2 map_flash_by_law1函数使用LBLAW1映射FE00_0000到FEFF_FFFF这段空间。
3 跳转到FE00_0000这段空间执行代码,由于4G空间重复,只要偏移地址计算正确就会顺序执行。
10
4 remap_flash_by_law0函数设置BR0为FE00_0000,OR0大小为32M。
5 remap_flash_by_law0函数设置LBLAW0窗口映射FE00_0000到FFFF_FFFF区域,并 清除LBLAW1。
11
2.4 Dcache 中分配空间做堆栈
程序跑到这里,就要进入第一个C函数了,C函数的运行需要堆栈空间,但这时,RAM还没有初始化,只能在Dcache中锁定一定的空间,用于C的堆栈空间。
lock_ram_in_cache函数在Dcache中锁定4k的空间。 下面的几行代码将堆栈指针指向刚刚分配好的Dcache空间。 lis r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@h ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@l li r0, 0 stwu r0, -4(r1) 2.5 cpu_init_f 函数
该函数是系统执行的第一个C语言的函数,主要是做一些CPU 寄存器的初始化,其中最重要的部分是初始化Local Access Windows的值和Local Bus上的片选BR,OR的值。这些值需要在/include/configs/MPC8308rru.h中配置好。
12
2.6 board_init_f 函数
该函数为板级初始化的第一个函数,会对板子上很多硬件外设做初始化,其中最重要的为init_sequence数组,该数组里面包含了很多板级的硬件初始化函数,在
board_init_f函数中会依次的调用该数组中的函数去初始化各个硬件,该数组如下:
init_fnc_t *init_sequence[] = { #if defined(CONFIG_BOARD_EARLY_INIT_F) board_early_init_f, #endif
#if !defined(CONFIG_8xx_CPUCLK_DEFAULT)
get_clocks, /* get CPU and bus clocks (etc.) */ #if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) / && !defined(CONFIG_TQM885D) adjust_sdram_tbs_8xx, #endif
init_timebase, #endif
#ifdef CFG_ALLOC_DPRAM #if !defined(CONFIG_CPM2) dpram_init, #endif #endif
#if defined(CONFIG_BOARD_POSTCLK_INIT) board_postclk_init, #endif
13
env_init,
#if defined(CONFIG_8xx_CPUCLK_DEFAULT)
get_clocks_866, /* get CPU and bus clocks according to the environment variable */
sdram_adjust_866, /* adjust sdram refresh rate according to the new clock */
init_timebase, #endif
init_baudrate, serial_init, console_init_f, display_options, #if defined(CONFIG_8260) prt_8260_rsr, prt_8260_clks,
#endif /* CONFIG_8260 */ #if defined(CONFIG_MPC83XX) prt_83xx_rsr, #endif checkcpu,
#if defined(CONFIG_MPC5xxx) prt_mpc5xxx_clks,
#endif /* CONFIG_MPC5xxx */ #if defined(CONFIG_MPC8220)
14
prt_mpc8220_clks, #endif checkboard,
INIT_FUNC_WATCHDOG_INIT #if defined(CONFIG_MISC_INIT_F) misc_init_f, #endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c, #endif
#if defined(CONFIG_DTT) /* Digital Thermometers and Thermostats */ dtt_init, #endif
#ifdef CONFIG_POST post_init_f, #endif
INIT_FUNC_WATCHDOG_RESET
init_func_ram,
#if defined(CFG_DRAM_TEST) testdram,
#endif /* CFG_DRAM_TEST */ INIT_FUNC_WATCHDOG_RESET
15
NULL, /* Terminate this list */ };
可以看到时钟,内存,串口,控制台等初始化函数的调用,其中串口的初始化要先于内存初始化。
2.7 relocate_code 函数
到目前为止,boot代码都是在Flash中运行,但是代码最终是要到RAM中运行的,在上面的board_init_f函数中已经将RAM初始化好了,具备了在RAM中运行程序的能力,现在relocate_code函数需要做两个事情:
1 从Flash中拷贝uboot的代码到RAM
2 记下现在执行代码的偏移,跳转到RAM中相应的位置执行。 2.8 board_init_r 函数
该函数为板级初始化的第二阶段,主要是初始化PCI,PCIE,网口,Flash等设备,关闭看门狗,把前面借dcache做堆栈的空间解锁,还给cache。在一切设备都初始化好后,便会进去main_loop的死循环中。
16
3 U-Boot 关键的数据结构
U-boot的主要功能是用于引导OS的,但是本身也提供了许多强大的功能,可以通过输入命令行来完成许多操作。所以它本身也是一个很完备的系统。U-boot的大部分操作都是围绕它自身的数据结构,这些数据结构是通用的,但是不同的板子初始化这些数据就不一样了。所有U-boot的通用代码是依赖于这些重要的数据结构的。 3.1 gd全局数据变量指针
它保存了U-boot运行需要的全局数据,类型定义:
typedef struct global_data {
bd_t *bd; //board data pointor板子数据指针
unsigned long flags; //指示标志,如设备已经初始化标志等。 unsigned long baudrate; //串口波特率 unsigned long have_console; //串口初始化标志
unsigned long reloc_off; //重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0
unsigned long env_addr; //环境参数地址
unsigned long env_valid; //环境参数CRC检验有效标志 unsigned long fb_base; //base address of frame buffer #ifdef CONFIG_VFD
unsigned char vfd_type; //display type #endif
void **jt; //跳转表 } gd_t;
3.2 bd 板子数据指针
板子很多重要的参数。类型定义如下: typedef struct bd_info {
17
int bi_baudrate; //串口波特率 unsigned long bi_ip_addr; //IP地址 unsigned char bi_enetaddr[6]; //MAC地址 struct environment_s *bi_env;
ulong bi_arch_number; //unique id for this board ulong bi_boot_params; //启动参数
struct //RAM配置 {
ulong start; ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
3.3环境变量指针
env_t *env_ptr = (env_t *)(&environment[0]); (common/Env_nand.c)
env_ptr指向环境参数区,系统启动是默认的环境参数environment[],定义在common/environment.c中。 参数解释: bootdelay 定义执行自动启动的等候秒数 baudrate 定义串口控制台的波特率 netmask 定义以太网接口的掩码 ethaddr 定义以太网接口的MAC地址 bootfile 定义缺省的下载文件
bootargs 定义传递给Linux内核的命令行参数
18
bootcmd 定义自动启动时执行的几条命令 serverip 定义tftp服务器段的IP地址 ipaddr 定义本地的IP地址
stdin 定义标准输入设备,一般是串口 stdout 定义标准输出设备,一般是串口 stderr 定义标准错误信息输出设备,一般是串口
3.4设备相关
标准IO设备数组 evice_t *stdio_devices[] = {NULL, NULL, NULL}; 设备列表 list_t devlist = 0; device_t的定义: include/devices.h typedef struct { int flags; /* Device flags: input/output/system */ int ext; /* Supported extensions */ char name[16]; /*Device name */ /* GENERAL functions */
int (*start)(void); /* To start the device */ int (*stop)(void); /* To stop the device */ /* 输出函数 */
void (*putc)(const char c); /* To put a char */
void (*puts)(const char *s); /* To put a string(accelerator) */ /* 输入函数 */
int (*tstc)(void); /* To test if a char is ready… */
19
int (*getc)(void); /* To get that char */ /* Other functions */
void *priv; /* Private extensions */\\
} device_t;
U-boot把可以用为控制台输入输出的设备添加到设备列表devlist,并把当前用作标准IO的设备指针加到stdio_devices数组中。
3.5命令结构体类型定义:include/command.h struct cmd_tbl_s {
char *name;
/* Command Name 命令名
*/
在调用标准IO函数如printf()时将调用stdio_devices数组对应设备的IO函数如putc()。
int maxargs; /* maximum number of arguments int repeatable;
/* autorepeat allowed?
*/
最大参数个数*/
/* Implementation function 命令执行函数*/
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); char *usage;
/* Usage message (short)
*/
#ifdef CFG_LONGHELP
char *help; #endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]); #endif };
20
/* Help message (long) */
typedef struct cmd_tbl_s cmd_tbl_t;
//定义section属性的结构体。 编译时会单独生成一个名为.u_boot_cmd的section段。 #define Struct_Section __attribute__ ((unused,section (\".u_boot_cmd\")))
//这个宏定义一个命令结构体变量。并用name,maxargs,rep,cmd,usage,help初始化各个域。
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \\
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage , help}
21
4 UBOOT启动
4.1 启动第一阶段 3.1.1填充启动配置字
.text
\\
#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(CONFIG_SYS_HRCW_LOW) _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_HIGH)
在.text开头位置填充启动配置字,执行该句后,TEXT_BASE开头的启动配置字分布情况如下:
22
该图只写出启动配置字的有效部分,例如0x00-0x07填充的是RCWL[0:7],只是取其有效的部分是低八位。启动配置字每个字节重复8次。
3.1.2 填充头部信息及全部函数 头部信息:
.long .globl
0x27051956 version_string
/* U-Boot Magic Number */
version_string:
.ascii U_BOOT_VERSION
.ascii \" (\.ascii \" \
全局函数: enable_addr_trans disable_addr_trans
23
get_pvr ppcDWstore ppcDWload
################################
紧接着启动配置字的0x64偏移位置,填充0x27051956以及uboot版本,时间和日期等字符串,以及全局函数。但是总容量最好不要超过0x100。
3.1.3. 将_start定位到0x100位置并重新设置IMMR . = EXC_OFF_SYS_RESET /*定位到0x100*/
.globl
_start
_start: /* time t 0 */ li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/
nop b
boot_cold
. = EXC_OFF_SYS_RESET + 0x10
.globl
_start_warm
_start_warm: li r21, BOOTFLAG_WARM /* Software reboot */ b
boot_warm
boot_cold: /* time t 3 */
24
lis r4, CONFIG_DEFAULT_IMMR@h
nop
boot_warm: /* time t 5 */ mfmsr
r5
/* save msr contents
*/
/* 83xx manuals prescribe a specific sequence for updating IMMRBAR. */ bl 1f
上面几步初始化了几个寄存器: R21 = 0x02 R4 = 0xff400000
R5= MSR
#############更新IMMR到0xe000_0000############################
1: mflr r7 //r7=lr
lis r3, CONFIG_SYS_IMMR@h
ori r3, r3, CONFIG_SYS_IMMR@l //r3= 0xe000000
lwz r6, IMMRBAR(r4) //r6=0xff400000
isync
stw r3, IMMRBAR(r4)
//更新IMMR寄存器值为0xe000,0000
lwz r6, 0(r7)
/* Arbitrary external load */
//r6=lr
25
isync
lwz r6, IMMRBAR(r3) //r6= 0xe000000
isync
3.1.4 初始化e300
init_e300_core: /* time t 10 */ /* Initialize machine status; enable machine check interrupt */ li
r3, MSR_KERNEL
/* Set ME and RI flags */
rlwimi r3, r5, 0, 25, 25
/* preserve IP bit set by HRCW,设置中断偏移量 */
#ifdef DEBUG
rlwimi
r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */
#endif SYNC
/* Some chip revs need this... */ mtmsr r3
/*保存到MSR*/
SYNC mtspr
SRR1, r3
/* Make SRR1 match MSR */
//禁止看门狗
lis r3, CONFIG_SYS_IMMR@h
#if defined(CONFIG_WATCHDOG) //如果看门狗宏定义了则启用它
/* Initialise the Wathcdog values and reset it (if req) */ /*------------------------------------------------------*/ lis r4, CONFIG_SYS_WATCHDOG_VALUE
ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR)
26
stw r4, SWCRR(r3)
/* and reset it */
li r4, 0x556C
sth r4, SWSRR@l(r3) li
r4, -0x55C7
sth r4, SWSRR@l(r3)
#else
//禁止看门狗 /* Disable Wathcdog */ /*-------------------*/ lwz r4, SWCRR(r3)
/* Check to see if its enabled for disabling once disabled by SW you can't re-enable */ andi. r4, r4, 0x4 beq 1f xor r4, r4, r4 stw r4, SWCRR(r3)
1:
#endif /* CONFIG_WATCHDOG */
########设置HID###################
27
lis r3, CONFIG_SYS_HID0_INIT@h ori r3, r3, (CONFIG_SYS_HID0_INIT | HID0_ICFI | HID0_DCFI)@l SYNC mtspr
HID0, r3
lis r3, CONFIG_SYS_HID0_FINAL@h
ori r3, r3, (CONFIG_SYS_HID0_FINAL & ~(HID0_ICFI | HID0_DCFI))@l SYNC mtspr
HID0, r3
lis r3, CONFIG_SYS_HID2@h ori r3, r3, CONFIG_SYS_HID2@l SYNC mtspr
HID2, r3
/* Done! */
/*------------------------------*/ Blr
//
返回
3.1.5 窗口设置 map_flash_by_law1: /* When booting from ROM (Flash or EPROM), clear the */ /* Address Mask in OR0 so ROM appears everywhere */
/*----------------------------------------------------*/
28
lis r3, (CONFIG_SYS_IMMR)@h /* r3= 0xe0000000 */ lwz r4, OR0@l(r3) li
r5, 0x7fff /* r5= 0x0007FFF */
and r4, r4, r5
stw r4, OR0@l(r3) /* OR0 = OR0 & 0x0000_7FFF */
/* As MPC8349E User's Manual presented, when RCW[BMS] is set to 0, * system will boot from 0x0000_0100, and the LBLAWBAR0[BASE_ADDR] * reset value is 0x00000; when RCW[BMS] is set to 1, system will boot * from 0xFFF0_0100, and the LBLAWBAR0[BASE_ADDR] reset value is * 0xFF800. From the hard resetting to here, the processor fetched and * executed the instructions one by one. There is not absolutely * jumping happened. Laterly, the u-boot code has to do an absolutely * jumping to tell the CPU instruction fetching component what the * u-boot TEXT base address is. Because the TEXT base resides in the * boot ROM memory space, to garantee the code can run smoothly after * that jumping, we must map in the entire boot ROM by Local Access * Window. Sometimes, we desire an non-0x00000 or non-0xFF800 starting * address for boot ROM, such as 0xFE000000. In this case, the default * LBIU Local Access Widow 0 will not cover this memory space. So, we * need another window to map in it. */
lis r4, (CONFIG_SYS_FLASH_BASE)@h ori r4, r4, (CONFIG_SYS_FLASH_BASE)@l
29
stw r4, LBLAWBAR1(r3) /* LBLAWBAR1 = CONFIG_SYS_FLASH_BASE */
/* Store 0x80000012 + log2(CONFIG_SYS_FLASH_SIZE) into LBLAWAR1 */ lis r4, (0x80000012)@h ori r4, r4, (0x80000012)@l li r5, CONFIG_SYS_FLASH_SIZE
1: srawi. r5, r5, 1 /* r5 = r5 >> 1 */
remap_flash_by_law0:
/* Initialize the BR0 with the boot ROM starting address. */ lwz r4, BR0(r3) li r5, 0x7FFF and r4, r4, r5
lis r5, (CONFIG_SYS_FLASH_BASE & 0xFFFF8000)@h ori r5, r5, (CONFIG_SYS_FLASH_BASE & 0xFFFF8000)@l or r5, r5, r4
stw r4, LBLAWAR1(r3) /* LBLAWAR1 <= 8MB Flash Size */ blr
addi r4, r4, 1 bne 1b
stw r5, BR0(r3) /* r5 <= (CONFIG_SYS_FLASH_BASE & 0xFFFF8000) | (BR0 & 0x00007FFF) */
30
lwz r4, OR0(r3)
lis r5, ~((CONFIG_SYS_FLASH_SIZE << 4) - 1) or r4, r4, r5 stw r4, OR0(r3)
/*OR0 = OR0|0xe000_0000*/
lis r4, (CONFIG_SYS_FLASH_BASE)@h ori r4, r4, (CONFIG_SYS_FLASH_BASE)@l
stw r4, LBLAWBAR0(r3) /* LBLAWBAR0 <= CONFIG_SYS_FLASH_BASE */
/* Store 0x80000012 + log2(CONFIG_SYS_FLASH_SIZE) into LBLAWAR0 */ lis r4, (0x80000012)@h ori r4, r4, (0x80000012)@l
li r5, CONFIG_SYS_FLASH_SIZE
1: srawi. r5, r5, 1 /* r5 = r5 >> 1 */ addi r4, r4, 1 bne 1b
stw r4, LBLAWAR0(r3) /* LBLAWAR0 <= Flash Size */
xor r4, r4, r4
stw r4, LBLAWBAR1(r3)
stw r4, LBLAWAR1(r3) /* Off LBIU LAW1 */
blr
31
3.1.6 BAT设置 setup_bats:
/* IBAT 0 */ addis
r4, r0, CONFIG_SYS_IBAT0L@h
addis
r0, r0, 0x0000
ori r4, r4, CONFIG_SYS_IBAT0L@l addis
r3, r0, CONFIG_SYS_IBAT0U@h
ori r3, r3, CONFIG_SYS_IBAT0U@l mtspr mtspr
IBAT0L, r4 IBAT0U, r3
/* block address translation */
3.1.7 enable_addr_trans,dcache_enable,
使能地址转换:配置MSR及HID lock_ram_in_cache
3.1.8
该段代码功能为在0xe6000000处的cache处的锁定一段作为堆栈使用,便于下面C语言函数调用参数的入栈和出栈操作。
/* Allocate Initial RAM in data cache. */
lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l li
r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \\ (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32
32
mtctr r4
1: dcbz r0, r3 addi r3, r3, 32 bdnz 1b
/* Lock the data cache */ mfspr
r0, HID0
ori r0, r0, HID0_DLOCK sync mtspr HID0, r0
sync blr
以及后面的设置堆栈操作: lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l
li
r0, 0
stwu r0, -4(r1)
stwu r0, -4(r1)
/* let the C-code set up the rest */
/*
*/
33
/* Be careful to keep code relocatable & stack humble */ /*------------------------------------------------------*/ cpu_init_f
3.1.9
本函数在cpu_init.c中,主要设置和初始化CPU。cpu_init_f主要用到mpc8308rru.h中配置的值去初始化CPU,所以这个文件是移植过程中的重点。 cpu_init_f主要配置的寄存器如下:
1. ACR,SPCR,SCCR,SICRH,SICRL,BR,OR,BR,OR,以及GPIO的配置 2. 初始化gd全局变量
这里非常重要点为gd为向linux内核传递的参数信息,并将其保存在r2寄存器中。Gd大小为256字节。具体地址为0xe6000000+0x1000-0x100;
3.1.10 board_init_f
该函数位于board.c文件中。主要工作有:
1. 调用init_sequence数组结构中的函数,这个数组中函数N多,需要仔细看。其中
比较重要的几个: 1. get_clocks: 计算各个模块使用到的时钟。哥在调试过程中便遇到PLL计算失误导致串口不能正常打印。并将计算到的各个模块PLL保存到gd全局变量中去。 2. env_init:初始化环境变量,并将起始地址保存到gd中去。
代码很长,但是这个函数是跳转到内存中去执行的关键,需要仔细分析,代码还是贴出来:
void board_init_f (ulong bootflag) {
bd_t *bd;
34
3.init_baudrate:直接初始化串口波特率并保存到gd中去。 4. serial_init:串口初始化
ulong len, addr, addr_sp; ulong *s; gd_t *id;
init_fnc_t **init_fnc_ptr;
/* Pointer is writable since we allocated a register for it */
//gd位于0xe600_0000+0x1000-0x100位置处 gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__(\"\": : :\"memory\");
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr) () != 0) { hang ();
}
}
/*
* Now that we have DRAM mapped and working, we can * relocate the code and continue running from DRAM. *
* Reserve memory at end of RAM for (top down in that order): * - area that won't get touched by U-Boot and Linux (optional)
* - kernel log buffer
35
* - protected RAM * - LCD framebuffer * - monitor code * - board info struct */
len = (ulong)&_end - CONFIG_SYS_MONITOR_BASE;
/*
* Subtract specified amount of memory to hide so that it won't * get \"touched\" at all by U-Boot. By fixing up gd->ram_size * the Linux kernel should now get passed the now \"corrected\" * memory size and won't touch it either. This should work * for arch/ppc and arch/powerpc. Only Linux board ports in * arch/powerpc with bootwrapper support, that recalculate the * memory size from the SDRAM controller setup will have to * get fixed. */
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
addr = CONFIG_SYS_SDRAM_BASE + get_effective_memsize();//addr=128M
/* round down to next 4 kB limit */
addr &= ~(4096 - 1);//addr=addr&0xfffff000=0x800_0000;
debug (\"Top of RAM usable for U-Boot at: %08lx\\n\
36
/*
* reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */
addr -= len; //len=0x52000; addr &= ~(4096 - 1);
debug (\"Reserving %ldk for U-Boot at: %08lx\\n\
//addr =0x07fae000;
addr_sp = addr - TOTAL_MALLOC_LEN;//addr_sp=07f2c000 debug (\"Reserving %dk for malloc() at: %08lx\\n\
TOTAL_MALLOC_LEN >> 10, addr_sp);
/*
* (permanently) allocate a Board Info struct * and a permanent copy of the \"global\" data */
addr_sp -= sizeof (bd_t);//addr_sp=07f2bfbc bd = (bd_t *) addr_sp; gd->bd = bd;
debug (\"Reserving %zu Bytes for Board Info at: %08lx\\n\
sizeof (bd_t), addr_sp);//modify by huangjl
addr_sp -= sizeof (gd_t);
37
id = (gd_t *) addr_sp;//id= 07f2bf4c
debug (\"Reserving %zu Bytes for Global Data at: %08lx\\n\
sizeof (gd_t), addr_sp);//modify by huangjl
/*
* Finally, we set up a new (bigger) stack. *
* Leave some safety gap for SP, force alignment on 16 byte boundary * Clear initial stack frame */
addr_sp -= 16; addr_sp &= ~0xF; s = (ulong *)addr_sp; *s-- = 0; *s-- = 0;
addr_sp = (ulong)s;
debug (\"Stack Pointer at: %08lx\\n\//addr_sp=0x07f2bf28
/*
* Save local variables to board info struct */
bd->bi_memstart = CONFIG_SYS_SDRAM_BASE; /* start of DRAM memory */
bd->bi_memsize = gd->ram_size;
/* size of DRAM memory in bytes */
38
bd->bi_sramstart = 0;
/* FIXME */ /* start of SRAM memory /* FIXME */ /* size of SRAM memory
*/ */
bd->bi_sramsize = 0;
#if defined(CONFIG_MPC83xx)
bd->bi_immrbar = CONFIG_SYS_IMMR; #endif bd->bi_bootflags = bootflag; /* boot / reboot flag (for LynxOS) WATCHDOG_RESET ();
bd->bi_intfreq = gd->cpu_clk; /* Internal Freq, in Hz */ bd->bi_busfreq = gd->bus_clk;
/* Bus Freq, in Hz */
bd->bi_baudrate = gd->baudrate; /* Console Baudrate */
debug (\"New Stack Pointer is: %08lx\\n\
WATCHDOG_RESET ();
WATCHDOG_RESET();
memcpy (id, (void *)gd, sizeof (gd_t));
*/
39
}
所以执行完代码后,几个关键的值为
//addr_sp=07f2bf28, id= 07f2bf4c,addr=07fae000 执行完后内存的分布情况如下图:
/* NOTREACHED - relocate_code() does not return */
relocate_code (addr_sp, id, addr);//addr_sp=07f2bf28, id= 07f2bf4c,addr=07fae000
0x0800_0000UBOOT代码内存空间(328KB)Addr:0x07fae000MALLOC内存空间(520KB)BD内存空间(68Byte)GD内存空间(112Byte)预留栈空间(16Byte)bdId:0x07f2bf4cAddr_sp:0x07f2bf280x0000_0000
其中addr为uboot将要复制到在内存中的起始地址。Bd保存开发板的信息。ID则保存全局变量信息,它是从cache中复制过来的。Addr_sp则指向可用内存的最高端地址。接下来是relocate_code函数,位于start.s汇编文件中,它是文件的精粹。 /*
* void relocate_code (addr_sp, gd, addr_moni) *
40
* This \"function\" does not return, instead it continues in RAM * after relocating the monitor code. * * r3 = dest * r4 = src
* r5 = length in bytes * r6 = cachelinesize */
.globl
relocate_code
relocate_code: mr r1, r3 /* Set new stack pointer :r1=r3*/
mr r9, r4 /* Save copy of Global Data pointer r9=r4*/ mr r10, r5
/* Save copy of Destination Address r10=r5*/
mr r3, r5 /* Destination Address r3=r5*/
lis r4, CONFIG_SYS_MONITOR_BASE@h
/* Source Address */
ori r4, r4, CONFIG_SYS_MONITOR_BASE@l //r4=0xfe00_0000 lwz r5, GOT(__bss_start)
sub r5, r5, r4 //r5=r5-r4=r5-0xfe00_0000; li
r6, CONFIG_SYS_CACHELINE_SIZE
/* Cache Line Size */
/*
* Fix GOT pointer:
*
41
* New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) * + Destination Address
* * Offset: */
sub r15, r10, r4 //r15=r10-0xfe00_0000=r5-0xfe00_0000
/* First our own GOT */
add r14, r14, r15 // r14=r14+ r5-0xfe00_0000 /* then the one used by the C code */
add r30, r30, r15 //r30=r30+ r5-0xfe00_0000
/*
* Now relocate code */
cmplw cr1,r3,r4
addi r0,r5,3 srwi. r0,r0,2 beq cr1,4f
/* In place copy is not necessary */
beq 7f /* Protect against 0 count
*/
mtctr
r0
bge cr1,2f la
r8,-4(r4)
42
la r7,-4(r3)
/* copy */
1: lwzu r0,4(r8) stwu r0,4(r7) bdnz 1b
addi r0,r5,3 srwi. r0,r0,2 mtctr r0
la r8,-4(r4) la
r7,-4(r3)
/* and compare */ 20: lwzu r20,4(r8) lwzu r21,4(r7) xor. r22, r20, r21 bne 30f bdnz 20b b 4f
/* compare failed */ 30: li r3, 0
blr
43
2: slwi r0,r0,2 /* re copy in reverse order ... y do we needed it? */ add r8,r4,r0
add r7,r3,r0
3: lwzu r0,-4(r8) stwu r0,-4(r7) bdnz 3b
/*
* Now flush the cache: note that we must start from a cache aligned * address. Otherwise we might miss one cache line. */ 4: cmpwi r6,0
add r5,r3,r5 beq 7f /* Always flush prefetch queue in any case */
subi r0,r6,1 andc r3,r3,r0
mr r4,r3
5: dcbst 0,r4
add r4,r4,r6 cmplw r4,r5
blt 5b sync
/* Wait for all dcbst to complete on bus */
mr r4,r3
44
6: icbi 0,r4
add r4,r4,r6 cmplw blt 6b
/* Wait for all icbi to complete on bus */ r4,r5
7: sync /*
isync
* We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */
addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET mtlr r0 blr
关于这段复制代码后面有时间再仔细分析,现在跳到in_ram标号处执行。这个将其作为第二阶段去分析。 4.2 启动第二阶段 3.2.1 in_ram in_ram:
/*
* Relocation Function, r14 point to got2+0x8000 *
* Adjust got2 pointers, no need to check for 0, this code
45
* already puts a few entries in the table. */ li r0,__got2_entries@sectoff@l la
r3,GOT(_GOT2_TABLE_)
lwz r11,GOT(_GOT2_TABLE_) mtctr
r0
sub r11,r3,r11
addi r3,r3,-4
1: lwzu r0,4(r3) cmpwi r0,0
beq- 2f add r0,r0,r11
stw r0,0(r3)
2: bdnz 1b
#ifndef CONFIG_NAND_SPL /*
* Now adjust the fixups and the pointers to the fixups * in case we need to move ourselves again. */ li
r0,__fixup_entries@sectoff@l
lwz r3,GOT(_FIXUP_TABLE_) cmpwi r0,0
mtctr
r0
46
addi r3,r3,-4
beq 4f
3: lwzu r4,4(r3) lwzux
r0,r4,r11
add r0,r0,r11 stw r10,0(r3) stw r0,0(r4) bdnz 3b
4: #endif
clear_bss: /*
* Now clear BSS segment */
lwz r3,GOT(__bss_start) #if defined(CONFIG_HYMOD) /*
* For HYMOD - the environment is the very last item in flash. * The real .bss stops just before environment starts, so only * clear up to that point. *
* taken from mods for FADS board
*/
47
lwz r4,GOT(environment)
#else
lwz r4,GOT(_end)
#endif cmplw 0, r3, r4
beq 6f
li
r0, 0 5: stw r0, 0(r3) addi r3, r3, 4 cmplw 0, r3, r4
bne 5b
6: mr r3, r9 /* Global Data pointer mr r4, r10
/* Destination Address bl
board_init_r
这其中的代码留下后面分析。 3.2.2 board_init_r
void board_init_r (gd_t *id, ulong dest_addr) {
*/ */
48
char *s; bd_t *bd; ulong malloc_start;
ulong flash_size;
gd = id; /* initialize RAM version of global data 好方法,又将这个变量地址改变了*/ bd = gd->bd;
gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */
/* The Malloc area is immediately below the monitor copy in DRAM */
malloc_start = dest_addr - TOTAL_MALLOC_LEN;// malloc_start=0x07fae000-520KB
serial_initialize();
debug (\"Now running in RAM - U-Boot at: %08lx\\n\ //dest_addr=0x07fae000 WATCHDOG_RESET ();
/*
* Setup trap handlers */
trap_init (dest_addr);
49
monitor_flash_len = (ulong)&__init_end - dest_addr;
WATCHDOG_RESET ();
#if defined(CONFIG_SYS_DELAYED_ICACHE) || defined(CONFIG_MPC83xx)
icache_enable (); /* it's time to enable the instruction cache */
#endif asm (\"sync ; isync\");
mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
#if !defined(CONFIG_SYS_NO_FLASH) puts (\"FLASH: \");
if ((flash_size = flash_init ()) > 0) {
print_size (flash_size, \"\\n\");
} else { puts (failed); hang ();
}
bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; /* update start of FLASH memory
*/
50
bd->bi_flashsize = flash_size; /* size of FLASH memory (final value) */
bd->bi_flashoffset = monitor_flash_len; /* reserved area for startup monitor */
WATCHDOG_RESET ();
/* initialize higher level parts of CPU like time base and timers */ cpu_init_r ();
WATCHDOG_RESET ();
/* relocate environment function pointers etc. */ env_relocate ();
/* IP Address */
bd->bi_ip_addr = getenv_IPaddr (\"ipaddr\");
WATCHDOG_RESET ();
/** leave this here (after malloc(), environment and PCI are working) **/ /* Initialize stdio devices */ stdio_init ();
/* Initialize the jump table for applications */
51
jumptable_init ();
/* Initialize the console (after the relocation and devices init) */ console_init_r ();
debug (\"U-Boot relocated to %08lx\\n\
/*
* Enable Interrupts */
interrupt_init ();
udelay (20);
set_timer (0);
/* Initialize from environment */ if ((s = getenv (\"loadaddr\")) != NULL) { load_addr = simple_strtoul (s, NULL, 16);
}
/* Initialization complete - start the monitor */
52
}
这个函数没有想象的做得事情多。到了最后,跳到main_loop中去执行。几乎可以不用去关注。
void main_loop (void) {
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s; int bootdelay;
/* NOTREACHED - no way out of command loop except booting */ /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { }
WATCHDOG_RESET (); main_loop ();
#endif
parse_string_outer(p, FLAG_PARSE_SEMICOLON |
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv (\"bootdelay\");
53
FLAG_EXIT_FROM_LOOP);
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug (\"### main_loop entered: bootdelay=%d\\n\\n\
s = getenv (\"bootcmd\");
debug (\"### main_loop: bootcmd=\\\"%s\\\"\\n\
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
}
#endif /* CONFIG_BOOTDELAY */
/*
* Main Loop for Monitor Command Processing
*/
#ifdef CONFIG_SYS_HUSH_PARSER parse_file_outer();
/* This point is never reached */
for (;;);
#endif /*CONFIG_SYS_HUSH_PARSER*/ }
54
好了,UBOOT已经起来了。最终公司做成20M产品。还要仔细分析是如何启动LINUX KERNEL,及文件系统的。这只是刚刚开始。相信后面的更加精彩。UBOOT启动可以说告一段落。
55
5 LINUX KERNEL引导启动
4.1启动命令解析
对于自动运行镜像文件来说,需要在dts文件中配置正确的地址。UBOOT从FLASH中找到地址后读取并执行。内核与文件系统都是通过命令来启动。
#define Struct_Section __attribute__ ((unused,section (\".u_boot_cmd\")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \\
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
U_BOOT_CMD进行展开就是
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
\\
cmd_tbl_t __u_boot_cmd_##name __attribute__ ((unused,section (\".u_boot_cmd\"))) = {#name, maxargs, rep, cmd, usage}
每次定义一个CMD命令,将其放入 .u_boot_cmd段中。
56
先看看uboot.lds文件 OUTPUT_ARCH(powerpc) SECTIONS {
. = + SIZEOF_HEADERS; 。。。。。 .text : {
cpu/mpc83xx/start.o (.text) *(.text) 。。。。。 } 。。。。 .data : { *(.data) *(.data1) *(.sdata) *(.sdata2) *(.dynamic) CONSTRUCTORS }
_edata = .;
PROVIDE (edata = .);
57
. = .;
__u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; 。。。。。 }
ENTRY(_start)
程序分析命令时候在__u_boot_cmd_start与__u_boot_cmd_end之间将其与指定的命令对比,找到则执行相应的函数。UBOOT就是这样分析命令的。启动内核命令如下:
U_BOOT_CMD(
bootm, CONFIG_SYS_MAXARGS,1,
do_bootm,。。。。。。}
4.2 do_bootm
这个函数比较复杂,但还是不能偷懒、
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
/* determine if we have a sub command */ if (argc > 1) {
char *endp;
58
ulong ulong int
ret;
iflag; load_end = 0;
boot_os_fn *boot_fn;
//bootm命令有参数的条件下
simple_strtoul(argv[1], &endp, 16);
/* endp pointing to NULL means that argv[1] was just a * valid number, pass it along to the normal bootm processing *
* If endp is ':' or '#' assume a FIT identifier so pass * along for normal processing. *
* Right now we assume the first arg should never be '-' */
if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
return do_bootm_subcommand(cmdtp, flag, argc, argv);
}
if (bootm_start(cmdtp, flag, argc, argv))//检查位于FLASH中的镜像头部
return 1;
/*
* We have reached the point of no return: we are going to * overwrite all exception vector code, so we cannot easily * recover from any failures any more... */
iflag = disable_interrupts();
59
#ifdef CONFIG_AMIGAONEG3SE
/*
* We've possible left the caches enabled during * bios emulation, so turn them off again */
icache_disable(); dcache_disable();
#endif
if (ret < 0) {
if (ret == BOOTM_ERR_RESET)
do_reset (cmdtp, flag, argc, argv);
ret = bootm_load_os(images.os, &load_end, 1);//加载并解压操作系统到0x0200_0000
if (ret == BOOTM_ERR_OVERLAP) {
if (images.legacy_hdr_valid) {
if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
puts (\"WARNING: legacy format multi component \"
\"image overwritten\\n\");
} else {
puts (\"ERROR: new format image overwritten - \"
\"must RESET the board to recover\\n\");
show_boot_progress (-113);
60
}
}
}
do_reset (cmdtp, flag, argc, argv);
if (ret == BOOTM_ERR_UNIMPLEMENTED) { }
if (iflag)
enable_interrupts();
show_boot_progress (-7); return 1;
lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));
if (images.os.type == IH_TYPE_STANDALONE) { }
show_boot_progress (8);
if (iflag)
enable_interrupts();
/* This may return when 'autostart' is 'no' */ bootm_start_standalone(iflag, argc, argv); return 0;
boot_fn = boot_os[images.os.os];//找到启动操作系统的函数,后面给出具体函数及代码
61
if (boot_fn == NULL) { }
arch_preboot_os();
boot_fn(0, argc, argv, &images); show_boot_progress (-9);
if (iflag)
enable_interrupts();
printf (\"ERROR: booting os '%s' (%d) is not supported\\n\
genimg_get_os_name(images.os.os), images.os.os);
show_boot_progress (-8); return 1;
#ifdef DEBUG
puts (\"\\n## Control returned to monitor - resetting...\\n\");
#endif }
从整个执行的情况来看,该函数执行的大致过程为:
1. 检查kernel,FS,FDT镜像文件头部并得到文件相关信息:文件MAGIC,CRC校验,文
件大小,加载地址等等 2. 解压加载kernel到指定地址0x0200_0000 3. 跳转到内存中kernel代码去执行。
62
do_reset (cmdtp, flag, argc, argv); return 1;
下面看看boot_fn是什么东东
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) {
/* Linux kernel load address */
void (*kernel) (void) = (void (*)(void))images->ep;//这个地址前面保存过,记得吗?这个//是在mpc8308rru.h中配置的指定地址,即loadaddr. /* empty_zero_page */ unsigned char *param
= (unsigned char *)image_get_load(images->legacy_hdr_os);
/* Linux kernel command line */ char *cmdline = (char *)param + 0x100; /* PAGE_SIZE */
unsigned long size = images->ep - (unsigned long)param; char *bootargs = getenv(\"bootargs\");
if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
return 1;
/* Setup parameters */
memset(param, 0, size); /* Clear zero page */ strcpy(cmdline, bootargs);
kernel();
/* does not return */
63
}
return 1;
相信通过这个函数,已经很清楚内核是如何跳转过去执行的以及参数是如何传递的。
4.3 UBOOT分析小结
到目前位置,UBOOT把控制权交给了kernel,通过这段时间的分析,应该对UBOOT启动过程了如指掌。UBOOT虽然是一个提供引导的程序,但可借鉴的地方很多。下面列出我的一些收获。
1. 掌握系统启动的一些配置:如FLASH,DDR,PLL等系统启动比较重要配置 2. 掌握UBOOT移植流程,对哪些最容易出现问题很清楚。
3. 从UBOOT上电找到第一条指令,初始化CPU核,复制并跳转到内存中执行。 4. 将kernel复制到RAM中并将控制权交给kernel执行 5. 对UBOOT如何向KERNEL传递参数清楚
6. 对UBOOT整个内存空间和FLASH空间分布及规划很清楚
7. 对指定如何从LINUX用户空间写驱动提供一种思路。对快速驱动开发有一个较
大的提升空间。 8. 写底层代码参考性很大,如驱动的配置,还有一些压缩解压算法等等。
UBOOT虽然移植完了,但是对学linux还是第一步,需要继续努力。。。。。。下面请看LINUX部分。
64
6 LINUX KERNEL篇
这里就不再对启动汇编一一分析,那样长时间不能进入主题。因为有了UBOOT汇编启 动基础,只是区别主要在于MMU开启与否。 在head32.s文件中有几个在外面执行的函数。 early_init machine_init 这些函数中使用到了用source insight找不到的变量位于vmlinux.lds链接脚本中。
65
因篇幅问题不能全部显示,请点此查看更多更全内容