当前位置:文档之家› uboot启动代码详解

uboot启动代码详解

uboot启动代码详解
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)

系统加电或复位后,所有的CPU 通常都从某个由CPU 制造商预先安排的地址上取指令。比如,基于ARM7TDMI core 的CPU 在复位时通常都从地址0x00000000 取它的第一条指令。而基于CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或FLASH 等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行Boot Loader 程序。

下图1就是一个同时装有Boot Loader、内核的启动参数、内核映像和根文件系统映像的固态存储设备的典型空间分配结构图:

图1 固态存储设备的典型空间分配结构

2.3 Boot Loader 的启动过程:单阶段(Single Stage)/多阶段(Multi-Stage)

通常多阶段的Boot Loader 能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的Boot Loader 大多都是2 阶段的启动过程,也即启动过程可以分为stage

1 和stage

2 两部分。而至于在stage 1 和stage 2 具体完成哪些任务将在下面讨论。

2.4 Boot Loader 的操作模式(Operation Mode)

大多数Boot Loader 都包含两种不同的操作模式:"启动加载"模式和"下载"模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,Boot Loader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。

启动加载(Boot loading)模式:这种模式也称为"自主"(Autonomous)模式。也即Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到RAM 中运行,整个过程并没有用户的介入。这种模式是Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。

下载(Downloading)模式:在这种模式下,目标机上的Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Boot Loader 保存到目标机的RAM 中,然后再被Boot Loader 写到目标机上的FLASH 类固态存储设备中。Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用Boot Loader 的这种工作模式。工作于这种模式下的Boot Loader 通常都会向它的终端用户提供一个简单的菜单界面或命令行接口来接收要执行的操作。

像Blob 或U-Boot 等这样功能强大的Boot Loader 通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,Blob 在启动时处于正常的启动加载模式,但是它会延时10 秒等待终端用户按下任意键而将blob 切换到下载模式。如果在10 秒内没有用户按键,则blob 继续启动Linux 内核。

2.5 常见的Boot Loader

U-BOOT:U-Boot是Das U-Boot的简称,其含义是Universal Boot Loader,是遵循GPL条款的开放源码项目。uboot是一个庞大的公开源码的软件。它支持一些系列的arm体系,包含常见的外设的驱动,是一个功能强大的板极支持包。

vivi:vivi是韩国mizi 公司开发的bootloader, 适用于ARM9处理器。Vivi也有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这是vivi的默认模式。如果修改或更新需要进入下载模式,在下载模式下,vivi为用户提供一个命令行接口通过接口可以使用vivi提供的一些命令,来实现flash的烧

写、管理、操作mtd分区信息、启动系统等功能。

·3 Boot Loader 的主要任务与典型结构框架

从操作系统的角度看,Boot Loader 的总目标就是正确地调用内核来执行。另外,由于Boot Loader 的实现依赖于CPU 的体系结构,因此大多数Boot Loader 都分为stage1 和stage2 两大部分。依赖于CPU 体系结构的代码,比如设备初始化代码等,通常都放在stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2 则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。

以u-boot为例,它启动过程的两个阶段(stage) 如下:

·第一阶段(stage 1) cpu/arm920t/start.S

依赖于CPU体系结构的代码(如设备初始化代码等),一般用汇编语言来实现。主要进行以下方面的设置:设置ARM进入SVC模式、禁止IRQ和FIQ、关闭看门狗、屏蔽所有中断。设置时钟(FCLK,HCLK,PCLK)、清空I/D cache、清空TLB、禁止MMU和cache、配置内存控制器、为搬运代码做准备、搬移uboot映像到RAM中(使用copy_loop实现)、分配堆栈、清空bss段(使用clbss_l实现)。最后通过ldr pc, _start_armboot跳转到第二阶段。

·第二阶段(stage 2) lib_arm/board.c

该阶段主要都是用C语言来实现。start_armboot()进行一系列初始化(cpu, 板卡,中断,串口,控制台等),开启I/D cache。初始化FLASH,根据系统配置执行其他初始化操作。打印LOG,使能中断,获取环境变量,初始化网卡。最后进入main_loop()函数。

综上所述,可简单的归纳两个阶段的功能如下:

·第一阶段的功能:

硬件设备初始化

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

设置好栈

跳转到第二阶段代码入口

·第二阶段的功能:

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

检测系统内存映射

将内核从Flash读取到RAM中

为内核设置启动参数

调用内核

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

3.1 u-boot 的stage1详细分析

uboot的第一阶段设计的非常巧妙,几乎都是用汇编语言实现的。

首先我们来看一下它的链接脚本(u-boot-1.1.6\board\smdk2410\u-boot.lds),通过它我们可以知道它整个程序的各个段是怎么存放的。它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置。

/* 指定输出可执行文件是elf 格式,32 位ARM 指令,小端*/

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

/* 指定输出可执行文件的平台架构为ARM架构*/

OUTPUT_ARCH(arm)

/* 指定输出可执行文件的起始代码段为_start */

ENTRY(_start)

SECTIONS

{

. = 0x00000000;//入口地址

. = ALIGN(4);//四字节对齐

.text ://代码段,上面3行标识是不占任何空间的

{

cpu/arm920t/start.o (.text)//这里将start.o放在第一位就表示把start.s编译时放在最开始,也就是uboot启动是最先执行start.S

*(.text)//所有的其他程序的代码段以四字节对齐放在它后面

}

. = ALIGN(4); //前面的“.”表示当前值

.rodata : { *(.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段,uboot把所有的uboot命令放在该段

__u_boot_cmd_end = .;//把__u_boot_cmd_end赋值为当前位置,即结束位置

. = ALIGN(4);

__bss_start = .;//__bss_start赋值为当前位置,即bss段得开始位置

.bss : { *(.bss) }

_end = .;//把_end赋值为当前位置,即bss段得结束地址

}

从上面这段代码我们可以看出uboot运行的第一个程序是cpu/arm920t/start.S里面的第一个段_start。我们查看start.S的源码。

3.1.1 硬件设备初始化

(1)设置异常向量

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

//global用于声明一个符号可被其他文档引用,相当于声明了一个全局变量,.globl 和.global 相同。

//该部分为处理器的异常处理向量表。地址范围为0x00000000 ~ 0x00000020,刚好8 条指令。

.globl _start /* 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 /* irq中断向量*/

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

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

//.word 伪操作用于分配一段字内存单元(分配的单元都是字对齐的),并用伪操作中的expr 初始化。.long 和.int 作用与之相同。

_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

根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU 就跳转到对应的异常处理程序执行。

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

许多人都认为_start的值是0x00000000,为什么是这个地址呢? 因为连接脚本上指定了。真的是这样吗?我们来看看我们编译好之后,在u-boot目录下有个System.map,这里面有各个变量的值,其中会告诉你,_start的值为:0x33f80000。注意,这里有两个地址:编译地址和运行地址。

什么是编译地址?什么是运行地址?

32 位的处理器,它的每一条指令是4个字节,以4个字节存储顺序,进行顺序执行,CPU是顺序执行的,只要没发生什么跳转,它会顺序进行执行,编译器会对每一条指令分配一个编译地址,这是编译器分配的,在编译过程中分配的地址,我们称之为编译地址。

运行地址是指,程序指令真正运行的地址,是由用户指定的,用户将运行地址烧录到哪里,哪里就是运行的地址。编译地址和运行地址如何来算呢?假如有两个编译地址a=0x10,b=0x7,b的运行地址是0x300 ,那么a的运行地址就是b的运行地址加上两者编译地址的差值,a-b=0x10-0x7=0x3,a的运行地址就是0x300+0x3=0x303。

(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 /* WATCHDOG控制寄存器地址*/

#define INTMSK 0x4A000008 /* INTMSK寄存器地址*/

#define INTSUBMSK 0x4A00001C /* INTSUBMSK寄存器地址*/

#define CLKDIVN 0x4C000014 /* CLKDIVN寄存器地址*/

# endif

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

(4)关闭看门狗

ldr r0, =pWTCON

mov r1, #0x0

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

以上代码向看门狗控制寄存器写入0,关闭看门狗。为什么需要关闭看门狗呢?这里有个喂狗的过程,所谓的喂狗是每隔一段时间给某个寄存器置位而已,在实际中会专门启动一个线程或进程会专门喂狗,当上层软件出现故障时就会停止喂狗,停止喂狗之后,cpu会自动复位,一般都在外部专门有一个看门狗,做一个外部的电路,不在cpu内部使用看门狗,否则在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寄存器全部位置1,从而

屏蔽对应的中断。为什么要关闭中断呢?中断处理(ldr pc ..)是将代码的编译地址放在了指针上,而这段时间内还没有搬移代码,所以不能进行跳转。

#if defined(CONFIG_S3C2440)

ldr r1, =0x7fff

ldr r0, =INTSUBMSK

str r1, [r0]

#endif

INTSUBMSK每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置1,从而屏蔽对应的中断。

(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为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的关系如下面公式:

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

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

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

(7)关闭MMU,cache

接着往下看:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl cpu_init_crit

#endif

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

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

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

cpu_init_crit:

/*

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

*/

mov r0, #0

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

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

/*

* disable MMU stuff and caches

*/

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

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

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

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

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

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

/*

* before relocating, we have to setup RAM timing

* because memory timing is board-dependend, you will

* find a lowlevel_init.S in your board directory.

*/

mov ip, lr

bl lowlevel_init

mov lr, ip

mov pc, lr

#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

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

通过修改CP15的c1寄存器来关闭了MMU。

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

catch 是cpu内部的一个2级缓存,她的作用是将常用的数据和指令放在cpu内部,MMU 是用来做虚实地址转换用的,我们的目的是设置控制的寄存器,寄存器都是实地址,如果既要开启MMU又要做虚实地址转换的话,中间还多一步,先要把实地址转换成虚地址,然后再做设置,但对uboot 而言就是起到一个简单的初始化的作用和引导操作系统,如果开启MMU 的话,很麻烦,也没必要,所以关闭MMU.

说到catch 就必须提到一个关键字V olatile,以后在设置寄存器时会经常遇到,他的本质是告诉编译器不要对我的代码进行优化,优化的过程是将常用的代码取出来放到catch中,它没有从实际的物理地址去取,它直接从cpu的缓存中去取,但常用的代码就是为了感知一些常用变量的变化,所以在这种情况下要用V olatile关键字告诉编译器不要做优化,每次从实际的物理地址中去取指令。

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

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

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

… …

_TEXT_BASE:

.word TEXT_BASE

.globl lowlevel_init

lowlevel_init:

/* memory control configuration */

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

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

ldr r0, =SMRDATA

ldr r1, _TEXT_BASE

sub r0, r0, r1 /* SMRDATA减_TEXT_BASE就是13个寄存器的偏移地址*/ ldr r1, =BWSCON /* Bus Width Status Controller */

add r2, r0, #13*4

0:

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

str r3, [r1], #4

cmp r2, r0

bne 0b

/* everything is fine now */

mov pc, lr

.ltorg

/* the literal pools origin */

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

.word … …

.word … …

… …

lowlevel_init的作用就是将SMRDA TA开始的13个值复制给开始地址[BWSCON]的13个寄存器,从而完成了存储控制器的设置。

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

relocate:

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

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

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

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

beq stack_setup

ldr r2, _armboot_start

ldr r3, _bss_start

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

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

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

copy_loop:

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

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

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

ble copy_loop

终于到重点部分了:代码重定向(拷贝stage2到RAM中),拷贝时要确定两点:

(1) stage2的可执行映象在固态存储设备的存放起始地址和终止地址;

(2) RAM空间的起始地址。

下面我们来看看它到底是怎么重定向的:

adr r0,_start

adr伪指令,汇编器会将执行到_start时PC的值放到r0中。所以此时r0中保存的不是编译地址,而是运行地址。假如U-boot 是从RAM 开始运行,则从adr,r0,_start 得到的地址信息为r0=_start=_TEXT_BASE=TEXT_BASE=0x33f80000;假如U-boot 从Flash 开始运行,即从处理器对应的地址运行,则r0=0x00000000。而这里r0=0。

ldr r1,_TEXT_BASE

这条汇编指令的意思是把_TEXT_BASE的值作为地址,把这个地址的内容赋给r1,从下面可以知道:

_TEXT_BASE里面存储的内容是TEXT_BASE,我们通过查看board/smdk2410/config.mk 发现TEXT_BASE的值为0x33f80000,所以r1的值就是0x33f80000

cmp r0,r1

将r0和r1做比较,此时r0 = 0x00000000,r1 = 0x33f80000,显然不相等,那么执行的就是下面的汇编指令:

ldr r2, _armboot_start

由此可以知道r2的值是_start,通过ldr将标号的编译地址放到r2中,也就是0x33f80000,即代码段的起始地址。

ldr r3, _bss_start

有此可知,r3就是__bss_start的值。由u-boot.lds的链接脚本可以知道,r3的值是整个代码得结尾.

sub r2,r3,r2

这条指令的意思是r2 = r3 -r2,即r2 = 代码结束- 代码开始,这样得到的是r2 = 整个代码的大小。

add r2,r0,r2

这条指令的意思是r2 = r0 + r2,即r2 = 代码开始+ 代码大小,这样得到的是r2 = falsh 里面代码的结尾,此时我们得到r0 = flash 代码的起始位置,r1 = 0x33f80000(sdram :0x300000000 ~ 0x34000000)

将flash里面的代码拷贝到sdram里面了。

(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内存使用情况如下图所示:

(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处。

3.2 Boot Loader 的stage2详细分析

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

正如前面所说,stage2 的代码通常用C 语言来实现,以便于实现更复杂的功能和取得更好的代码可读性和可移植性。在分析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_DA TA_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(在lib_arm/board.c中)来存储对于大多数开发板都要执行的初始化函数的函数指针。init_sequence数组中有较多的编译选项,去掉编译选项后init_sequence数组如下所示:

typedef int (init_fnc_t) (void); /* 这是使用typedef定义一个init_fnc_t为函数类型,该函数返回int型,无参数*/

init_fnc_t *init_sequence[] = { /* 定义一个init_fnc_t指针类型的数组。简单的说就是定义了个函数指针数组,指向一系列cpu初始化函数*/

cpu_init, /* basic cpu dependent setup CPU的相关配置,如初始化IRQ/FIQ模式的栈cpu/arm920t/cpu.c */

board_init, /* basic board dependent setup开发板相关配置,是对板子的初始化,设置MPLL,改变系统时钟,以及一些GPIO寄存器的值,还设置了U-Boot机器码和内核

启动参数地址,它是开发板相关的函数,比如2410是在:board/smdk2410/smdk2410.c中实现*/

interrupt_init, /* set up exceptions 初始化中断,在cpu/arm920t/s3c24x0/interrupts.c 实现*/

env_init, /* initialize environment 初始化环境变量,检查flash上的环境变量是否有效common/env_flash.c 或common/env_nand.c */

init_baudrate, /* initialze baudrate settings 初始化波特率lib_arm/board.c */

serial_init, /* serial communications setup串口初始化串口初始化后我们就可以打印信息了cpu/arm920t/s3c24x0/serial.c */

console_init_f, /* stage 1 init of console 控制台初始化第一阶段common/console.c */ display_banner, /* say that we are here通知代码已经运行到该处,打印U-Boot版本、编译的时间-- lib_arm/board.c */

#if defined(CONFIG_DISPLAY_CPUINFO)

print_cpuinfo, /* display cpu info (and speed) */

#endif

#if defined(CONFIG_DISPLAY_BOARDINFO)

checkboard, /* display board info */

#endif

dram_init, /* configure available RAM banks 配置可用的内存区,检测系统内存映射,设置内存的起始地址和大小board/smdk2410/smdk2410.c */

display_dram_config,

NULL,

};

可以看出这里定义了一个指针数组,它的每一个元素都是指针变量,这些指针变量指向的类型为init_fnc_t,在C语言中函数的入口地址就是函数名,所以这里使用一系列函数名来初始化这个数组。现在来看看到底做了哪些初始化工作:

·cpu_init函数:cpu/arm920t/cpu.c

int cpu_init (void)

{

#ifdef CONFIG_USE_IRQ

IRQ_STACK_START =_armboot_start-CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE-4;

FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;

#endif

return 0;

}

其实这个函数没有做任何工作,因为CONFIG_USE_IRQ这个宏没有定义。实际上就是把IRQ_STACK_START、FIQ_STACK_START指到RAM中的IRQ stuff区域。

·board_init函数:board/smdk2410/smdk2410.c

int board_init (void)

{

/* 获取power和clock及GPIO方面的寄存器地址,稍后的操作会对这些寄存器操作,需要注意的是,像S3C24X0_CLOCK_POWER里面的field对象都是按照实际寄存器的地址来安排的*/

S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

/* to reduce PLL lock time, adjust the LOCKTIME register降低PLL的lock time的值*/ clk_power->LOCKTIME = 0xFFFFFF;

/* configure MPLL配置MPLL */

clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

/* some delay between MPLL and UPLL延时*/

delay (4000);

/* configure UPLL配置UPLL */

clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);

/* some delay between MPLL and UPLL延时*/

delay (8000);

/* 配置每个GPIO的功能,输入输出,等参数*/

gpio->GPACON = 0x007FFFFF;

gpio->GPBCON = 0x00044555;

gpio->GPBUP = 0x000007FF;

gpio->GPCCON = 0xAAAAAAAA;

gpio->GPCUP = 0x0000FFFF;

gpio->GPDCON = 0xAAAAAAAA;

gpio->GPDUP = 0x0000FFFF;

gpio->GPECON = 0xAAAAAAAA;

gpio->GPEUP = 0x0000FFFF;

gpio->GPFCON = 0x000055AA;

gpio->GPFUP = 0x000000FF;

gpio->GPGCON = 0xFF95FFBA;

gpio->GPGUP = 0x0000FFFF;

gpio->GPHCON = 0x002AFAAA;

gpio->GPHUP = 0x000007FF;

/* SMDK2410开发板的机器码*/

gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

/* adress of boot parameters内核启动参数地址,运行时在linux内核之下*/

gd->bd->bi_boot_params = 0x30000100;

/* 使能指令cache和数据cache */

icache_enable();

dcache_enable();

return 0;

}

函数icache_enable和dcache_enable,定义在cpu/arm920t/cpu.c中,这两个函数是通过修改CP15的c1寄存器来实现的,使能cache很简单,只要把协处理器15的相关位打开就行了,这里来是将c1的I、C位置1,来开启Icache、DCaches。我这里只分析icache_enable,dcache_enable类似。icache_enable具体实现如下:

void icache_enable (void)

{

ulong reg;

reg = read_p15_c1 (); /* get control reg. 获取CP15的c1寄存器值存到reg中*/ cp_delay ();

write_p15_c1 (reg | C1_IC); /*这里将C1寄存器的I、C位置1,来开启Icache、Dcaches*/ }

这里须要理解的是read_p15_c1与write_p15_c1函数,它们分别在cpu/arm920t/cpu.c中定义如下:

static unsigned long read_p15_c1 (void)

{

unsigned long value;

__asm__ __volatile__(

"mrc p15, 0, %0, c1, c0, 0 @ read control reg\n" /* %0是参数传递时R0寄存器,其功能是读取CP15的c1寄存器值放到r0中*/

: "=r" (value)

:

: "memory");

#ifdef MMU_DEBUG

printf ("p15/c1 is = %08lx\n", value);

#endif

return value; 返回读取CP15的c1寄存器的值

}

/* write to co-processor 15, register #1 (control register) */

static void write_p15_c1 (unsigned long value)

{

#ifdef MMU_DEBUG

printf ("write %08lx to p15/c1\n", value);

#endif

__asm__ __volatile__(

"mcr p15, 0, %0, c1, c0, 0 @ write it back\n" @保存r0的值到控制寄存器CP15的c1寄存器中,因为函数参数传递时,第一个参数都是放在r0寄存器中的。

:

: "r" (value)

: "memory");

read_p15_c1 ();

}

·interrupt_init函数:cpu/arm920t/s3c24x0/interrupts.c

int timer_load_val = 0;

static ulong timestamp;

static ulong lastdec;

int interrupt_init (void) /* 初始化timer4相关寄存器,用于产生10ms定时中断信号*/ {

/* 返回定时器配置寄存器地址0x51000000,即TCFG0寄存器地址*/

S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();

/* 这里使用定时器4,定时器4有只有一个内部定器没有输出管脚*/

/* prescaler for Timer 4 is 16 */

timers->TCFG0 = 0x0f00;

if (timer_load_val == 0)

{

timer_load_val = get_PCLK()/(2 * 16 * 100); /* get_PCLK返回PCLK频率*/ }

/* load value for 10 ms timeout */

lastdec = timers->TCNTB4 = timer_load_val; /* 设置计数缓存寄存器初始值*/

/* 设置定时器4手动更新,自动加载模式,并关闭定时器4 */

timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;

/* auto load, 启动Timer 4 */

timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;

timestamp = 0;

return (0);

}

对着datasheet来看这个函数,实际上这个函数使用timer 4来作为系统clock,即时钟滴答,10ms一次,到点就产生一个中断,但由于此时中断还没打开所以这个中断不会响应。·env_init函数: 由于我们在inculde/configs/smdk2410.h下定义了CFG_ENV_IS_IN_FLASH 因此该函数位于common/env_flash.c下

int env_init(void)

{

#ifdef CONFIG_OMAP2420H4

int flash_probe(void);

if(flash_probe() == 0)

goto bad_flash;

#endif

if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {

gd->env_addr = (ulong)&(env_ptr->data);

gd->env_valid = 1; /* 使用include/configs/smdk2410.h配置的环境变量则设置环境变量可用标志*/

return(0);

}

/* env_ptr在前面定义为env_t *env_ptr = (env_t *)CFG_ENV_ADDR;而

CFG_ENV_ADDR被定义在include/configs/smdk2410.h中了,这里判断如果校验正确(即CFG_ENV_ADDR被定义了)则环境变量的存放地址使用smdk2410.h中定义的,否则使用后面的默认的环境变量值default_environment数组*/

#ifdef CONFIG_OMAP2420H4

bad_flash:

#endif

gd->env_addr = (ulong)&default_environment[0];

gd->env_valid = 0; 使用默认的环境配置变量则设置环境变量不可用标志

return (0);

}

这个函数主要是在gd里保存环境变量的存放地址。一般使用默认的环境变量值即default_environment数组,它在common/env_commom.c中定义如下:

uchar default_environment[] = {

#ifdef CONFIG_BOOTARGS

"bootargs=" CONFIG_BOOTARGS "\0"

#endif

#ifdef CONFIG_BOOTCOMMAND

"bootcmd=" CONFIG_BOOTCOMMAND "\0"

#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)

"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"

#endif

……

#ifdef CONFIG_EXTRA_ENV_SETTINGS

CONFIG_EXTRA_ENV_SETTINGS

#endif

"\0"

};

可见环境变量以如下的方式存放在数组中:Name=value,并且以”/0”结束,而类似CONFIG_BOOTARGS的宏都定义在板子自己的配置文件中即smdk2410.h里。

·init_baudrate函数:lib_arm/board.c

static int init_baudrate (void)

{

char tmp[64]; /* long enough for environment variables */

int i = getenv_r ("baudrate", tmp, sizeof (tmp)); /* 从环境变量中获取波特率值*/

gd->bd->bi_baudrate = gd->baudrate = (i > 0)

? (int) simple_strtoul (tmp, NULL, 10)

: CONFIG_BAUDRATE;

return (0);

}

该函数从上面刚初始化好的环境变量列表里找波特率值,如果没有就赋初始值为CONFIG_BAUDRATE。

·serial_init函数:cpu/arm920t/s3c24x0/serial.c

int serial_init (void)

{

serial_setbrg (); /* 设置波特率,停止位等*/

return (0);

}

void serial_setbrg (void)

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移植详解

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 独立于处理器体系结构的通用代码,如内存大小探测与故障检测;

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/e05453109.html,/batch.viewl ink.php?itemid=1694 ftp://ftp.denx.de/pub/u-boot/ )

AM335x uboot spl分析

AM335x uboot spl分析 芯片到uboot启动流程 ROM → SPL→ uboot.img 简介 在335x 中ROM code是第一级的bootlader。mpu上电后将会自动执行这里的代码,完成部分初始化和引导第二级的bootlader,第二级的bootlader引导第三级bootader,在 ti官方上对于第二级和第三级的bootlader由uboot提供。 SPL To unify all existing implementations for a secondary program loader (SPL) and to allow simply adding of new implementations this generic SPL framework has been created. With this framework almost all source files for a board can be reused. No code duplication or symlinking is necessary anymore. 1> Basic ARM initialization 2> UART console initialization 3> Clocks and DPLL locking (minimal) 4> SDRAM initialization 5> Mux (minimal) 6> BootDevice initialization(based on where we are booting from.MMC1/MMC2/Nand/Onenand) 7> Bootloading real u-boot from the BootDevice and passing control to it. uboot spl源代码分析 一、makefile分析 打开spl文件夹只有一个makefile 可见spl都是复用uboot原先的代码。 主要涉及的代码文件为u-boot-2011.09-psp04.06.00.03/arch/arm/cpu/armv7 u-boot-2011.09-psp04.06.00.03/arch/arm/lib u-boot-2011.09-psp04.06.00.03/drivers LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds 这个为链接脚本 __image_copy_end _end 三、代码解析 __start 为程序开始(arch/arm/cpu/armv7/start.S) .globl _start 这是在定义u-boot的启动定义入口点,汇编程序的缺省入口是 start 标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。

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 维护

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/e05453109.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/e05453109.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,且文件内容如下: ###################################################################

uboot版本文件结构

uboot版本文件结构的更新改变 分类:ARM2011-09-22 12:57 339人阅读评论(0) 收藏举报本来是开始分析uboot代码的,但是无论是教材还是网上资料都对于我最新下的uboot原码结构不同,对于还是小白的我不容易找到相应的文件,下面是uboot版本中文件组织结构的改变,,,,, u-boot版本情况 网站:http://ftp.denx.de/pub/u-boot/ 1、版本号变化: 2008年8月及以前 按版本号命名:u-boot-1.3.4.tar.bz2(2008年8月更新) 2008年8月以后均按日期命名。 目前最新版本:u-boot-2011.06.tar.bz2(2011年6月更新) 2、目录结构变化: u-boot目录结构主要经历过2次变化,u-boot版本第一次从u-boot-1.3.2开始发生变化,主要增加了api的内容;变化最大的是第二次,从2010.6版本开始。 u-boot-2010.03及以前版本 ├── api存放uboot提供的接口函数 ├── board根据不同开发板定制的代码,代码也不少 ├── common通用的代码,涵盖各个方面,已命令行处理为主 ├── cpu与体系结构相关的代码,uboot的重头戏 ├── disk磁盘分区相关代码 ├── doc文档,一堆README开头的文件 ├── drivers驱动,很丰富,每种类型的设备驱动占用一个子目录 ├── examples示例程序 ├── fs文件系统,支持嵌入式开发板常见的文件系统 ├── include头文件,已通用的头文件为主 ├── lib_【arch】与体系结构相关的通用库文件 ├── nand_spl NAND存储器相关代码 ├── net网络相关代码,小型的协议栈 ├── onenand_ipl

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环境变量总结

Common目录下面与环境变量有关的文件有以下几个:env_common.c,env_dataflash.c,env_eeprom.c,env_flash.c,env_nand.c,env_nowhere.c,env_nvram.c,environment.c。 env_common.c中包含的是default_environment[]的定义; env_dataflash.c,env_eeprom.c,env_flash.c,env_nand.c, env_nvram.c 中包含的是相应存储器与环境变量有关的函数:env_init(void),saveenv(void),env_relocate_spec (void),env_relocate_spec (void),use_default()。至于env_nowhere.c,因为我们没有定义CFG_ENV_IS_NOWHERE,所以这个文件实际上没有用。 environment.c这个文件时是我真正理解环境变量的一个关键。在这个文件里定义了一个完整的环境变量的结构体,即包含了这两个ENV_CRC(用于CRC校验),Flags(标志有没有环境变量的备份,根据CFG_REDUNDAND_ENVIRONMENT这个宏定义判断)。定义这个环境变量结构体的时候还有一个非常重要的关键字: __PPCENV__,而__PPCENV__在该.c文件中好像说是gnu c编译器的属性,如下: # define __PPCENV__ __attribute__ ((section(".text"))) 意思是把这个环境变量表作为代码段,所以在编译完UBOOT后,UBOOT的代码段就会有环境变量表。当然,这要在我们定义了ENV_IS_EMBEDDED之后才行,具体而言,环境变量表会在以下几个地方出现(以nand flash为例): 1、UBOOT中的代码段(定义了ENV_IS_EMBEDDED), 2、UBOOT中的默认环 境变量, 3、紧接UBOOT(0x0 ~ 0x1ffff)后面:0x20000 ~ 0x3ffff 之间,包括备份的环境变量,我们读取,保存也是对这个区域(即参数区)进行的。3、SDRAM中的UBOOT中,包括代码段部分和默认部分,4、SDRAM中的melloc分配的内存空间中。 Environment.c代码如下: env_t environment __PPCENV__ = { ENV_CRC, /* CRC Sum */ #ifdef CFG_REDUNDAND_ENVIRONMENT 1, /* Flags: valid */ #endif { #if defined(CONFIG_BOOTARGS) "bootargs=" CONFIG_BOOTARGS "\0" #endif #if defined(CONFIG_BOOTCOMMAND) "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif #if defined(CONFIG_RAMBOOTCOMMAND) "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"

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

嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(一) 嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。一为总结经验,二希望能给想入门嵌入式Linux 的朋友提供方便。如有错误之处,谢请指正。 ?共享资源,欢迎转载:https://www.doczj.com/doc/e05453109.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项目的代码作为模板,以后再修改

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移植 CAMDIVN与时钟

关于uboot移植 CAMDIVN与时钟 2010-03-09 19:57 在该文件的122行附近有这样一个结构体 typedef struct { S3C24X0_REG32 LOCKTIME; S3C24X0_REG32 MPLLCON; S3C24X0_REG32 UPLLCON; S3C24X0_REG32 CLKCON; S3C24X0_REG32 CLKSLOW; S3C24X0_REG32 CLKDIVN; } /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER; 是用来封装时钟寄存器的,我们要在其中增加一项S3C24X0_REG32 CAMDIVN,为什么加这么一个呢?因为这个寄存器是2410所没有的,而2440在配置时钟的时候又必须用到,看名字我们就知道是用来配置CAMERA时钟的,也就是配置摄像头的时钟的。 貌似和配置uboot启动的时钟没有关系?其实不然,我们在修改下一个文件的时候就可以看到其用途了, 此结构体修改后的结果为 typedef struct { S3C24X0_REG32 LOCKTIME; S3C24X0_REG32 MPLLCON; S3C24X0_REG32 UPLLCON; S3C24X0_REG32 CLKCON; S3C24X0_REG32 CLKSLOW; S3C24X0_REG32 CLKDIVN; S3C24X0_REG32 CAMDIVN; } /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER; 第二个文件..\cpu\arm920t\s3c24x0\speed.c 在这个文件中需要修改两个函数 第一个函数在54行附近:static ulong get_PLLCLK(int pllreg) 由于S3C2410和S3C2440的MPLL、UPLL计算公式不一样,所以get_PLLCLK 函数也需要修改:

经典=Uboot-2-命令详解(bootm)

bootm命令中地址参数,内核加载地址以及内核入口地址 分类:u-boot2010-11-04 10:472962人阅读评论(0)收藏举报downloadlinuxbytecmdheaderimage bootm命令只能用来引导经过mkimage构建了镜像头的内核镜像文件以及根文件镜像,对于没有用mkimage对内核进行处理的话,那直接把内核下载到连接脚本中指定的加载地址0x30008000再运行就行,内核会自解压运行(不过内核运行需要一个tag来传递参数,而这个tag是由bootloader提供的,在u-boot下默认是由bootm命令建立的)。 通过mkimage可以给内核镜像或根文件系统镜像加入一个用来记录镜像的各种信息的头。同样通过mkimage也可以将内核镜像进行一次压缩(指定-C none/gzip/bzip2),所以这里也就引申出了两个阶段的解压缩过程:第一个阶段是u-boot里面的解压缩,也就是将由mkimage压缩的镜像解压缩得到原始的没加镜像头的内核镜像。第二个阶段是内核镜像的自解压,u-boot 里面的解压实际上是bootm 实现的,把mkimage -C bzip2或者gzip 生成的uImage进行解压;而kernel的自解压是对zImage进行解压,发生在bootm解压之后。 下面通过cmd_bootm.c文件中对bootm命令进行解析以及执行的过程来分析,这三种不同地址的区别: ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { ...... if (argc < 2) { addr = load_addr;//当bootm命令后面不带地址参数时,将默认的加载地址赋值给addr } else { addr = simple_strtoul(argv[1], NULL, 16); //如果bootm命令后面带了加载地址,则将该地址赋值给addr,所以最终有用的地址还是bootm命令后附带的地址 } ...... //

uboot调试指南

Uboot调试参考指南 一、调试目的 Uboot的调试旨在通过观察uboot运行时状态来测试硬件问题。 二、调试步骤 1.修改代码 在uboot代码路径下,编辑uboot代码,需要做以下修改; a.修改config.mk文件,添加以下两行内容: AFLAGS += -Wa,-gdwarf2 CFLAGS += -g2 -gdwarf-2 b.修改. /arch/powerpc/lib/board.c文件 debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr); printf("Now running in RAM - U-Boot at: %08lx\n", dest_addr); 将debug改为printf,如上所示。 2.编译uboot 执行make BSC9131RDB_SYSCLK100_NAND,编译uboot 3.将编译好的u-boot-nand.bin(uboot image格式)及u-boot(elf格式文件)文件拷 贝出来 4.烧录uboot 将步骤3中保存的u-boot-nand.bin烧录到目标板中,烧录过程略。 5.建立工程 a.在cw界面,点击file->import, 选择code warrior -> Power architecture ELF executable,如图1所示: 图1 建立elf工程 b.选择步骤3中保存的u-boot(elf格式文件),toolchain选择bareboard application, target OS选择none,工程名字请根据需要设置,比如我的机器上设置为example, 点击next,如图2所示:

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的工作循环),接受用户从串口输入的命令,然后进行相应的工作。

Ubuntu下配置并使用LXR查看Uboot代码(原创)

Ubuntu下配置并使用LXR查看Uboot代码(原创) 之前买了个mini6410觉得查看uboot的源代码太麻烦,上网查到,利用lxr查看源代码比较方便,使用到的有:apache2,glimpse-4.18.6,lxr,u-boot-mini6410(查看的目标文件夹),我使用的Ubuntu9.10,在ylmf3下面也验证成功。 下面就正式开始搭建我们自己的lxr. 建议下面的所有的操作都使用root权限操作: sudo su 输入当前用户的使用密码即可就变成“root@XXXXXXX:” 一、安装apach2: sudo apt-get install apache2 二、安装glimpse: 先去网站下载最新的源代码glimpse-4.18.6.tar.gz,然后解压到当前目录下 tar -xvgf glimpse-4.18.6.tar.gz 再接着进入解压后的目录下,比如我的是: cd glimpse-4.18.6/ 在编译之前,首先看看你的机器上是否已经安装了flex,因为编译glimpse的时候需要这个软件。如果没有的话,那么进行安装: sudo apt-get install flex 接着进行编译: ./configure make sudo make install 执行完上面的步骤后,将生成的glimpse glimpseindex 拷贝到/bin目录下: cd /bin sudo cp glimpse glimpseindex /bin 三、安装lxr sudo apt-get install lxr 新建/usr/share/lxr/http/.htaccess文件 在里面增加如下内容: SetHandler cgi-script 四、复制U-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)

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