当前位置:文档之家› U-BOOT启动过程

U-BOOT启动过程

U-BOOT启动过程
U-BOOT启动过程

U-Boot启动过程

U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能

?硬件设备初始化

?加载U-Boot第二阶段代码到RAM空间

?设臵好栈

?跳转到第二阶段代码入口

(2)第二阶段的功能

?初始化本阶段使用的硬件设备

?检测系统内存映射

?将内核从Flash读取到RAM中

?为内核设臵启动参数

?调用内核

1.1.1U-Boot启动第一阶段代码分析

第一阶段对应的文件是cpu/arm920t/start.S和

board/samsung/mini2440/lowlevel_init.S。

U-Boot启动第一阶段流程如下:

图 2.1 U-Boot启动第一阶段流程根据cpu/arm920t/u-boot.lds中指定的连接方式:

ENTRY(_start)

SECTIONS

{

. = 0x00000000;

. = ALIGN(4);

.text :

{

cpu/arm920t/start.o (.text)

board/samsung/mini2440/lowlevel_init.o (.text)

board/samsung/mini2440/nand_read.o (.text)

*(.text)

}

……

}

第一个链接的是cpu/arm920t/start.o,因此u-boot.bin的入口代码在

cpu/arm920t/start.o中,其源代码在cpu/arm920t/start.S中。下面我们来分析cpu/arm920t/start.S的执行。

1.硬件设备初始化

(1)设臵异常向量

cpu/arm920t/start.S开头有如下的代码:

.globl _start

_start: b start_code /* 复位*/

ldr pc, _undefined_instruction /* 未定义指令向量 */

ldr pc, _software_interrupt /* 软件中断向量*/

ldr pc, _prefetch_abort /* 预取指令异常向量 */

ldr pc, _data_abort /* 数据操作异常向量 */

ldr pc, _not_used /* 未使用 */

ldr pc, _irq /* irq中断向量 */

ldr pc, _fiq /* fiq中断向量 */

/* 中断向量表入口地址 */

_undefined_instruction: .word undefined_instruction

_software_interrupt: .word software_interrupt

_prefetch_abort: .word prefetch_abort

_data_abort: .word data_abort

_not_used: .word not_used

_irq: .word irq

_fiq: .word fiq

.balignl 16,0xdeadbeef

以上代码设臵了ARM异常向量表,各个异常向量介绍如下:

表 2.1 ARM异常向量表

在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。

其中复位异常向量的指令“b start_code”决定了U-Boot启动后将自动跳转到标号“start_code”处执行。

(2)CPU进入SVC模式

start_code:

/*

* set the cpu to SVC32 mode

*/

mrs r0, cpsr

bic r0, r0, #0x1f /* 工作模式位清零 */

orr r0, r0, #0xd3 /* 工作模式位设臵为“10011”(管理模式),并将中断禁止位和快中断禁止位臵1 */

msr cpsr, r0

以上代码将CPU的工作模式位设臵为管理模式,并将中断禁止位和快中断禁止位臵一,从而屏蔽了IRQ和FIQ中断。

(3)设臵控制寄存器地址

#if defined(CONFIG_S3C2400)

#define pWTCON 0x15300000

#define INTMSK 0x14400008

#define CLKDIVN 0x14800014

#else /* s3c2410与s3c2440下面4个寄存器地址相同 */

#define pWTCON 0x53000000 /*看门狗寄存器*/

#define INTMSK 0x4A000008 /*中断屏蔽寄存器*/

#define INTSUBMSK 0x4A00001C /*次级中断屏蔽寄存器*/

#define CLKDIVN 0x4C000014 /*时钟分频寄存器*/

#endif

对于s3c2440开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设臵。

(4)关闭看门狗

ldr r0, =pWTCON

mov r1, #0x0

str r1, [r0] /*看门狗控制器的最低位为0时,看门狗不输出复位信号 */

以上代码向看门狗控制寄存器写入0,关闭看门狗。否则在U-Boot启动过程中,CPU将不断重启。

(5)屏蔽中断

/*

* mask all IRQs by setting all bits in the INTMR - default

*/

mov r1, #0xffffffff /*某位被臵1则对应的中断被屏蔽*/

ldr r0, =INTMSK

str r1, [r0]

INTMSK是主中断屏蔽寄存器,每一位对应SRCPND(中断源引脚寄存器)中的一位,表明SRCPND 相应位代表的中断请求是否被CPU所处理。

INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK 寄存器全部位臵一,从而屏蔽对应的中断。

#if defined(CONFIG_S3C2440)

ldr r1, =0x7fff

ldr r0, =INTSUBMSK

str r1, [r0]

#endif

INTSUBMSK是次级中断屏蔽器,每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。

INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)臵一,从而屏蔽对应的中断。

为什么要屏蔽所有中断?

中断处理中ldr pc是将代码的编译地址放在了指针上,而这段时间还没有搬移代码,所以编译地址上面没有这个代码,如果进行跳转就会跳转到空指针上面

(6)设臵MPLLCON,UPLLCON, CLKDIVN

#if defined(CONFIG_S3C2440)

#define MPLLCON 0x4C000004

#define UPLLCON 0x4C000008

ldr r0, =CLKDIVN

mov r1, #5

str r1, [r0]

ldr r0, =MPLLCON

ldr r1, =0x7F021

str r1, [r0]

ldr r0, =UPLLCON

ldr r1, =0x38022

str r1, [r0]

# else

/* FCLK:HCLK:PCLK = 1:2:4 */

/* default FCLK is 120 MHz ! */

ldr r0, =CLKDIVN

mov r1, #3

str r1, [r0]

#endif

CPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。这就需要设臵CLKDIVN,MPLLCON,UPLLCON这3个寄存器。

CLKDIVN寄存器用于设臵FCLK,HCLK,PCLK三者间的比例,可以根据表2.2来设臵。

表 2.2 S3C2440 的CLKDIVN寄存器格式

设臵CLKDIVN为5,就将HDIVN设臵为二进制的10,由于CAMDIVN[9]没有被改变过,取默认值0,因此HCLK = FCLK/4。PDIVN被设臵为1,因此PCLK= HCLK/2。因此分频比FCLK:HCLK:PCLK = 1:4:8 。

MPLLCON寄存器用于设臵FCLK与Fin的倍数。MPLLCON的位[19:12]称为MDIV,位[9:4]称为PDIV,位[1:0]称为SDIV。

对于S3C2440,FCLK与Fin的关系如下面公式:

MPLL(FCLK) = (2×m×Fin)/(p×2^s)

其中: m=MDIV+8,p=PDIV+2,s=SDIV

MPLLCON与UPLLCON的值可以根据参考文献4中“PLL VALUE SELECTION TABLE”设臵。该表部分摘录如下:

表 2.3 推荐PLL值

当mini2440系统主频设臵为405MHZ,USB时钟频率设臵为48MHZ时,系统可以稳定运行,因此设臵MPLLCON与UPLLCON为:

MPLLCON=(0x7f<<12) | (0x02<<4) | (0x01) = 0x7f021

UPLLCON=(0x38<<12) | (0x02<<4) | (0x02) = 0x38022

为什么要设臵时钟?

起始可以不设,系统能不能跑起来和频率没有任何关系,频率的设臵是要让外围的设备能承受所设臵的频率,如果频率过高则会导致cpu操作外围设备失败

说白了:设臵频率,就为了CPU能去操作外围设备

(7)关闭MMU,cache

接着往下看:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl cpu_init_crit

#endif

cpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。

下面分析一下cpu_init_crit到底做了什么:

320#ifndef CONFIG_SKIP_LOWLEVEL_INIT

321 cpu_init_crit:

322 /*

323 * 使数据cache与指令cache无效 */

324 */

325 mov r0, #0

326 mcr p15, 0, r0, c7, c7, 0 /*向c7写入0将使ICache与DCache无效*/

327 mcr p15, 0, r0, c8, c7, 0 /*向c8写入0将使TLB失效*/

328

329 /*

330 * disable MMU stuff and caches

331 */

332 mrc p15, 0, r0, c1, c0, 0 /* 读出控制寄存器到r0中 */

333 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)

334 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)

335 orr r0, r0, #0x00000002 @ set bit 1 (A) Align

336 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache

337 mcr p15, 0, r0, c1, c0, 0 /* 保存r0到控制寄存器 */

338

339 /*

340 * before relocating, we have to setup RAM timing

341 * because memory timing is board-dependend, you will

342 * find a lowlevel_init.S in your board directory.

343 */

344 mov ip, lr

345

346 bl lowlevel_init

347

348 mov lr, ip

349 mov pc, lr

350#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。其中c7是cache控制寄存器,c8是TLB控制寄存器。325--327行代码将0写入c7、c8,使Cache,TLB内容无效。

第332--337行代码关闭了MMU。这是通过修改CP15的c1寄存器来实现的,先看CP15的c1寄存器的格式(仅列出代码中用到的位):

表 2.3 CP15的c1寄存器格式(部分)

各个位的意义如下:

V : 表示异常向量表所在的位臵,0:异常向量在0x00000000;1:异常向量在 0xFFFF0000

I : 0:关闭ICaches;1:开启ICaches

R、S : 用来与页表中的描述符一起确定内存的访问权限

B : 0:CPU为小字节序;1:CPU为大字节序

C : 0:关闭DCaches;1:开启DCaches

A : 0:数据访问时不进行地址对齐检查; 1:数据访问时进行地址对齐检查

M : 0:关闭MMU;1:开启MMU

332--337行代码将c1的 M位臵零,关闭了MMU。

为什么要关闭cache和MMU呢?cache和MMU是做什么用的?

MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授

概述:

一.关于ca che

Cache和MMU是通过CP15管理的,刚上电的时候,CPU还不能管理他们,上电的时候MMU必须关闭,指令Cache可关闭,可不关闭,但数据Cache一定要关闭,否则可能导致刚开始的代码去取数据的时候,从Cache里面取,而这时候RAM中数据还没有Cache过来,导致数据预取异常。

二. 关于MMU

因为MMU的作用是把虚拟地址转化为物理地址,而目的是设臵控制寄存器,控制寄存器本来就是实地址(物理地址),再使能MMU,不就是多此一举了吗?

详细分析---

Cache是cpu内部的一个2级缓存,它的作用是将常用的数据和指令放在cpu内部,MMU是用来把虚实地址转换为物理地址用的。

我们的目的:是设臵控制的寄存器,寄存器都是实地址(物理地址),如果既要开启MMU又要做虚实地址转换的话,中间还多一步,多此一举了嘛?

先要把实地址转换成虚地址,然后再做设臵,但对uboot而言就是起到一个简单的初始化的作用和引导操作系统,如果开启MMU的话,很麻烦,也没必要,所以关闭MMU。

说到cache就必须提到一个关键字Volatile,以后在设臵寄存器时会经常遇到,他的本质:是告诉编译器不要对我的代码进行优化,作用是让编写者感觉不倒变量的变化情况(也就是说,让它执行速度加快吧)优化的过程:是将常用的数据取出来放到cache中,它没有从实际的物理地址去取,它直接从cpu的缓存中去取,但常用的代码就是为了感觉一些常用变量的变化

优化原因:如果正在取数据的时候数据发生跳变,那么就感觉不到变量的变化了,所以在这种情况下要用Volatile关键字告诉编译器不要做优化,每次从实际的物理地址中去取指令,这就是为什么关闭cache关闭MMU。

但在C语言中是不会关闭catch和MMU的,会打开,如果编写者要感觉外界变化,或变化太快,从cache

中取数据会有误差,就加一个关键字Volatile。

(8)初始化RAM控制寄存器

其中的lowlevel_init就完成了内存初始化的工作,由于内存初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board下面相应的目录中。对于mini2440,lowlevel_init 在board/samsung/mini2440/lowlevel_init.S中定义如下:

45#define BWSCON 0x48000000 /*13个存储控制器的起始地址*/

……

129 _TEXT_BASE:

130 .word TEXT_BASE

131

132 .globl lowlevel_init

133 lowlevel_init:

134 /* memory control configuration */

135 /* make r0 relative the current location so that it */

136 /* reads SMRDATA out of FLASH rather than memory ! */

137 ldr r0, =SMRDATA

138 ldr r1, _TEXT_BASE

139 sub r0, r0, r1 /* r0-r1即13个寄存器的偏移地址 */

140 ldr r1, =BWSCON /* Bus Width Status Controller */

141 add r2, r0, #13*4

142 0:

143 ldr r3, [r0], #4/* 将13个寄存器的值逐一赋值给对应的寄存器 */

144 str r3, [r1], #4

145 cmp r2, r0

146 bne 0b

147

148 /* everything is fine now */

149 mov pc, lr

150

151 .ltorg

152 /* the literal pools origin */

153

154 SMRDATA: /* 下面是13个寄存器的值 */

155 .word ……

156 .word ……

……

lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。lowlevel_init函数对于

U-Boot从NAND Flash或NOR Flash启动的情况都是有效的。

U-Boot.lds链接脚本有如下代码:

.text :

{

cpu/arm920t/start.o (.text)

board/samsung/mini2440/lowlevel_init.o (.text)

board/samsung/mini2440/nand_read.o (.text)

……

}

board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o后面,因此board/samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。

U-Boot在NAND Flash启动时,lowlevel_init.o将自动被读取到CPU内部4KB的内部RAM 中。因此第137--146行的代码将从CPU内部RAM中复制寄存器的值到相应的寄存器中。

对于U-Boot在NOR Flash启动的情况,由于U-Boot连接时确定的地址是U-Boot在内存中的地址,而此时U-Boot还在NOR Flash中,因此还需要从NOR Flash中读取数据到RAM中。

由于NOR Flash的开始地址是0,而U-Boot加载到内存的起始地址是TEXT_BASE,SMRDATA标号在Flash的地址就是SMRDATA-TEXT_BASE。

综上所述,lowlevel_init的作用就是将SMRDATA开始的13个值复制给开始地址[BWSCON]的13个寄存器,从而完成了存储控制器的设臵。

问题一:如果换一块开发板有可能改哪些东西?

首先,cpu的运行模式,如果需要对cpu进行设臵那就设臵,管看门狗,关中断不用改,时钟有可能要改,如果能正常使用则不用改,关闭catch和MMU不用改,设臵bank有可能要改。最后一步拷贝时看地址会不会变,如果变化也要改,执行内存中代码,地址有可能要改。

问题二:Nor Flash和Nand Flash本质区别:

就在于是否进行代码拷贝,也就是下面代码所表述:无论是Nor Flash还是Nand Flash,核心思想就是将uboot代码搬运到内存中去运行,但是没有拷贝bss后面这段代码,只拷贝bss 前面的代码,bss代码是放臵全局变量的。Bss段代码是为了清零,拷贝过去再清零重复操作

(9)复制U-Boot第二阶段代码到RAM

cpu/arm920t/start.S原来的代码是只支持从NOR Flash启动的,经过修改现在U-Boot在NOR Flash和NAND Flash上都能启动了,实现的思路是这样的:

bl bBootFrmNORFlash /*判断U-Boot是在NAND Flash还是NOR Flash启动*/

cmp r0, #0 /*r0存放bBootFrmNORFlash函数返回值,若返回0表示

NAND Flash启动,否则表示在NOR Flash启动*/

beq nand_boot /* 跳转到NAND Flash启动代码 */

/* NOR Flash启动的代码 */

b stack_setup /* 跳过NAND Flash启动的代码 */

nand_boot:

/* NAND Flash启动的代码 */

stack_setup:

/* 其他代码 */

其中bBootFrmNORFlash函数作用是判断U-Boot是在NAND Flash启动还是NOR Flash启动,若在NOR Flash启动则返回1,否则返回0。根据ATPCS规则,函数返回值会被存放在r0寄存器中,因此调用bBootFrmNORFlash函数后根据r0的值就可以判断U-Boot在NAND Flash启动还是NOR Flash启动。bBootFrmNORFlash函数在board/samsung/mini2440/nand_read.c中定义如下:

int bBootFrmNORFlash(void)

{

volatile unsigned int *pdw = (volatile unsigned int *)0;

unsigned int dwVal;

dwVal = *pdw; /*先记录下原来的数据*/

*pdw = 0x12345678;

if (*pdw != 0x12345678) /*写入失败,说明是在NOR Flash启动*/

{

return 1;

}

else /*写入成功,说明是在NAND Flash启动*/

{

*pdw = dwVal; /*恢复原来的数据*/

return 0;

}

}

无论是从NOR Flash还是从NAND Flash启动,地址0处为U-Boot的第一条指令“b

start_code”。

对于从NAND Flash启动的情况,其开始4 KB的代码会被自动复制到CPU内部4K内存中,因此可以通过直接赋值的方法来修改。

对于从NOR Flash启动的情况,NOR Flash的开始地址即为0,必须通过一定的命令序列才能向NOR Flash中写数据,所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:向地址0写入一个数据,然后读出来,如果发现写入失败的就是NOR Flash,否则就是NAND Flash。

下面来分析NOR Flash启动部分代码:

208 adr r0, _start /* r0 <- current position of code */

209 ldr r1, _TEXT_BASE /*test if we run from flash or RAM*/

/* 判断U-Boot是否是下载到RAM中运行,若是,则不用再复制到RAM中了,这种情况通常在调试U-Boot 时才发生 */

210 cmp r0, r1/*_start等于_TEXT_BASE说明是下载到RAM中运行 */

211 beq stack_setup

/*以下直到nand_boot标号前都是NOR Flash启动的代码*/

213 ldr r2, _armboot_start

214 ldr r3, _bss_start

215 sub r2, r3, r2 /* r2 <- size of armboot */

216 add r2, r0, r2 /* r2 <- source end address */

/* 搬运U-Boot自身到RAM中*/

218 copy_loop:

219 ldmia r0!, {r3-r10} /*从地址为[r0]的NOR Flash中读入8个字的数据*/

220 stmia r1!, {r3-r10} /*将r3至r10寄存器的数据复制给地址为[r1]的内存*/

221 cmp r0, r2 /* until source end addreee [r2] */

222 ble copy_loop

223 b stack_setup /* 跳过NAND Flash启动的代码 */

下面再来分析NAND Flash启动部分代码:

nand_boot:

mov r1, #NAND_CTL_BASE

ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )

str r2, [r1, #oNFCONF] /* 设臵NFCONF寄存器 */

/* 设臵NFCONT,初始化ECC编/解码器,禁止NAND Flash片选 */

ldr r2, =( (1<<4)|(0<<1)|(1<<0) )

str r2, [r1, #oNFCONT]

ldr r2, =(0x6) /* 设臵NFSTAT */

str r2, [r1, #oNFSTAT]

/* 复位命令,第一次使用NAND Flash前复位 */

mov r2, #0xff

strb r2, [r1, #oNFCMD]

mov r3, #0

/* 为调用C函数nand_read_ll准备堆栈 */

ldr sp, DW_STACK_START

mov fp, #0

/*下面先设臵r0至r2,然后调用nand_read_ll函数将U-Boot读入RAM */

ldr r0, =TEXT_BASE /*目的地址:U-Boot在RAM的开始地址*/

mov r1, #0x0 /*源地址:U-Boot在NAND Flash中的开始地址*/

mov r2, #0x30000 /*复制的大小,必须比u-boot.bin文件大,并且必须是

NAND Flash块大小的整数倍,这里设臵为0x30000(192KB)*/

bl nand_read_ll /*跳转到nand_read_ll函数,开始复制U-Boot到RAM */ tst r0, #0x0 /*检查返回值是否正确*/

beq stack_setup

bad_nand_read:

loop2: b loop2 //infinite loop

.align 2

DW_STACK_START: .word STACK_BASE+STACK_SIZE-4

其中NAND_CTL_BASE,oNFCONF等在include/configs/mini2440.h中定义如下:#define NAND_CTL_BASE 0x4E000000 /* NAND Flash控制寄存器基址 */

#define STACK_BASE 0x33F00000 //base address of stack

#define STACK_SIZE 0x8000 //size of stack

#define oNFCONF 0x00 /* NFCONF相对于NAND_CTL_BASE偏移地址 */

#define oNFCONT 0x04 /* NFCONT相对于NAND_CTL_BASE偏移地址 */

#define oNFADDR 0x0c /* NFADDR相对于NAND_CTL_BASE偏移地址 */

#define oNFDATA 0x10/* NFDATA相对于NAND_CTL_BASE偏移地址 */

#define oNFCMD 0x08 /* NFCMD相对于NAND_CTL_BASE偏移地址 */

#define oNFSTAT 0x20 /* NFSTAT相对于NAND_CTL_BASE偏移地址 */

#define oNFECC 0x2c /* NFECC相对于NAND_CTL_BASE偏移地址 */

NAND Flash各个控制寄存器的设臵在S3C2440的数据手册有详细说明,这里就不介绍了。

代码中nand_read_ll函数的作用是在NAND Flash中搬运U-Boot到RAM,该函数在

board/samsung/mini2440/nand_read.c中定义。

NAND Flash根据page大小可分为2种: 512B/page和2048B/page的。这两种NAND Flash 的读操作是不同的。因此就需要U-Boot识别到NAND Flash的类型,然后采用相应的读操作,也就是说nand_read_ll函数要能自动适应两种NAND Flash。

参考S3C2440的数据手册可以知道:根据NFCONF寄存器的Bit3(AdvFlash (Read only))和Bit2 (PageSize (Read only))可以判断NAND Flash的类型。Bit2、Bit3与NAND Flash 的block类型的关系如下表所示:

表 2.4 NFCONF的Bit3、Bit2与NAND Flash的关系

由于NAND Flash只有512B/page和2048 B/page这两种,因此根据NFCONF寄存器的Bit3即可区分这两种NAND Flash了。

完整代码见board/samsung/mini2440/nand_read.c中的nand_read_ll函数,这里给出伪代码:

int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)

{

/*根据NFCONF寄存器的Bit3来区分2种NAND Flash*/

If ( NFCONF & 0x8 ) /* Bit是1,表示是2KB/page的NAND Flash */

{

读取2K block 的NAND Flash

}

else /* Bit是0,表示是512B/page的NAND Flash */

{

读取512B block 的NAND Flash

}

return 0;

}

(10)设臵堆栈

/* 设臵堆栈 */

stack_setup:

ldr r0, _TEXT_BASE /*upper 128 KiB: relocated uboot */

sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */

sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /*跳过全局数据区*/

#ifdef CONFIG_USE_IRQ

sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

sub sp, r0, #12 /* leave 3 words for abort-stack */

只要将sp指针指向一段没有被使用的内存就完成栈的设臵了。根据上面的代码可以知道U-Boot内存使用情况了,如下图所示:

图2.2 U-Boot内存使用情况

(11)清除BSS段

clear_bss:

ldr r0, _bss_start /* BSS段开始地址,在u-boot.lds中指定*/

ldr r1, _bss_end /* BSS段结束地址,在u-boot.lds中指定*/

mov r2, #0x00000000

clbss_l:str r2, [r0] /* 将bss段清零*/

add r0, r0, #4

cmp r0, r1

ble clbss_l

初始值为0,无初始值的全局变量,静态变量将自动被放在BSS段。应该将这些变量的初始值赋为0,否则这些变量的初始值将是一个随机的值,若有些程序直接使用这些没有初始化的变量将引起未知的后果。

(12)跳转到第二阶段代码入口

ldr pc, _start_armboot

_start_armboot: .word start_armboot

跳转到第二阶段代码入口start_armboot处。

1.1.2U-Boot启动第二阶段代码分析

start_armboot函数在lib_arm/board.c中定义,是U-Boot第二阶段代码的入口。U-Boot 启动第二阶段流程如下:

图2.3 U-Boot第二阶段执行流程

在分析start_armboot函数前先来看看一些重要的数据结构:

(1)gd_t结构体

U-Boot使用了一个结构体gd_t来存储全局数据区的数据,这个结构体在

include/asm-arm/global_data.h中定义如下:

typedef struct global_data {

bd_t *bd;

unsigned long flags;

unsigned long baudrate;

unsigned long have_console; /* serial_init() was called */

unsigned long env_addr; /* Address of Environment struct */

unsigned long env_valid; /* Checksum of Environment valid? */

unsigned long fb_base; /* base address of frame buffer */

void **jt; /* jump table */

} gd_t;

U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址:

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8") DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。

根据U-Boot内存使用图中可以计算gd的值:

gd = TEXT_BASE -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)

(2)bd_t结构体

bd_t在include/asm-arm.u/u-boot.h中定义如下:

typedef struct bd_info {

int bi_baudrate; /* 串口通讯波特率 */

unsigned long bi_ip_addr; /* IP 地址*/

struct environment_s *bi_env; /* 环境变量开始地址 */

ulong bi_arch_number; /* 开发板的机器码 */

ulong bi_boot_params; /* 内核参数的开始地址 */

struct /* RAM配臵信息 */

{

ulong start;

ulong size;

}bi_dram[CONFIG_NR_DRAM_BANKS];

} bd_t;

U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,bd_t结构体中的信息来设臵标记列表。

(3)init_sequence数组

U-Boot使用一个数组init_sequence来存储对于大多数开发板都要执行的初始化函数的函数指针。init_sequence数组中有较多的编译选项,去掉编译选项后init_sequence数组如下所示:typedef int (init_fnc_t) (void);

init_fnc_t *init_sequence[] = {

board_init, /* 开发板相关的配臵--board/samsung/mini2440/mini2440.c */

timer_init, /* 时钟初始化-- cpu/arm920t/s3c24x0/timer.c */

env_init, /* 初始化环境变量--common/env_flash.c或common/env_nand.c */

init_baudrate, /* 初始化波特率-- lib_arm/board.c */

serial_init, /* 串口初始化-- drivers/serial/serial_s3c24x0.c */

console_init_f,/* 控制通讯台初始化阶段1-- common/console.c */

display_banner, /* 打印U-Boot版本、编译的时间-- gedit lib_arm/board.c */

dram_init, /* 配臵可用的RAM-- board/samsung/mini2440/mini2440.c */

display_dram_config, /* 显示RAM大小-- lib_arm/board.c */

NULL,

};

其中的board_init函数在board/samsung/mini2440/mini2440.c中定义,该函数设臵了MPLLCOM,UPLLCON,以及一些GPIO寄存器的值,还设臵了U-Boot机器码和内核启动参数地址:/* MINI2440开发板的机器码 */

gd->bd->bi_arch_number = MACH_TYPE_MINI2440;

/* 内核启动参数地址 */

gd->bd->bi_boot_params = 0x30000100;

其中的dram_init函数在board/samsung/mini2440/mini2440.c中定义如下:

int dram_init (void)

{

/* 由于mini2440只有 */

gd->bd->bi_dram[0].start = PHYS_SDRAM_1;

gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

UBOOT命令详解

常用U-boot命令详解(z) 2010-09-30 15:05:52| 分类:学习心得体会|字号订阅 U-boot发展到现在,他的命令行模式已经非常接近Linux下的shell了,在我编译的 U-boot-2009.11中的命令行模式模式下支持“Tab”键的命令补全和命令的历史记录功能。而且如果你输入的命令的前几个字符和别的命令不重复,那么你就只需要打这几个字符即可,比如我想看这个U-boot的版本号,命令就是“ version”,但是在所有的命令中没有其他任何一个的命令是由“v”开头的,所以只需要输入“v”即可。 [u-boot@MINI2440]# version U-Boot 2009.11 ( 4月04 2010 - 12:09:25) [u-boot@MINI2440]# v U-Boot 2009.11 ( 4月04 2010 - 12:09:25) [u-boot@MINI2440]# base Base Address: 0x00000000 [u-boot@MINI2440]# ba Base Address: 0x00000000 由于U-boot支持的命令实在太多,一个一个细讲不现实,也没有必要。所以下面我挑一些烧写和引导常用命令介绍一下,其他的命令大家就举一反三,或者“help”吧! (1)获取帮助 命令:help 或? 功能:查看当前U-boot版本中支持的所有命令。 [u-boot@MINI2440]#help ?- alias for'help' askenv - get environment variables from stdin base - print or set address offset bdinfo - print Board Info structure bmp - manipulate BMP image data boot - boot default, i.e., run 'bootcmd' bootd - boot default, i.e., run 'bootcmd' bootelf - Boot from an ELF image in memory bootm - boot application image from memory bootp - boot image via network using BOOTP/TFTP protocol

Tiny6410_Uboot移植步骤详解

Uboot_for_Tiny6410_移植步骤详解 一、设计要求 1.目的 1)掌握U-boot剪裁编写 2)掌握交叉编译环境的配置 3)掌握U-boot的移植 2.实现的功能 1)U-boot编译成功 2)移植U-boot,使系统支持从NAND FLASH启动 二、设计方案 1.硬件资源 1)ARM处理器:ARM11芯片(Samsung S3C6410A),基于ARM1176JZF-S核设 计,运行频率533Mhz,最高可达 667Mhz 2)存储器:128M DDR RAM,可升级至 256M;MLC NAND Flash(2GB) 3)其他资源:具有三LCD接口、4线电阻 触摸屏接口、100M标准网络接口、标准DB9 五线串口、Mini USB2.0接口、USB Host 1.1、3.5mm音频输入输出口、标准TV-OUT

接口、SD卡座、红外接收等常用接口;另外 还引出4路TTL串口,另1路TV-OUT、 SDIO2接口(可接SD WiFi)接口等;在板的 还有蜂鸣器、I2C-EEPROM、备份电池、A D 可调电阻、8个中断式按键等。 2.软件资源 1)arm-linux-gcc-4.5.1(交叉编译) 2)u-boot-2010.09.tar.gz arm-linux-gcc-4.5.1-v6-vfp-20101103.t gz 三、移植过程 1.环境搭建 1)建立交叉编译环境 2)去这2个网站随便下载都可以下载得到最 新或者你想要的u-boot。( https://www.doczj.com/doc/2b6867047.html,/batch.viewl ink.php?itemid=1694 ftp://ftp.denx.de/pub/u-boot/ )

UBoot移植详解

u-boot 移植步骤详解 1 U-Boot简介 U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux 系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11 月PPCBOOT 改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。 选择U-Boot的理由: ①开放源码; ②支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS; ③支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale; ④较高的可靠性和稳定性; ④较高的可靠性和稳定性; ⑤高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等; ⑥丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等; ⑦较为丰富的开发调试文档与强大的网络技术支持; 2 U-Boot主要目录结构 - board 目标板相关文件,主要包含SDRAM、FLASH驱动; - common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测;

i.MX6UL -- Linux系统移植过程详解(最新的长期支持版本)

i.MX6UL -- Linux系统移植过程详解(最新的长期支持版本) ?开发平台:i.MX 6UL ?最新系统: u-boot2015.04 + Linux4.1.15_1.2.0 ?交叉编译工具:dchip-linaro-toolchain.tar.bz2 源码下载地址: U-Boot: (选择rel_imx_4.1.15_1.2.0_ga.tar.bz2) https://www.doczj.com/doc/2b6867047.html,/git/cgit.cgi/imx/uboot-imx.git/ Kernel: (选择rel_imx_4.1.15_1.2.0_ga.tar.bz2) https://www.doczj.com/doc/2b6867047.html,/git/cgit.cgi/imx/linux-2.6-imx.git/ 源码移植过程: 1、将linux内核及uBoot源码拷贝到Ubuntu12.04系统中的dchip_imx6ul目录下; 2、使用tar命令分别将uboot和kernel解压到dchip_imx6ul目录下; 3、解压后进入uboot目录下,新建文件make_dchip_imx6ul_uboot201504.sh,且文件内容如下: ################################################################### # Build U-Boot.2015.04 For D518--i.MX6UL By FRESXC # ################################################################### #!/bin/bash export ARCH=arm export CROSS_COMPILE=/dchip-linaro-toolchain/bin/arm-none-linux-gnueabi - make mrproper # means CLEAN make mx6ul_14x14_evk_defconfig make2>&1|tee built_dchip_imx6ul_uboot201504.out 4进入kernel目录下,新建文件make_dchip_imx6ul_linux4115120.sh,且文件内容如下: ###################################################################

u-boot启动分析

背景: Board →ar7240(ap93) Cpu →mips 1、首先弄清楚什么是u-boot Uboot是德国DENX小组的开发,它用于多种嵌入式CPU的bootloader程序, uboot不仅支持嵌入式linux系统的引导,当前,它还支持其他的很多嵌入式操作系统。 除了PowerPC系列,还支持MIPS,x86,ARM,NIOS,XScale。 2、下载完uboot后解压,在根目录下,有如下重要的信息(目录或者文件): 以下为为每个目录的说明: Board:和一些已有开发板有关的文件。每一个开发板都以一个子目录出现在当前目录中,子目录存放和开发板相关的配置文件。它的每个子文件夹里都有如下文件(以ar7240/ap93为例): Makefile Config.mk Ap93.c 和板子相关的代码 Flash.c Flash操作代码 u-boot.lds 对应的链接文件 common:实现uboot命令行下支持的命令,每一条命令都对应一个文件。例如bootm命令对应就是cmd_bootm.c cpu:与特定CPU架构相关目录,每一款Uboot下支持的CPU在该目录下对应一个子目录,比如有子目录mips等。它的每个子文件夹里都有入下文件: Makefile Config.mk Cpu.c 和处理器相关的代码s Interrupts.c 中断处理代码 Serial.c 串口初始化代码 Start.s 全局开始启动代码 Disk:对磁盘的支持

Doc:文档目录。Uboot有非常完善的文档。 Drivers:Uboot支持的设备驱动程序都放在该目录,比如网卡,支持CFI的Flash,串口和USB等。 Fs:支持的文件系统,Uboot现在支持cramfs、fat、fdos、jffs2和registerfs。 Include:Uboot使用的头文件,还有对各种硬件平台支持的汇编文件,系统的配置文件和对文件系统支持的文件。该目下configs目录有与开发板相关的配置文件,如 ar7240_soc.h。该目录下的asm目录有与CPU体系结构相关的头文件,比如说mips 对应的有asm-mips。 Lib_xxx:与体系结构相关的库文件。如与ARM相关的库放在lib_arm中。 Net:与网络协议栈相关的代码,BOOTP协议、TFTP协议、RARP协议和NFS文件系统的实现。 Tools:生成Uboot的工具,如:mkimage等等。 3、mips架构u-boot启动流程 u-boot的启动过程大致做如下工作: 1、cpu初始化 2、时钟、串口、内存(ddr ram)初始化 3、内存划分、分配栈、数据、配置参数、以及u-boot代码在内存中的位置。 4、对u-boot代码作relocate 5、初始化malloc、flash、pci以及外设(比如,网口) 6、进入命令行或者直接启动Linux kernel 刚一开始由于参考网上代码,我一个劲的对基于smdk2410的板子,arm926ejs的cpu看了N 久,启动过程和这个大致相同。 整个启动中要涉及到四个文件: Start.S →cpu/mips/start.S Cache.S →cpu/mips/cache.S Lowlevel_init.S →board/ar7240/common/lowlevel_init.S Board.c →lib_mips/board.c 整个启动过程分为两个阶段来看: Stage1:系统上电后通过汇编执行代码 Stage2:通过一些列设置搭建了C环境,通过汇编指令跳转到C语言执行. Stage1: 程序从Start.S的_start开始执行.(至于为什么,参考u-boot.lds分析.doc) 先查看start.S文件吧!~ 从_start标记开始会看到一长串莫名奇妙的代码:

UBoot源码分析1

?UBoot源码解析(一)

主要内容 ?分析UBoot是如何引导Linux内核 ?UBoot源码的一阶段解析

BootLoader概念?Boot Loader 就是在操作系统内核运行之前运行 的一段小程序。通过这段小程序,我们可以初始 化硬件设备、建立内存空间的映射图,从而将系 统的软硬件环境带到一个合适的状态,以便为最 终调用操作系统内核准备好正确的环境 ?通常,Boot Loader 是严重地依赖于硬件而实现 的,特别是在嵌入式世界。因此,在嵌入式世界 里建立一个通用的Boot Loader 几乎是不可能的。 尽管如此,我们仍然可以对Boot Loader 归纳出 一些通用的概念来,以指导用户特定的Boot Loader 设计与实现。

UBoot来源?U-Boot 是 Das U-Boot 的简称,其含义是 Universal Boot Loader,是遵循 GPL 条款的开放源码项目。最早德国 DENX 软件工程中心的 Wolfgang Denk 基于 8xxROM 和 FADSROM 的源码创建了 PPCBoot 工程项目,此后不断 添加处理器的支持。而后,Sysgo Gmbh 把 PPCBoot 移 植到 ARM 平台上,创建了 ARMBoot 工程项目。最终, 以 PPCBoot 工程和 ARMBoot 工程为基础,创建了 U- Boot 工程。 ?而今,U-Boot 作为一个主流、通用的 BootLoader,成功地被移植到包括 PowerPC、ARM、X86 、MIPS、NIOS、XScale 等主流体系结构上的百种开发板,成为功能最多、 灵活性最强,并且开发最积极的开源 BootLoader。目前。 U-Boot 仍然由 DENX 的 Wolfgang Denk 维护

嵌入式Linux之我行 史上最牛最详细的uboot移植,不看别后悔

嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(一) 嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。一为总结经验,二希望能给想入门嵌入式Linux 的朋友提供方便。如有错误之处,谢请指正。 ?共享资源,欢迎转载:https://www.doczj.com/doc/2b6867047.html, 一、移植环境 ?主机:VMWare--Fedora 9 ?开发板:Mini2440--64MB Nand,Kernel:2.6.30.4 ?编译器:arm-linux-gcc-4.3.2.tgz ?u-boot:u-boot-2009.08.tar.bz2 二、移植步骤 本次移植的功能特点包括: ?支持Nand Flash读写 ?支持从Nor/Nand Flash启动 ?支持CS8900或者DM9000网卡 ?支持Yaffs文件系统 ?支持USB下载(还未实现) 1.了解u-boot主要的目录结构和启动流程,如下图。

u-boot的stage1代码通常放在cpu/xxxx/start.S文件中,他用汇编语言写成;u-boot的stage2代码通常放在lib_xxxx/board.c文件中,他用C语言写成。各个部分的流程图如下:

2. 建立自己的开发板项目并测试编译。 目前u-boot对很多CPU直接支持,可以查看board目录的一些子目录,如:board/samsung/目录下就是对三星一些ARM 处理器的支持,有smdk2400、smdk2410和smdk6400,但没有2440,所以我们就在这里建立自己的开发板项目。 1)因2440和2410的资源差不多,主频和外设有点差别,所以我们就在board/samsung/下建立自己开发板的项目,取名叫my2440 2)因2440和2410的资源差不多,所以就以2410项目的代码作为模板,以后再修改

uboot启动常见的错误汇总

【uboot启动常见的错误汇总】 1. operating at 100M full duplex mode *** ERROR: `ethaddr' not set dm9000 i/o: 0x5000000, id: 0x90000a46 DM9000: running in 16 bit mode MAC: 00:00:00:00:00:00 operating at 100M full duplex mode Wrong Image Format for bootm command ERROR: can't get kernel image! 原因是:没有设置mac地址,需要重新设置 setenv ethaddr 01:02:03:04:05:06 saveenv/save 2.在开发板上ping ubuntu的ip地址ping不通 1.网线没插 2.ubuntu没有打开 3.ping 的过程中,ubuntu会扫描ip地址,会一直去获取ip地址,但是开发板没有分配ip地址的权利,也就是ubuntu获取不了ip地址,同时查看ubuntu 的ip地址是没有的。 1.设置临时的ip地址 sudo ifconfig eth0 192.168.1.* 2.永久生效 在ubuntu的右上角添加静态ip地址。 3.发现ubuntu的右上角网络图标类似于wifi的图标,如何将这个图标改成网络的 图标 sudo /etc//init.d/network-manager restart 如何执行之后,还是wifi图标 sudo vi /etc/NetworkManager/NetworkManager.conf managed=false false --->true sudo /etc//init.d/network-manager restart 如果执行之后,还是wifi图标 重启系统 4.你的pc电脑已经打开wifi网,需要将无线网关闭 5.虚拟机网卡设置出错,需要将nat设置为桥接 6.换开发板

iTop4412的uboot第一阶段

2 uboo t 源码分析 2.5.1.star t.S 2.5.1.star t.S 引入引入 2.5.1.1、u-boot.lds中找到start.S入口 (1)在C语言中整个项目的入口就是 main函数(这是 个.c文件的项目,第一个要分析的文件就是包含了C语言规定的),所以譬如说一 个有 main函数的那个文件。 10000 ( 2 方。ENTRY(_start)因此 _start 符号所在的文件就是整个程序的起始文 件, _sta rt 所在处的 代码就是整个程序的起始代码。 2.5.1.2、SourceInsight中如何找到 文件 (1)当前状况:我们知道在uboot中的1000多个文件中有一个符号 叫 _start,但是我们不知道 这个符号在哪个文件中。这种情况下要查找一个符号在所有项目中文件中的引用,要使用SourceInsight的搜索功能。 (2)start.s 在cpu/arm_cortexa9/start.s (3)然后进入start.S文件中,发现 个uboot的入口代码,就是第57 57行中就 是行。_sta rt 标号的定义处,于是乎我们就找到了整 2.5.1.3、SI中找文件技巧 (1)以上,找到了start.S文件,下面我们就从start.S文件开始分析uboot第一阶段。 (2)在SI中,如果我们知道我们要找的文件的名字,但是我们又不知道他在哪个目录下,我 们要怎样找到并打开这个文件?方法是在 SI中先打开右边的工程项目管理栏目,然后点击 最左边那个(这个是以文件为单位来浏览的),然后在上面输入栏中输入要找的文件的名 字。我们在输入的时候,SI在不断帮我们进行匹配,即使你不记得文件的全名只是大概记 得名字,也能帮助你找到你要找的文件。 2.5.2.start.S解析1 2.5.2.1、不简单的头文件包含

uboot_freescale_imx51_start.s_详解

/* * *Purpose: the document is used to learn detailed information aboutimx51 cpu start.S, *referring to some documents on websites. *file address: U-boot-2009.08/Cpu/Arm_cortexa8/start.S * * writer: xfhai 2011.7.22 * *Instruction: *1.@xxxx : indicates annotation *2./***** *** *****/ : stand for code in my files *3.instructions refers to code not included in my file * */ Section 1: uboot overview 大多数bootloader都分为stage1和stage2两部分,u-boot也不例外。依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。 1、Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下:==> (1)定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。 ==>(2)设置异常向量(Exception Vector)。 ==>(3)设置CPU的速度、时钟频率及终端控制寄存器。 ==>(4)初始化内存控制器。 ==>(5)将ROM中的程序复制到RAM中。 ==>(6)初始化堆栈。 ==>(7)转到RAM中执行,该工作可使用指令ldr pc来完成。 2、Stage2 C语言代码部分 lib_arm/board.c中的start arm boot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数只要完成如下操作: ==>(1)调用一系列的初始化函数。 ==>(2)初始化Flash设备。 ==>(3)初始化系统内存分配函数。 ==>(4)如果目标系统拥有NAND设备,则初始化NAND设备。 ==>(5)如果目标系统有显示设备,则初始化该类设备。 ==>(6)初始化相关网络设备,填写IP、MAC地址等。 ==>(7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。

uboot启动代码详解

·1 引言 在专用的嵌入式板子运行GNU/Linux 系统已经变得越来越流行。一个嵌入式Linux 系统从软件的角度看通常可以分为四个层次: 1. 引导加载程序。固化在固件(firmware)中的boot 代码,也就是Boot Loader,它的启动通常分为两个阶段。 2. Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数。 3. 文件系统。包括根文件系统和建立于Flash 内存设备之上文件系统,root fs。 4. 用户应用程序。特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式GUI 有:MicroWindows 和MiniGUI 等。 引导加载程序是系统加电后运行的第一段软件代码。回忆一下PC 的体系结构我们可以知道,PC 机中的引导加载程序由BIOS(其本质就是一段固件程序)和位于硬盘MBR 中的OS Boot Loader(比如,LILO 和GRUB 等)一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘MBR 中的Boot Loader 读到系统的RAM 中,然后将控制权交给OS Boot Loader。Boot Loader 的主要运行任务就是将内核映象从硬盘上读到RAM 中,然后跳转到内核的入口点去运行,也即开始启动操作系统。 而在嵌入式系统中,通常并没有像BIOS 那样的固件程序(注,有的嵌入式CPU 也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由Boot Loader 来完成。比如在一个基于ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址 0x00000000 处开始执行,而在这个地址处安排的通常就是系统的Boot Loader 程序。·2 bootloader简介 简单地说,Boot Loader (引导加载程序)就是在操作系统内核运行之前运行的一段小程序,它的作用就是加载操作系统, 实现硬件的初始化,建立内存空间的映射图,为操作系统内核准备好硬件环境并引导内核的启动。如上图所示的那样在设备的启动过程中bootloader位于最底层,首先被运行来引导操作系统运行,很容易可以看出bootloader是底层程序所以它的实现严重地依赖于硬件,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的BootLoader几乎是不可能的。尽管如此,一些功能强大、支持硬件环境较多的BootLoader也被广大的使用者和爱好者所支持,从而形成了一些被广泛认可的、较为通用的的bootloader实现。 2.1 Boot Loader 所支持的CPU 和嵌入式板 每种不同的CPU 体系结构都有不同的Boot Loader。有些Boot Loader 也支持多种体系结构的CPU,比如U-Boot 就同时支持ARM 体系结构和MIPS 体系结构。除了依赖于CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU 而构建的,要想让运行在一块板子上的Boot Loader 程序也能运行在另一块板子上,通常也都需要修改Boot Loader 的源程序。 2.2 Boot Loader 的安装媒介(Installation Medium)

U_Boot第一启动阶段Uboot启动分析笔记-----Stage1(start.S与lowlevel_init.S详解)

Uboot启动分析笔记-----Stage1(start.S与lowlevel_init.S详解) Uboot启动分析笔记-----Stage1(start.S与lowlevel_init.S详解) 1 u-boot.lds 首先了解uboot的链接脚本board/my2410/u-boot.lds,它定义了目标程序各部分的链接顺序。OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") /*指定输出可执行文件为ELF格式,32为,ARM小端*/ OUTPUT_ARCH(arm) /*指定输出可执行文件为ARM平台*/ ENTRY(_start) /*起始代码段为_start*/ SECTIONS { /* 指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置*、. = 0x00000000;从0x0位置开始 . = ALIGN(4); 4字节对齐 .text : {

cpu/arm920t/start.o (.text) board/my2440/lowlevel_init.o (.text) *(.text) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data) } /* 只读数据段,所有的只读数据段都放在这个位置*/ . = ALIGN(4); .got : { *(.got) } /*指定got段, got段式是uboot自定义的一个段, 非标准段*/ . = .; __u_boot_cmd_start = .; /*把__u_boot_cmd_start赋值为当前位置, 即起始位置*/ .u_boot_cmd : { *(.u_boot_cmd) } /* u_boot_cmd段,所有的u-boot命令相关的定义都放在这个位置,因为每个命令定义等长,所以只要以__u_boot_cmd_start为起始地址进行查找就可以很快查找到某一个命令的定义,并依据定义的命令指针调用相应的函数进行处理用户的任务*/ __u_boot_cmd_end = .; /* u_boot_cmd段结束位置,由此可以看出,这段空间的长度并没有严格限制,用户可以添加一些u-boot的命令,最终都会在连接是存放在这个位置。*/

Uboot启动代码解析

U-Boot启动过程 开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot 启动函数。看一下board/smdk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面分两阶段介绍启动流程: 第一阶段 1.cpu/arm920t/start.S 这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。_start: b reset //复位向量 ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq //中断向量 ldr pc, _fiq //中断向量 … /* the actual reset code */ reset: //复位启动子程序

/* 设置CPU为SVC32模式 */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 /* 关闭看门狗 */ ………… relocate: /* 把U-Boot重新定位到RAM */ adr r0, _start /* r0是代码的当前位置 */ ldr r1, _TEXT_BASE /*_TEXT_BASE是RAM中的地址 */ cmp r0, r1 /* 比较r0和r1,判断当前是从Flash启动,还是RAM */ beq stack_setup /* 如果r0等于r1,跳过重定位代码 */ /* 准备重新定位代码 */ ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 得到armboot的大小 */ add r2, r0, r2 /* r2 得到要复制代码的末尾地址 */ copy_loop: /* 重新定位代码 */ ldmia r0!, {r3-r10} /*从源地址[r0]复制 */

UBOOT详细解读

大多数bootloader都分为stage1和stage2两部分,u-boot也不例外。依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。 1、Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下:(1)定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。 (2)设置异常向量(Exception Vector)。 (3)设置CPU的速度、时钟频率及终端控制寄存器。 (4)初始化内存控制器。 (5)将ROM中的程序复制到RAM中。 (6)初始化堆栈。 (7)转到RAM中执行,该工作可使用指令ldr pc来完成。 2、Stage2 C语言代码部分 lib_arm/board.c中的start arm boot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数只要完成如下操作: (1)调用一系列的初始化函数。 (2)初始化Flash设备。 (3)初始化系统内存分配函数。 (4)如果目标系统拥有NAND设备,则初始化NAND设备。 (5)如果目标系统有显示设备,则初始化该类设备。 (6)初始化相关网络设备,填写IP、MAC地址等。 (7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。 3、U-Boot的启动顺序(示例,其他u-boot版本类似) cpu/arm920t/start.S @文件包含处理 #include @由顶层的mkconfig生成,其中只包含了一个文件:configs/<顶层makefile中6个参数的第1个参数>.h #include #include

u_boot初始化流程

U-Boot启动代码分析 U-boot的启动顺序分为stage1和stage2两部分,见下图。依赖于CPU体系结构的代码(如设备初始化代码等)通常放在stage1中用汇编语言实现,而在stage2则通常由C语言实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。以下主要梳理了stage2阶段函数的调用顺序以及每个函数的功能。 U-boot的启动顺序 C语言代码部分lib_arm/board.c中的start_armboot既是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个U-boot的主函数,该函数只要完成如下操作。 (1)调用一系列的初始化函数。 (2)初始化Flash设备。 (3)初始化系统内存分配函数 (4)如果目标系统拥有NAND设备,则初始化NAND设备 (5)如果目标系统有显示设备,则初始化该类设备。 (6)初始化相关网络设备,填写IP、MAC地址等。 (7)进入命令循环(即整个Boot的工作循环),接收用户从串口输入的命令,然后进行相应的工作。 下面结合源码分析函数调用顺序以及函功能: 代码: void start_armboot (void) { init_fnc_t **init_fnc_ptr; char *s; int mmc_exist = 0;

#if defined(CONFIG_VFD) || defined(CONFIG_LCD) unsigned long addr; #endif 注释:从U-boot stage1中start.s程序调到这里执行start_armboot函数,这一段代码进行了变量声明,其中定义了一个名为init_fnc_ptr的双重指针。如果CONFIG_VFD或者CONFIG_LCD被定义了则声明一无符号长整型变量addr,本开发板中没有定义无需声明addr。 代码: /* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); //内存屏障,告诉编译器内存被修改过了 memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //指向gd之前 memset (gd->bd, 0, sizeof (bd_t)); // gd->flags |= GD_FLG_RELOC; monitor_flash_len = _bss_start - _armboot_start; //u-boot映像的大小其中_armboot_start为code start ,_bss_start为code + data end == BSS start. 注释: gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));//内存强制转换,gd为全局环境变量,gd指向uboot之前的地址; memset ():void * memset(void * s,char c,size_t count)将指针s所指地址以及之后count个地址中数值赋值为c。memset ((void*)gd, 0, sizeof (gd_t))的作用为:gd整个地址的值初始化为0;memset (gd->bd, 0, sizeof (bd_t))的作用为bd地址的值初始化为0。 代码: for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { //相当于调用指针中的一个函数,如果不为0就表示死机 hang (); } } 注释:

最新Uboot移植步骤 5:NorFlash

最新Uboot移植步骤5:NorFlash 显示Flash:***failed***,说明norflash未识别,我们搜索“Flash:” 进入第一个查看 找到这个判断条件,如果flash_size>0则输出flash大小,否则输出 *** failed *** ### ERROR ### Please RESET the board ###

其中hang函数导致程序无法继续向下执行,我们只实现了nand启动肯定在这会卡住,所以我们不用这个hang 函数,直接输出flash未识别的信息就好了,改动如下: 现在来找norflash未识别的原因,进入flash_init函数 看见这样一段代码 可知,有2个函数可以检测flash的大小如果flash_detect_legacy函数不行再使用flash_get_size函数,先进入flash_detect_legacy函数看下,其结构如下: 该函数有2个,使用哪一个由宏CONFIG_FLASH_CFI_LEGACY决定,搜索该宏:

很明显,前面我们都是使用该函数进行大小检测的,而该函数无法识别flash,那我们使用新方法进行检测,进入新方法查看: 发现有很多可用调试信息,我们看看如何起用这些调试信息: 发现只要定义了_DEBUG即可启用调试信息,我们定义该宏: 在文件开始发现注释: 我们直接定义DEBUG即可,

配置,编译,下载到板子norflash: 重新上电从norflash启动,输出如下: 我们查看JEDEC PROBE:从哪来

查看norflash手册,看读取的设备ID是否正确 可以看到输出的厂家设备ID是正确的, 说明下面这个函数读取正确 那就是 函数出现错误,我们进入该函数查看:

分析uboot是如何启动内核的

分析uboot是如何启动内核的 (2011-12-27 19:16:30) 转载▼ 标签: 鏉傝皥 1.uboot启动内核的代码缩减如下: s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); if (bootdelay >= 0 && s && !abortboot (bootdelay)) { run_command (s, 0); } 2.假设bootcmd = nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0 <1> nand read.jffs2 0x30007FC0 kernel nand read.jffs2 0x30007FC0 kernel; 从nand读出内核:从哪里读?从kernel分区 放到哪里去?-0x30007FC0 下面讲解什么是分区: 就是将nand划分为几个区域,一般如下: bootloader-》params-》kernel-》root 这些分区的划分是在/include/configs/mini2440.h中写死的: #define MTDPARTS_DEFAULT "mtdparts=nandflash0:250k@0(bootloader)," \ "128k(params)," \ "5m(kernel)," \ "-(root)" 注:@0表示从0地址开始,250k的bootloader分区可能对某些uboot不够用,这里只是举例而已。 将上面的信息换算成十六进制: # name 大小在nand上的起始地址 0 bootloader 0x00040000 0x00000000 1 params 0x00020000 0x00040000 2 kernel 0x00200000 0x00060000 3 root 0xfda00000 0x00260000

u_boot移植(五)之分析uboot源码中nand flash操作

u_boot移植(五)之分析uboot源码中nand flash操作 一、OneNand 和Nand Flash 我们已经能从Nand Flash启动了,启动之后,大家会看到如下效果: 可以看出,我们的uboot默认使用的是OneNand。需要注意的是我们的FSC100上面是没有OneNand的,有的是K9F2G08U0B 型号的NAND FLASH。 前面我们了解过Nor Flash 和Nand Flash,那OneNand Flash又是什么呢?

二、uboot 源码中Nand Flash部分代码分析 我们从Nand Flash初始化看起,打开lib_arm/board.c文件,为了紧抓主 线,以下代码只列举出了主线代码。

可以看出,我们可以通过CONFIG_CMD_NAND和 CONFIG_CMD_ONENAND两个宏来选择NAND FLASH初始化还是 ONENAND FLASH初始化。 uboot 中默认定义了宏CONFIG_CMD_ONENAND,所以选择的是ONENAND FLASH初始化。我们的FSC100上面使用的是 NAND FLASH,所以我们要定义CONFIG_CMD_NAND宏,取消CONFIG_CMD_ONENAND宏的定义。 嗯!先做个记录: 修改include/configs/fsc100.h,定义宏CONFIG_CMD_NAND,取消宏CONFIG_CMD_ONENAND。 好了,接下我们看看nand_init()函数时如何实现的。

看以看出,这段代码调用根据CONFIG_SYS_MAX_NAND_DEVICE宏[默认没有定义]的值来决定系统中Nand Flash设备的个数。接着调 用nand_init_chip()函数完成Nand Flash初始化,然后计算出每块Nand Flash的大小。最终会输出Nand Flash总的容量。 嗯!做个记录: 修改include/configs/fsc100.h,定义 宏CONFIG_SYS_MAX_NAND_DEVICE,值为1 没有看明白的地方是给nand_init_chip()函数传递的参数,接下来我们来看看他们是如何定义的。 哈哈,终于到了让人头疼的地方了。先来看看struct nand_chip和struct mtd_info两个结构体,这两个结构体的成员有很多很多,很多初学者一看到就晕头转向了,为了让大家不被吓到,我对这两个结构体的成员做了精简,只保留我们关心的成员,其它的都去掉了。 (1)struct nand_chip

相关主题
文本预览
相关文档 最新文档