您好,欢迎来到爱站旅游。
搜索
您的当前位置:首页UBOOT+LINUX上电启动

UBOOT+LINUX上电启动

来源:爱站旅游
基于MPC8308 uboot全程启动

【本文导读】:本文基于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

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- azee.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务