当前位置:文档之家› 自动编写bootloader

自动编写bootloader

自动编写bootloader
自动编写bootloader

CPU上电后会从IO空间的某地址取第一条指令。但此时:PLL没有启动,CPU工作频率为外部输入晶振频率,非常低;CPU工作模式、中断设置等不确定;存储空间的各个BANK(包括内存)都没有驱动,内存不能使用。在这种情况下必须在第一条指令处做一些初始化工作,这段初始化程序与操作系统独立分开,称之为bootloader。

实际上,很少有必要自己写一个Bootloader,因为U-Boot已经强大到能够满足各种需要。但是强大必然复杂,一个初学者想要分析U-Boot的源代码,还是有些难度的。出于学习的目的,我写了这个史上最简单的启动加载器,它只包含最基本的功能,却囊括了一个嵌入式Bootloader应该有的核心和精华。我把这个启动加载器命名为S-Boot,是Simple Bootloader的缩写,亦可进一步简称为SB。

使用的实验环境为OK2440开发板,板上处理器为S3C2440A,有64M内存,Nand存储器为K9F1208,64M。网口芯片为CS8900A。我们要实现的功能是:从串口下载Linux内核映像到RAM;从网口下载Linux内核映像到RAM;从RAM启动内核挂载NFS根文件系统。

1. 第一阶段的汇编代码:start.S

一个嵌入式Bootloader最初始部分的代码几乎必须是用汇编语言写成的,因为开发板刚上电后没有准备好C程序运行环境,比如堆栈指针SP没有指到正确的位置。汇编代码应该完成最原始的硬件设备初始化,并准备好C运行环境,这样后面的功能就可以用C语言来写了。

对我们的S-Boot来说,上电后的起始运行代码是 start/start.S。

.text

.global _start

_start:

b Reset @ 0x00: 发生复位异常时从地址零处开始运行

b HandleUndef @ 0x04: 未定义指令中止模式的向量地址

b HandleSWI @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式 b HandlePrefetchAbort @ 0x0C: 指令预取终止导致的异常的向量地址

b HandleDataAbort @ 0x10: 数据访问终止导致的异常的向量地址

b HandleNotUsed @ 0x14: 保留

b HandleIRQ @ 0x18: 中断模式的向量地址

b HandleFIQ @ 0x1C: 快中断模式的向量地址

这里,汇编指示符.text表明以下内容属于代码段,.global _start指明_start是全局可访问的符号(Give the symbol external linkage)。按照ARM920T 的规定,从地址0x00到0x1C放置异常向量表,向量表每个条目占四个字节,正好可以放置一条跳转指令,跳转到相应异常的服务程序中去。在S-Boot中没有使用中断,所以除Reset异常外,其它异常的服务程序都可简单地写个死循环。

Reset异常是系统上电后自动触发的,所以我们的代码都写在Reset的服务程序里面。

实际上,异常向量表不一定非要位于地址0x00处,CP15协处理器中的c1寄存器的第13位用来控制异常向量表的起始地址。该位为0时,异常向量表位于低地址0x00处;该位为1时,异常向量表位于高地址 0xFFFF0000处。我们没有必要改变这个位的值,使用默认的低地址就行了。

Reset:

mrs r0,cpsr @set cpu to SVC32 mode

bic r0,r0,#0x1F

orr r0,r0,#0xD3

msr cpsr,r0 @cpsr=11x10011, IRQ/FIQ disabled

代码最初始的任务是设置CPU工作在SVC32模式,关闭所有中断,禁用看门狗。实际上,即使不设置工作模式,CPU在复位之后将自动工作在管理模式。在整个S-Boot运行期间,我们没有使用中断,也没有改变CPU工作模式,它将一直工作在SVC32模式。

MMU、ICache、DCache的打开和关闭都是由CP15协处理器的c1寄存器控制的。实际上在复位之后这三者都是自动关闭的,所以省略了关闭它们的代码。

S3C2440A的PSR寄存器(Program Status Reguster)中每个Bit位的含义如图1所示。Bit4~Bit0为模式位,用来设置CPU工作模式,现在只要知道 M[4:0] = 10011 表示SVC32模式就行了。Bit5为状态位,T=0表示工作在ARM状态,T=1表示工作在Thumb状态,默认为0,不需要改变。Bit6为快速中断禁止位,F=1为禁止快速中断,F=0为使能快速中断。Bit7为中断禁止位,I=1为禁止中断,F=0为使能中断。其它Bit位暂时可以不必理会。

mrs 和msr是在PSR寄存器和其它寄存器间传递数据的指令。如:mrs

r0,cpsr 把cpsr的值传送到r0中, msr cpsr,r0 把r0的值传送到cpsr中。bic是位清零(Bit Clear)指令,bic r0,r0,#0x1F 意思是把r0的Bit[4:0]位清零(由0x1F指示),然后把结果写入r0中。 orr是按位求或指令,orr r0,r0,#0xD3 表示把r0的 Bit7,Bit6,Bit4,Bit1,Bit0 置为1,其它位保持不变。

执行完上述操作后,cpsr中的 I=1, F=1, T保持不变(默认为0),

M[4:0]=10011,意思是禁止IRQ,禁止FIQ,工作在ARM状态,工作在SVC32模式。

ldr r0, =0x53000000

mov r1, #0x0

str r1, [r0] @disable watch dog

禁用看门狗更简单,因为WTCON寄存器的地址为0x53000000,直接向该寄存器写0即可。

到目前为止,CPU工作在外接晶振12MHz频率之下。使用以下代码设置PLL,提升工作频率。

ldr r0, =0x4C000014 @CLKDIVN register

mov r1, #0x05 @FCLK:HCLK:PCLK = 1:4:8

str r1, [r0]

mrc p15,0,r0,c1,c0,0 @if HDIVN Not 0, must asynchronous bus m ode

orr r0,r0,#0xC0000000 @see S3C2440A manual P7-9

mcr p15,0,r0,c1,c0,0

ldr r0, =0x4C000004 @MPLLCON register

ldr r1, =0x0005C011 @((92<<12)|(1<<4)|(1))

str r1, [r0] @FCLK is400 MHz !

最后的结果是,FCLK=400MHz,HCLK=100MHz,PCLK=50MHz。

@ SDRAM Init

mov r1, #0x48000000 @MEM_CTL_BASE

adrl r2, mem_cfg_val

add r3, r1, #52

1:

ldr r4, [r2], #4 @ 读取设置值,并让r2加4

str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4

cmp r1, r3 @ 判断是否设置完所有13个寄存器

bne 1b @ 若没有写成,继续

设置存储控制器。

ldr sp, =0x32FFF000 @设置堆栈

bl nand_init @初始化NAND Flash

@nand_read_ll函数需要3个

参数:

ldr r0, =0x33000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址

mov r1, #0 @2. 源地址 =0,S-Boot代码都存在NAND地址0开始处 mov r2, #102400 @3. 复制长度=102400(bytes)

bl nand_read @调用C函数nand_read

ldr lr, =halt_loop @设置返回地址

ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,故使用向pc赋值的方法进行跳转

halt_loop:

b halt_loop

这里把所有的代码从Nand拷贝到RAM中,然后跳转到main函数去执行。此后程序便在RAM中运行了。但是到目前为止,前面的程序都是在SteppingStone 里运行的。所谓SteppingStone,是指在S3C2440A的内部的4KB的RAM缓存,它总是映射到地址0x00处。硬件加电后会自动将Nand Flash中的前4KB的数据拷贝到Stepping Stone中,然后从地址0x00处开始运行。

如果代码足够小(小于4KB)的话,那只在SteppingStone中运行,加载Linux 内核到内存即可。但通常代码肯定会大于4KB。所以Bootloader一般分为两部分,Stage1的代码在SteppingStone中运行,它会把Stage2的代码拷贝到RAM 中,并跳转到RAM中执行;Stage2的代码在RAM中执行,它可以完成加载内核及其它任何复杂的功能。因为Stage2的起始位置不好确定,为了方便,我们把所有的代码都拷贝到RAM中了。

C 函数nand_read有三个参数,第一个参数为目的地起始地址,第二个参数为源起始地址,第三个参数为要复制的数据长度,以字节为单位。根据ATPCS 函数调用规则,三个参数分别用寄存器r0,r1,r2来传递。我们在内存的0x33000000处存放Bootloader,复制长度根据编译生成的S- Boot.bin映像文件大小,向上取512字节的整数倍。

这里先来规划一下内存空间的分配。RAM的地址范围是从0x30000000到

0x34000000共64MByte。把S-Boot和Kernel放在高地址处,S-Boot从

0x33000000开始,预留8MByte的空间,内核从0x33800000开始,可供使用的空间也是8MByte。因栈空间是向下生长的,我们在 S-Boot下面预留4096Byte 的空闲区域,然后向下为栈空间,故栈指针SP初始化为 0x32FFF000。其实留不留空闲区域是无所谓的,这里只是为了把二者更明显地区分开。我们只设置SVC 模式下的SP,不使用CPU的其它工作模式,所以也没必要设置其它模式下的栈指针。另外,程序中不使用动态内存分配,故而也不必分配堆空间。

2. nand读操作

在编译连接时,我们把上述 start.S 代码放在生成的二进制映像文件的最开始位置,因而也被烧写到 Nand Flash 的最起始位置,因而会被自动拷贝到SteppingStone 里运行。start.S 要完成的任务之一,是把S-Boot的所有代码从Nand Flash拷贝到内存中,这里需要对NAND的读操作,因此对NAND的初始化和读操作要在第一阶段写好。

以开发板上使用的K9F1208为例,每个页(page)为512Byte数据和16Byte 校验,每个块(Block)为32个页,即16KByte数据和512Byte校验。

Nand Flash只用8根线与CPU的DATA0-7连接,位宽为8位,不管是数据、地址或控制字都通过这8根线传递,如果读写数据的话每次只能传输一个字节数据。Nand Flash的操作通过NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT和NFECC 六个寄存器来完成。在S3C2440A数据手册第218页可以看到读写Nand Flash

的操作时序:1. 通过NFCONF寄存器配置Nand Flash;2.写Nand Flash命令到NFCMD寄存器;3.写Nand Flash地址到 NFADDR寄存器;4. 在读写数据时,通过NFSTAT寄存器获得Nand Flash的状态信息。应该在读操作前或写操作后检查

R/nB信号(Ready/Busy信号)。

初始化NAND Flash:S3C2440的NFCONF寄存器用来设置时序参数TACLS、TWRPH0、TWRPH1,设置数据位宽;还有一些只读位。TACLS、 TWRPH0、TWRPH1这三个参数控制的是Nand Flash信号线CLE/ALE与写控制信号nWE的时序关系。

注意,寄存器值转换成实际的时钟周期值时,TACLS不需加1,而TWRPH0

和TWRPH1需要加1。比如NFCONF寄存器中设置 TACLS=1,TWRPH0=3,TWRPH1=0,意思是时序图中 TACLS=1个HCLK时钟,TWRPH0=4个HCLK时钟,TWRPH1=1个HCLK 时钟。

void nand_init(void)

{

//时间参数设为:TACLS=0 TWRPH0=3 TWRPH1=0

NFCONF = 0x300;

/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */

NFCONT = (1<<4)|(1<<1)|(1<<0);

/* 复位NAND Flash */

NFCONT &= ~(1<<1); //发出片选信号

NFCMMD = 0xFF; //复位命令

s3c2440_wait_idle();//循环查询NFSTAT位0,直到它等于1

NFCONT |= 0x2; //取消片选信号

}

读操作:读操作也是以页(512Byte)为单位进行的。在初始上电时,器件进入缺省的“读方式1模式”。在这一模式下,页读操作通过将0x00写入指令寄存器,接着写入3个地址(1个列地址和2个行地址)来启动。一旦页读指令被

器件锁存,下面的页读操作就不需要再重复写入页读指令了。写入页读指令和地址后,处理器可以通过对信号线R//B的分析来判断页读操作是否完成。如果信号为低电平,表示器件正忙;如果信号为高电平,表示器件内部操作完成,要读取的数据被送入了数据寄存器。外部控制器可以再以50ns为周期的连续/RE脉冲信号的控制下,从IO口依次读出数据。连续页读操作中,输出的数据是从指定的列地址开始,直到该页最后一个列地址的数据为止。

for(i=start_addr; i < (start_addr + size);)

{

NFCMMD = 0; //发出READ0命令

s3c2440_write_addr(i); //Write Address

s3c2440_wait_idle(); //循环查询NFSTAT位0,直到它等于1 for(j=0; j < NAND_SECTOR_SIZE; j++, i++)

{

*buf = (unsigned char)NFDATA;

buf++;

}

}

缺点:没有使用ECC校验和纠错;没有使用坏块检查;

3. main 函数

串口初始化,以便能够向用户输出一些信息;网口初始化,以便能够从主机下载内核映像;输出一些菜单,以便用户选择执行所需要的功能。比如,用户可以选择从串口或网口下载内核映像到RAM中某个地址,然后运行这个内核。关于下载内核映像的实现,在后文会详细介绍。这里只看当内核映像已经存在于RAM 中时,怎样才能把这个内核启动起来。

4. 启动参数的传递

启动Linux内核之前需要设置好一些必要的启动参数,这些参数以TAG列表的形式传递给内核。所谓TAG列表,就是多个TAG在内存空间中按顺序排列。每个TAG,其实都是一个结构体,每个结构体中又包含了一个头部结构体和一个内容结构体称。头部结构体指明了本TAG的类型、占用空间大小;所谓TAG的类型,就是一个宏定义,用一个确定的整数来识别该标记。内容结构体包含了该TAG

的具体内容。

下面以具体的例子做说明。

在atag.h中就有:

#define ATAG_CORE 0x54410001

#define ATAG_MEM 0x54410002

#define ATAG_CMDLINE 0x54410009

#define ATAG_NONE 0x00000000

这些都是TAG的类型,注意这些整数跟地址没有关系,只是一个用来识别标记类型的符号而已。

每个Tag都用结构体表示,包含TagHeader 头结构体以及随后的参数值数据结构。如 ATAG_CORE:

struct Atag {

struct TagHeader stHdr;

struct TagCore stCore;

};

其中包含两个结构体。第一个结构体TagHeader含两个整型变量,用以表示本结构体的长度、标记类型;nSzie赋值为头部TagHeader和数据TagCore的大小之和,注意是以字(即4字节)为单位;ulTag 就赋值为先前定义的宏

ATAG_CORE。第二个结构体就是实际的数据了。

struct TagHeader {

UINT32 nSize;

UINT32 ulTag;

};

struct TagCore {

UINT32 ulFlags;

UINT32 nPageSize;

UINT32 ulRootDev;

};

由于每个Tag都由一个TagHeader加一个数据部分组成,因此通常的做法是使用Struct和Union相结合来定义:

struct Atag {

struct TagHeader stHdr;

union {

struct TagCore stCore;

struct TagMem32 stMem;

struct TagVideoText stVideoText;

struct TagRamDisk stRamDisk;

struct TagInitrd stInitRd;

struct TagSerialnr stSerialNr;

struct TagRevision stRevision;

struct TagVideolfb stVideoLfb;

struct TagCmdline stCmdLine;

};

};

其中涉及到的所有数据结构均可在 Linux 内核源码的

include/asm/setup.h 头文件找到,我们把这些定义放在Bootloader的头文件atag.h中。

启动参数标记列表以标记 ATAG_CORE 开始,以标记 ATAG_NONE 结束。每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。数据结构 tag 和 tag_header 定义在 Linux 内核源码的

include/asm/setup.h 头文件中,在我们的S-Boot中对应的头文件为 atag.h。

在嵌入式 Linux 系统中,通常需要由 Boot Loader 设置的常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。

向内核传递参数的方法,先在内存中某个起始地址开始,连续存放多个Tag, 组成Tag列表。列表中的每个Tag包括头部TagHeader和数据结构体。按规定,第一个Tag必须是ATAG_CORE,最末一个Tag必须是ATAG_NONE,而且中间必须包含至少一个ATAG_MEM。注意的是末尾的ATAG_NONE只包括头部,没有数据内容。如图所示。

在编程时先定义好起始地址,然后用一个指针,每设置完毕一个Tag的内容就向后移动相应的长度,然后设置下一个Tag内容,以保证各个Tag的连续存放。

下面具体说明几个关键Tag的数据区域内容的设置。struct TagCore结构体已经在前面列出,它包含三个整型变量,ulFlags一般设为零,nPageSize表示分页内存管理中每一页的大小,一般为4096字节,ulRootDev是系统启动的设备号,设为零即可,因为通常在后面的命令行参数Cmdline中覆盖这个设置。Struct TagMem用来描述系统的物理内存地址空间,定义如下:

struct atag_mem {

UINT32 nSize; /* size of the area */

UINT32 ulStart; /* physical start address */

};

其中nSzie表示内存的总大小,ulStart为内存的起始物理地址,二者结合告诉内核系统可用的物理内存空间是哪些。Struct TagCmdline结构体的定义就更简单了,只是一个字符数组,初始长度为1,如下所示:

struct TagCmdline {

char cCmdLine[1]; /* this is the minimum size */

};

实际上命令行参数不可能只有一个字节,我们通常使用strcpy函数把命令行参数拷贝到cCmdLine地址处,在结尾附加一个字符串结束符’\0’,然后用strlen函数获得cCmdLine数组的实际长度(包括字符串结束符)。常见的命令行参数如:root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200

mem=65536。我们知道的是,Bootloader以标记列表的形式向内核传递的参数,大概有10种不同类型的Tag,而命令行参数只是其中的一种。其它需要设置的Tag包括ATAG_RAMDISK、ATAG_INITRD等,此处不再详细介绍。

在我们的S-Boot中设置了ATAG_CORE,ATAG_MEM,ATAG_CMDLINE,ATAG_NONE 四项。其中CmdLine 使用的是:

const char *CmdLine = "root=/dev/nfs

nfsroot=192.168.1.249:/home/hongwang/mkrootfs/rootfs

ip=192.168.1.252:192.168.1.249:192.168.1.1:255.255.255.0:https://www.doczj.com/doc/8e1688334.html,:et h0:off console=ttySAC0,115200 init=/linuxrc mem=65536K console=tty1 fbcon=rotate:2";

这里root=/dev/nfs表示使用NFS做根文件系统,注意并不真的存在

/dev/nfs这个设备,它只是一个符号而已,告诉内核使用NFS而不是使用真正的设备做根文件系统。

nfsroot=[:][,]

nfsroot=192.168.1.249:/home/hongwang/mkrootfs/rootfs是NFS服务器地址及要挂载的目录。

ip=::::::

ip=192.168.1.252:192.168.1.249:192.168.1.1:255.255.255.0:https://www.doczj.com/doc/8e1688334.html,:et h0:off

只说明一下autoconf,这一个选项指明开发板使用的自动配置IP地址的方法,有时开发板可以设置成通过DHCP或者BOOTP等协议从服务器获取IP地址。off 或 none 表示不使用自动配置,使用指定的静态IP地址信息。

console=ttySAC0,115200 串口控制台

console=tty1 fbcon=rotate:2 液晶屏Framebuffer控制台,如果内核支持,可以在LCD屏幕上显示Linux内核启动过程,起点结束后在LCD屏幕上进入Shell 控制台供用户操作。fbcon=rotate:2表示控制台旋转180度,若为1表示旋转90度,3旋转270度,0不旋转。

5. boot kernel zImage

zImage 二进制文件包含两部分内容,起始部分是解压缩程序,后面是压缩的内核。解压缩程序是最先运行的,内核中文件是:arch/arm/boot

/compressed/head.S,它负责把压缩的内核解压到0x30008000处。因此zImage 可以下载到RAM任意位置处,由解压缩程序负责搬移到正确的运行地址。

所以 Bootloader启动Linux内核的方法就是直接跳转到内核的第一条指令处,也就是跳转到内存中存放内核映像的开始地址,内核映像具有自解压功能,会把自己释放到正确的运行地址。Tag列表怎样传给内核呢?使用的方法是把Tag 列表的起始地址传给内核。首先,定义一个指向函数的指针:

typedef void (*LINUX_KERNEL_ENTRY)(int, int, UINT32);

LINUX_KERNEL_ENTRY pfExecKernel;

这样pfExecKernel就是一个函数指针,函数具有三个整型变量。然后,让pfExecKernel指向内核映像的起始地址处,这里使用强制类型转换把地址转换成函数指针类型:

pfExecKernel = (LINUX_KERNEL_ENTRY)pKernelStartAddr;

最后,以三个参数调用pfExecKernel函数:

pfExecKernel(0, MACH_ID, ATAG_BASE);

其中第一个参数默认为零,可以不必理会。第二个参数是机器ID号,不同的CPU有不同的号码与之对应,可以在内核源代码的

linux/arch/arm/tools/mach-types 文件中查到,S3C2440 对应的MACH_ID 为362。第三个参数ATAG_BASE就是上文讲到的Tag列表的首地址。

这个函数调用的作用其实就是设置 r0=0,r1=机器ID,r2=TAG首地址,然后跳到arch/arm/boot/compressed/head.S文件中的第一条指令处。

既然可以把TAG首地址传递给内核,那么TAG LIST就可以放在RAM中的任何位置了,只要不与其它有用内容冲突即可。但是事实却并不是想象的这样。实验发现,第三个参数传递进去的TAG首地址似乎没有起到作用,因为启动时总是找不到正确的启动参数。后来发现内核有个默认的TAG首地址0x30000100,它总是到0x30000100去寻找启动参数,而不理会我们传进来的第三个参数。所以,S-Boot中把TAG首地址就设置为0x30000100。

6. 小结

综上所述,包含最基本功能的S-Boot运行流程已经很清楚了。下图对此作了一个总结。

BootLoader引导程序

BootLoader引导程序 一、实验目的 1.学会配置linux下的minicom和windows下的超级终端 2.了解bootloader的基本概念和框架结构 3.了解bootloader引导操作系统的过程 4.掌握bootloader程序的编译方法 5.掌握bootloader程序的使用方法 二、实验内容 1. 学习x-loader 作用和编译过程 2.学习uboot作用和编译过程 3.学习bootloader的操作 三、实验设备 PentiumII以上的PC机, LINUX操作系统 四、BOOTLOADER程序说明 完整的系统由x-loader、u-boot、kernel(内核)、rootfs(根文件系统)组成,x-loader 是一级引导程序,其作用是初始化CPU,拷贝u-boot到内存,然后把控制权交给u-boot。当OMAP3530上电时,memory controller(内存控制器)还未初始化,这个任务便由完成的x-loader。初始化外部RAM控制器,把u-boot读到外部RAM,之后把控制入口交给。u-boot 是二级引导程序,其作用主要是引导内核,提供映像更新,同用户进行交互。系统结构图如 下: 1. BootLoader的作用 在嵌入式系统中,BootLoader的作用与PC机上的BIOS类似,其主要作用:(1)初始化硬件设备;(2)建立内存空间的映射图;(3)完成内核的加载,为内核设置启动参数。通过BootLoader可以完成对系统板上的主要部件如CPU、SDRAM、Flash、串行口等进行初始化,也可以下载文件到系统板上,对Flash进行擦除与编程。当运行操作系统时,它会在操作系统内核运行之前运行,通过它,可以分配内存空间的映射,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统准备好正确的环境。 通常,BootLoader 是依赖于硬件而实现的,特别是在嵌入式系统中。因此,在嵌入式系统里建立一个通用的 BootLoader 几乎是不可能的,不同的处理器架构都有不同的

详解bootloader的执行流程与ARM Linux启动过程分析

详解bootloader的执行流程与ARM Linux启动过程分析 ARM Linux启动过程分析是本文要介绍的内容,嵌入式Linux 的可移植性使得我们可以在各种电子产品上看到它的身影。对于不同体系结构的处理器来说Linux的启动过程也有所不同。 本文以S3C2410 ARM处理器为例,详细分析了系统上电后bootloader的执行流程及ARM Linux的启动过程。 1、引言 Linux 最初是由瑞典赫尔辛基大学的学生Linus Torvalds在1991 年开发出来的,之后在GNU的支持下,Linux 获得了巨大的发展。虽然Linux 在桌面PC 机上的普及程度远不及微软的Windows 操作系统,但它的发展速度之快、用户数量的日益增多,也是微软所不能轻视的。而近些年来Linux 在嵌入式领域的迅猛发展,更是给Linux 注入了新的活力。 一个嵌入式Linux 系统从软件角度看可以分为四个部分:引导加载程序(bootloader),Linux 内核,文件系统,应用程序。 其中bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用Linux 内核。 Linux 内核在完成系统的初始化之后需要挂载某个文件系统做为根文件系统(Root Filesystem)。 根文件系统是Linux 系统的核心组成部分,它可以做为Linux 系统中文件和数据的存储区域,通常它还包括系统配置文件和运行应用软件所需要的库。 应用程序可以说是嵌入式系统的“灵魂”,它所实现的功能通常就是设计该嵌入式系统所要达到的目标。如果没有应用程序的支持,任何硬件上设计精良的嵌入式系统都没有实用意义。 从以上分析我们可以看出bootloader 和Linux 内核在嵌入式系统中的关系和作用。Bootloader在运行过程中虽然具有初始化系统和执行用户输入的命令等作用,但它最根本

bootloader

Boot Loader的启动流程和开发经验总结 Windows CE最大程度继承了桌面版Windows的丰富功能,但是Windows CE并不是一个通用的安装版操作系统。在形形色色的嵌入式设备世界里,一款CE系统通常只会针对某一种硬 件平台生成。 一般来说,Windows CE的开发过程可以分为:0AL(OEM Abstraction Layer)、驱动、应用程序开发三个步骤。其中,0AL开发最基本的一步是板级支持包(BSP),而BootLoader 设计则在BSP开发中具有极为关键的地位。 1.什么是BootLoader 嵌入式系统的启动代码一般由两部分构成:引导代码和操作系统执行环境的初始化代码。其中引导代码一般也由两部分构成:第一部分是板级、片级初始化代码,主要功能是通过设置寄存器初始化硬件的工作方式,如设置时钟、中断控制寄存器等,完成内存映射、初始化MMU等。第二部分是装载程序,将操作系统和应用程序的映像从只读存储器装载或者拷贝到系统的RAM中并执行。 (1)什么是板级BSP? BSP(Board Support Package)是板级支持包,是介于主板硬件和操作系统之间的一层,主要是为了支持操作系统,使之能够更好的运行于硬件主板。不同的操作系统对应于不同形式的BSP,例如WinCE的BSP和Linux的BSP相对于某CPU来说尽管实现的功能一样,可是写法和接口定义是完全不同的。所以,BSP一定要按照该系统BSP的定义形式来写,这样才能与上 层OS保持正确的接口,良好的支持上层OS。 (2)什么是Boot Loader

在BSP中有一个重要的组成部分就是BootLoader,它是在操作系统内核运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,为调用操作系统内核准备好环境。 一般来说,在嵌入式世界里BootLoader 是严重地依赖于硬件的,因此想建立一个通用的 BootLoader 几乎是不可能的。不同的 CPU 体系结构有不同的BootLoader,而且除了依赖于 CPU的体系结构外,BootLoader还依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种 CPU 结构而构建的,要想让运行在一块板子上的 BootLoader 程序也能运行在另一块板子上,通常也都需要修改 BootLoader 的源程序。 2.BootLoader在PC机与嵌入式的区别比较 (1)引导程序在PC机和嵌入式上的区别 一般来说,在PC的硬件平台上,由于硬件启动根本就不是通过BootLoader(而是通过BIOS),所以BootLoader就不需要对CPU加电后的初始化做任何工作。在桌面系统中,有以下几种设备可以作为启动设备使用:硬盘、USB盘、光盘驱动器、还有网卡的Boot ROM 等。但无论选择了哪一种启动设备,操作系统都会去将该设备起始地址的内容读入内存,BIOS 将控制移交给引导装载程序。如果启动设备是IDE硬盘,这时通常将引导装载程序装入第一个扇区(通常被称做主引导扇区,MBR),然后将内容读入内存再运行。 在嵌入式平台上,引导装载程序是在硬件上执行的第一段代码,通常将引导程序放置在不易丢失的存储器的开始地址或者是系统冷启动时PC寄存器的初始值。在嵌入式系统中,通常并没有像BIOS那样的固件程序,因此整个系统的加载启动任务就完全由BootLoader来完

bootloader分析

Bootloader分析

?熟悉BootLoader的实现原理?认识Bootloader的主要任务?熟悉BootLoader的结构框架?U-boot使用

引言本章详细地介绍了基于嵌入式系统中的OS启动加载程序――Boot Loader的概念、软件设计的主要任务以及结构框架等内容。 一个嵌入式Linux系统从软件的角度看通常可以分为四个层次: ?1.引导加载程序。包括固化在固件(firmware)中的boot代码(可 选),和Boot Loader两大部分。 ?2.Linux内核。特定于嵌入式板子的定制内核以及内核的启动参数。 ?3.文件系统。包括根文件系统和建立于Flash内存设备之上文件 系统。通常用ram disk来作为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程序。

Linux启动全过程-由bootloader到fs

Linux启动过程 许多人对Linux的启动过程感到很神秘,因为所有的启动信息都在屏幕上一闪而过。其实Linux的启动过程并不象启动信息所显示的那样复杂,它主要分成两个阶段: 1.启动内核。在这个阶段,内核装入内存并在初始化每个设备驱动器时打印信息。 2.执行程序init。装入内核并初始化设备后,运行init程序。init程序处理所有程序的启动, 包括重要系统精灵程序和其它指定在启动时装入的软件。 下面以Red Hat为例简单介绍一下Linux的启动过程。 一、启动内核 首先介绍启动内核部分。电脑启动时,BIOS装载MBR,然后从当前活动分区启动,LILO获得引导过程的控制权后,会显示LILO提示符。此时如果用户不进行任何操作,LILO将在等待制定时间后自动引导默认的操作系统,而如果在此期间按下TAB键,则可以看到一个可引导的操作系统列表,选择相应的操作系统名称就能进入相应的操作系统。当用户选择启动LINUX操作系统时,LILO就会根据事先设置好的信息从ROOT文件系统所在的分区读取LINUX映象,然后装入内核映象并将控制权交给LINUX内核。LINUX内核获得控制权后,以如下步骤继续引导系统: 1. LINUX内核一般是压缩保存的,因此,它首先要进行自身的解压缩。内核映象前面的一些代码完成解压缩。 2. 如果系统中安装有可支持特殊文本模式的、且LINUX可识别的SVGA卡,LINUX会提示用户选择适当的文本显示模式。但如果在内核的编译过程中预先设置了文本模式,则不会提示选择显示模式。该显示模式可通过LILO或RDEV工具程序设置。 3. 内核接下来检测其他的硬件设备,例如硬盘、软盘和网卡等,并对相应的设备驱动程序进行配置。这时,显示器上出现内核运行输出的一些硬件信息。 4. 接下来,内核装载ROOT文件系统。ROOT文件系统的位置可在编译内核时指定,也可通过LILO 或RDEV指定。文件系统的类型可自动检测。如果由于某些原因装载失败,则内核启动失败,最终会终止系统。 二、执行init程序 其次介绍init程序,利用init程序可以方便地定制启动其间装入哪些程序。init的任务是启动新进程和退出时重新启动其它进程。例如,在大多数Linux系统中,启动时最初装入六个虚拟的控制台进程,退出控制台窗口时,进程死亡,然后init启动新的虚拟登录控制台,因而总是提供六个虚拟登陆控控制台进程。控制init程序操作的规则存放在文件/etc/inittab中。Red Hat Linux缺省的inittab文件如下:# #inittab This file describes how the INIT process should set up the system in a certain #run-level. # # #Default runlevel.The runlevels used by RHS are: #0-halt(Do NOT set initdefault to this) #1-Single user mode #2-Multiuser,without NFS(the same as 3,if you do not have networking) #3-Full multiuser mode #4-unused #5-X11 #6-reboot(Do NOT set initdefault to this)

Boot_Loader介绍

Boot Loader Windows CE最大程度继承了桌面版Windows的丰富功能,但是Windows CE并不是一个通用的安装版操作系统。在形形色色的嵌入式设备世界里,一款CE系统通常只会针对某一种硬件平台生成。 一般来说,Windows CE的开发过程可以分为:0AL(OEM Abstraction Layer)、驱动、应用程序开发三个步骤。其中,0AL开发最基本的一步是板级支持包(BSP),而BootLoader 设计则在BSP开发中具有极为关键的地位。 1.什么是BootLoader 嵌入式系统的启动代码一般由两部分构成:引导代码和操作系统执行环境的初始化代码。其中引导代码一般也由两部分构成:第一部分是板级、片级初始化代码,主要功能是通过设置寄存器初始化硬件的工作方式,如设置时钟、中断控制寄存器等,完成内存映射、初始化MMU等。第二部分是装载程序,将操作系统和应用程序的映像从只读存储器装载或者拷贝到系统的RAM中并执行。 (1)什么是板级BSP? BSP(Board Support Package)是板级支持包,是介于主板硬件和操作系统之间的一层,主要是为了支持操作系统,使之能够更好的运行于硬件主板。不同的操作系统对应于不同形式的BSP,例如WinCE的BSP和Linux的BSP相对于某CPU来说尽管实现的功能一样,可是写法和接口定义是完全不同的。所以,BSP一定要按照该系统BSP的定义形式来写,这样才能与上层OS保持正确的接口,良好的支持上层OS。 (2)什么是Boot Loader 在BSP中有一个重要的组成部分就是BootLoader,它是在操作系统内核运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,为调用操作系统内核准备好环境。

单片机自编程及Bootloader设计

?Bootloader是在单片机上电启动时执行的一小段程序。也称作固件,通过这段程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用应用程序准备好正确的环境。 Boot代码由MCU启动时执行的指令组成。这里的loader指向MCU的Flash中写入新的应用程序。因此,Bootloader是依赖于特定的硬件而实现的,因此,在众多嵌入式产品中目前还不可能实现通用Bootloader。 Bootloader的最大优点是:在不需要外部编程器的情况下,对嵌入式产品的应用代码进行更新升级。它使得通过局域网或者Intemet远程更新程序成为可能。例如,如果有5 000个基于MCU的电能表应用程序需要更新,电能表制造商的技术人员就可以避免从事对每一个电能表重新编程的巨大工作量,通过使用Bootloader的功能,由控制中心通过电能表抄表系统网络,远程对5 000个电表重新编程。可见,Bootloader功能对于嵌入式系统的广泛应用具有十分重要的意义。 1 78K0/Fx2系列单片机简介 78K0/Fx2系列是带CAN控制器的8位单片机,该系列单片机广泛应用于汽车电子,智能仪表等领域。其内置POC(可编程上电清零电路)/LVI(可编程低电压指示器),单电压自编程闪存,引导交换功能(闪存安全保护),具有低功耗、宽电压范围、超高抗干扰等性能。 78K0系列单片机支持自编程(Self-programming)。所谓自编程,是指用Flash存储器中的驻留的软件或程序对Flash存储器进行擦除/编程的方法。通过单片机的自编程功能,可以设计Bootloader程序,通过串口等通信接口实现对产品重新编程、在线升级的功能。 以μPD78F0881为例。μPD78F0881为78KO/Fx2系列中的一款44管脚单片机,内置32 KB Flash ROM,2 KB RAM,自带2个串行通信接口。其内部Flash结构如图1所示。为了方便实现擦除和编程,人为地将整个Flash分成若干个block,每个block 大小为1 KB。block为自编程库函数中空白检测、擦除、校验的最小单位。blockO从地址0000H开始,程序都从0000H开始执行。block0~block3共4 KB存储空间为 Bootloader程序存储区域。block4~block31为应用程序存储区域。

bootloader流程

Bootloader 设计分析 3.1 Bootloader 的操作模式 (Operation Mode) 大多数 Bootloader 都包含两种不同的操作模式[2]: (1). 启动加载(Boot loading)模式:也称为“自主”模式。即Bootloader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。 (2).下载(Downloading)模式:在这种模式下,目标机上的Bootloader将通过串口或网络连接等通信手段从主机(Host)下载内核映像和根文件系统映像等,然后保存到目标机上的FLASH 类固态存储设备中。Bootloader的这种模式通常在系统初次安装和更新时被使用,工作于这种模式下的Bootloader通常都会向它的终端用户提供一个简单的命令行接口。 在我们的Bootloader设计中我们同时支持这两种工作模式,采用的方法是:一开始启动时处于正常的启动加载模式,但并不立即启动进入uClinux内核,而是提示延时5秒,等待终端用户如果按下某一特定按键,则切换到下载模式,否则继续启动uCLinux 内核。 3.2 Bootloader 的启动及初始化 基于ARM的芯片多数为复杂的片上系统(SoC),这类复杂系统里的多数硬件模块都是可配置的[3]。因此大多数 Bootloader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,通常都放在 stage1 中,而且在这一部分,我们直接对处理器内核和硬件控制器进行编程,因此常常都用汇编语言来实现。而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。 3.2.1 Bootloader的stage1 这部分代码必须首先完成一些基本的硬件初始化,为stage2的执行以及随后的kernel 的执行准备好一些基本的硬件环境[2]。Bootloader的stage1一般通用的内容包括: * 定义程序入口点 * 设置异常向量表 * 初始化存储系统(包括地址重映射) * 初始化有特殊要求的端口,设备 * 初始化用户程序的执行环境

移植Bootloader过程总结

一.linux系统上电后启动过程: ---→启动引导加载程序bootloader(一些CPU在运行bootloader之前,会先运行一段固化的程序)。 --->启动内核 --->挂载根文件系统 其中,Boot paramoters分区中放置一些可设置的参数,比如,IP地址、串口波特率、要传递给内核的命令行参数等。 二.为什么要进行bootloader移植? ---→bootloader的实现依赖于具体的硬件,而在嵌入式产品中硬件的配置千差万别,即使相同的CPU,它的外设也有可能不同,所以不可能有一个bootloader支持所有的CPU,即使是支持CPU架构比较多的U-Boot,也不是拿来就能用的,也需要做一些简单的移植。 三.Bootlader的启动过程分为两个阶段: 第一阶段: --->硬件设备的初始化(进入svc模式、关闭watchdog、禁中断、设置系统时钟频率、初始化存储控制器、初始化堆栈)。 --->uboot的代码重定位,从nand中拷贝至sdram中,默认是从norflash拷贝至sdram --->跳转到第二阶段c代码继续执行 第二阶段: --->初始化本阶段要使用的硬件设备(如串口)。 --->检测系统内存的映射(memory map)。 --->从flash读取内核镜像和文件系统到sdram中。

--->为内核设置启动参数。 --->启动内核。 注意:1、所谓检测内存映射,就是确定板子上使用了多少内存,它们的地址空间如何 2、存储在flash上的内核镜像可能是压缩的(如.cramfs 格式),在读到内存中就需要解压,但是对于带有自解压功能的内核来说,是不需要boorloader来解压。 3、将根文件系统拷贝到sdram中,这个不是必须的,具体看内核访问它的方式。

BOOTLOADER启动程序分析

BOOTLOADER启动程序分析 ——基于工程D003_Malata35P_63509_chinese 桂洁 2007.12.3 .eboot.nb0是第一次被JTAG烧到NOR。以后,bootloader负责对:xip.bin:先写到RAM,再烧到nand flash; nk.bin:直接写到RAM; eboot.bin:由eboot.nb0下载,先写到RAM,再烧到nor flash。 上电后:eboot本身会将自己写到RAM。也会把nand中的镜像写到RAM中再执行。 程序一上电,自动执行0x00000000处的指令。所以,只要把这条指令设定好,下面就可以按我们自己的思想进行设计。 一.f wxsc1.s 位于:D:\WINCE420\PLA TFORM\SEUICBSP\KERNEL\HAL\ARM 这是startup。他是一个汇编程序。 1.入口为:LEAF_ENTRY StartUp,第一条语句是:B Reset_Handler, 放在0x00000000处。所以上电后首先执行这条指令。 2.Reset_Handler,是StartUp的主体。根据复位类型进行硬件的初始 化。具体步骤为(可能不是很准确):串口初始化,CPU进入特殊指令模式,关MMU,关中断,检查复位原因,初始化硬件(GPIO,

内存,中断控制器,时钟,PowerManager),通过串口打印信息,查SDRAM,bootloader将自己写到RAM,检查正确性,跳到RAM,设置并使能MMU,Cache,设堆。 3.最后一条语句是bl main。跳到main函数。 二main 1.main 位置:D:\WINCE420\PLA TFORM\SEUICBSP\EBOOT\main.c。 作用:调用BootloaderMain()。 2.BootloaderMain 位置: D:\WINCE420\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BLCOMMON\blco mmon.c 作用:这是bootloader的框架。

BootLoader 的概念与功能

U-Boot之一:BootLoader 的概念与功能 一般情况下嵌入式Linux 系统中的软件主要分为以下几部分: 1) 引导加载程序:其中包括内部ROM 中的固化启动代码和BootLoader 两部分。内部固化ROM 是厂家在芯片生产时候固化的,作用基本上是引导BootLoader。有的芯片比较复杂,比如Omap3 在flash 中没有代码的时候有许多启动方式:USB、UART 或以太网等等。而S3C24x0 则很简单,只有Norboot 和Nandboot。 2) Linux kernel 和drivers。 3) 文件系统。包括根文件系统和建立于Flash 内存设备之上的文件系统(EXT4、UBI、CRAMFS 等等)。它是提供管理系统的各种配置文件以及系统执行用户应用程序的良好运行环境及载体。 4) 应用程序。用户自定义的应用程序,存放于文件系统之中。 在Flash 存储器中,他们的分布一般如下: 但是以上只是大部分情况下的分布,也有一些可能根文件系统是initramfs,被一起压缩到了内核映像里,或者没有Bootloader 参数区,等等。 1.2 在嵌入式Linux 中BootLoader 的必要性 Linux 内核的启动除了内核映像必须在主存的适当位置,CPU 还必须具备一定的条件:

但是在CPU 刚上电启动的时候,一般连内存控制器都没有初始化过,根本无法在主存中运行程序,更不可能处在Linux 内核启动环境中。为了初始化CPU 及其他外设,使得Linux 内核可以在系统主存中运行,并让系统符合Linux 内核启动的必备条件,必须要有一个先于内核运行的程序,他就是所谓的引导加载程序(Boot Loader)。 而BootLoader 并不是Linux 才需要,而是几乎所有运行操作系统的设备都需要。我们的PC 的BOIS 就是Boot Loader 的一部分(只是前期引导,后面一般还有外存中的各种BootLoader),对于Linux PC 来说,Boot Loader = BIOS + GRUB/LILO。 1.3 Boot Loader 的功能和选择 综上所述:BootLoader 是在操作系统内核启动之前运行的一段小程序。通过这段程序,我们可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境,最后从别处(Flash、以太网、UART)载入内核映像到主存并跳到入口地址。 由于BootLoader 需要直接操作硬件,所以它严重依赖于硬件,而且依据所引导的操作系统的不同,也有不同的选择。对于嵌入式世界中更是如此。就S3C24x0 而言,如果是引导Linux,一般选用韩国的mizi 公司设计的vivi 或者DENX 软件工程中心的Das U-boot,如果是引导Win CE,就选用Eboot。如果是开发StrongARM 构架下的LART,就可选用由Jan-Derk Bakker 和Erik Mouw 发布的Blob(Boot Loader Object)。如果是要引导eCos 系统,可 以选用同是RedHat公司开发的Redboot。 所以在嵌入式世界中建立一个通用的BootLoader 几乎是不可能的,而可能的是让一个Boot Loader 代码支持多种不同的构架和操作系统,并让她有很好的可移植性。U-boot 就是支持多平台多操作系统的一个杰出代表。这也是U-boot 的优势所在,因为如果在开发 S3C2440 时熟悉了U-boot,再转到别的平台的时候,就可以很快地完成这个平台下U-boot 的移植。而且U-boot 的代码结构越来越合理,对于新功能的添加也十分容易。

基于ARM9的Bootloader启动流程分析

1引言 在嵌入式系统中,通常没有像PC机中的BIOS那样的固件程序,因此整个系统的加载启动任务就完全由Bootloader来完成。Bootloader是CPU加电后运行的第一段程序,它的作用就是对嵌入式系统中的硬件进行初始化,创建内核需要的一些信息并将这些信息通过相关机制传递给内核,从而将系统的软硬件环境带到一个合适的状态,最终调用操作系统内核,真正起到引导和加载内核的作用[1]。 Vivi是韩国Mizi公司开发的开源Bootloader,适用于ARM9核的S3C2410处理器。Vivi的代码包括arch,init,lib,drivers和include等几个目录,共200多个文件。 2系统启动流程 本系统所用的S3C2410处理器是三星公司生产的,其内核是ARM920T。它片内集成了MMU,因此可以支持linux和WinCE等操作系统的运行。芯片内部32M的SDRAM,还集成了32M的NandFlash和2M的NorFlash。Vivi可以通过NandFlash启动,也可通过NorFlash启动。 NandFlash的特点就是代码不能在NandFlash上直接执行,要先下载在系统的RAM中才能执行。当前市场上基于S3C2410微处理器的开发板绝大部分都是用NandFlash作为主存储器。本文以NandFlash为例来分析Vivi的启动过程。 Vivi在Nandflash中有两种工作模式:启动加载模式与下载模式。 在启动加载模式(Autonomous)下,Vivi从目标机系统的Flash存储器中把操作系统加载到RAM中运行。这是嵌入式系统在正常工作的时候采用的工作模式。由于RAM的存取速度要比Flash快的多,这样做能提高代码的执行速度,加大系统的实时性。 下载模式(Downloading)下,从主机上下载的文件通常被Vivi保存到目标机的RAM中,然后再被烧写到Flash中。在第一次安装Bootloader、系统内核和跟文件系统时,都采取这种工作模式,以后系统更新也会采取这种工作模式[2]。 Vivi启动过程可以分为Stage1和Stage2两部分。Stage1是和系统硬件密切相关的部分,用汇编语言编写。Stage2用C语言编写,来实现一般的流程以及对板级的一些驱动支持。如果是相同的CPU以及存储设备,要增加外设支持的话,我们只需要对Stage2的代码进行修改,Stage1的代码可就以保持不变;如果要支持体系结构不同的CPU,我们只要修改Stage2部分的代码就可以了。 Stage1代码在文件Vivi/arch/S3C2410/Head.S目录下的Head.S中。它的主要工作是进行与CPU核以及存储设备密切相关的一些必要的初始化工作。按照Head.S文件中的代码执行顺序,依次完成了下面的任务: (1)启动代码的第一步是设置异常和中断向量表。 (2)关闭看门狗定时器。系统加电以后,看门狗默认是开着的,所以要关掉。 (3)关闭所有中断。中断的操作是在操作系统里边进行的,在Bootloader阶段,不需要用到中断。 (4)初始化系统时钟频率。S3C2410有两个锁相环,一个叫MPLL,另一个叫UPLL。MPLL用来产生FCLK,HCLK,PCLK,支持内核,AHB总线和APB总线。UPLL用来支持USB总线。通过设置MPLL可以把时钟频率设置为200MHz或者100MHz。 (5)初始化内存控制器。S3C2410共有15个寄存器,在此初始化其中的13个寄存器。 (6)点亮LED。通过配置GPIO口来点亮开发板上的LED。 (7)初始化UART。主要完成两个任务:设置GPIO选择UART0所用的引脚;初始化UART,设置工作方式(使用FIFO、波特率115200、无硬件流控等)。在终端也要如此设置,才能使用串口与S3C2410通信,通过终端显示Vivi启动状态。 (8)跳到内存测试函数。为了确保S3C2410所安排的地址范围的确是可读写的RAM空间,因此必须对所安排的地址范围进行测试。 (9)将全部的代码拷贝到SDRAM中。S3C2410的存储器是统一编址的。Flash区的地址是ox30000000到0x38000000,RAM区的地址是0x38000000到0x40000000。拷贝Vivi是调用nand_read_ll()函数来实现的,调用函数前要先设置三个参数:r0=目的地址(SDRAM的地址),r1=源地址(nandflash的地址),r2=复制的长度(以字节为单位)。 (10)跳到Stage2阶段运行。亦即调用init/main.c中的main函数。 Stage2部分主要完成了开发板的初始化。主要包括内存映射和内 3Vivi汇编代码分析 在系统加电后,通常CPU都是从复位地址上取第一条指令。ARM9核的CPU都是从系统最低端,也就是0x00000000处来取第一条指令。在NandFlash中,程序不能直接运行,S3C2410通过芯片内部一个4K大小的名字叫做Stepstone的Buffer来实现。Vivi在启动的时候先把自己在flash中开始的前4K内容映射到Stepstone中,映射地址是0x00000000。[3] Bootloader的第一条指令就放在这里。Vivi代码是从异常向量表开始的。 .text/*下面是代码段*/ ENTRY(_start)/*文件vivi/include/linkage.h里边的宏封装,程序默认入口点为_start*/ bReset/*跳转到reset处执行*/ bHandleUndef/*未定义的指令异常*/ bHandleSWI/*软件中断异常*/ bHandlePrefetchAbort/*内存操作异常*/ bHandleDataAbort/*数据异常*/ bHandleNotUsed/*未使用*/ bHandleIRQ/*慢速中断处理*/ bHandleFIQ/*快速中断处理*/ …… Reset:/*上电后,转到此处执行*/ movr1,#0x53000000/*WTCON寄存器地址是0x53000000*/ movr2,#0x0/*WTCON寄存器清0,禁止看门狗*/ strr2,[r1] …… movr1,#0x56000000/*GPACON寄存器地址是0x56000000*/movr2,#0x00000005 strr2,[r1,#0x70]/*配置GPHCON寄存器*/ movr2,#0x00000001 strr2,[r1,#0x78]/*配置GPHUP寄存器*/ movr2,#0x00000001 strr2,[r1,#0x74]/*配置GPHDAT寄存器*/ movr1,#INT_CTL_BASE movr2,#0xffffffff strr2,[r1,#oINTMSK]/*关闭所有中断*/ ldrr2,=0x7ff strr2,[r1,#oINTSUBMSK]/*设置次级中断屏蔽寄存器*/ movr1,#CLK_CTL_BASE mvnr2,#0xff000000 /*S3C2410CPU默认的工作主频为12MHz,使用PLL电路可以产生更高的主频供CPU及外围器件使用。S3C2410有两个PLL:MPLL和UPLL,UPLL专用于USB设备;MPLL用于CPU及其它外围器件。通过MPLL会产生三个部分的时钟频率:FCLK、HCLK、PLCK。FCLK用于CPU核,HCLK用于AHB总线的设备(比如SDRAM),PCLK用于APB总线的设备(比如UART)[4]。*/ strr2,[r1,#oLOCKTIME]/*初始化系统时钟*/ mvnr2,#0xff000000 movr1,#CLK_CTL_BASE/*CLKDIVN用来设置FCLK:HCLK:PCLK的比例关系,这里 movr2,#0x3/*设置为0x03,即FCLK:HCLK:PCLK=1:2:4*/strr2,[r1,#oCLKDIVN] mrcp15,0,r1,c1,c0,0/*读控制寄存器的值到r1*/ orrr1,r1,#0xc0000000/*将r1最高两位置1*/ mcrp15,0,r1,c1,c0,0/*将r1的值回写控制寄存器*/ movr1,#CLK_CTL_BASE ldrr2,mpll_200mhz strr2,[r1,#oMPLLCON]/*CPU的频率是200MHz*/ /*对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为MPLL。(FCLK)=(m*Fin)/(p*2^s)其中:m=MDIV+8,p=PDIV+2。Fin即默认输入的时钟频率12MHz[4]*/ …… blmemsetup/*跳转到memsetup函数*/ ENTRY(memsetup)/*memsetup函数实现,初始化内存控制寄存器 湖南工学院计算机科学系常赟杰罗丹霞 [摘要]Bootloader是系统加电后运行的第一段代码,其作用是对操作系统的引导和加载内核。设计一个功能强大的Bootloader比较困难,但是研究通用开源代码的Bootloader有着十分重要的借鉴意义。本文着重介绍了启动程序对硬件的初始化过程,并结合代码详细分析介绍了Vivi的启动流程。 [关键词]Vivi启动引导程序ARM

STM32_bootloader流程解析

STM32 bootloader 流程解析 stm32在内部有硬件bootloader,但那是在生产烧录时使用的。 stm32在flash开头的4K程序空间是可以设置成防擦除的,是天生的bootloader存放区。 我的思想是在开头的4K空间(0x8000000-0x8000FFF)放置一个独立程序,主程序编译地址 放在0x8001000后端。 上电时,bootloader程序先判断是否满足升级条件(IO电平判断),然后选择是循环等待 升级,或者直接跳转到主程序区。 现给出流程如下,大家可交流探讨。 BOOT流程.JPG (11.13 KB) boot程序流程 程序流程详细.JPG (35.79 KB) 程序详细流程

说的太过简单笼统了, 经过一个来星期的调试,总算完成了这个东西.. 调试中发现一个问题,在已经开始执行BOOT程序之后,再进行切换到应用程序时,有些中断会出问题. 而在BOOT启动时(所有软硬件操作都没开始)马上切换到应用程序就不会出问题.

一直没找到怎么解决. 最后只好把IAP程序和应用程序合在一起使用,在上电后的短时间内执行IAP,无响应就进入应用程序,没有所谓的BOOT区了. stm32在flash开头的4K程序空间是可以设置成防擦除的,是天生的bootloader存放区。我的思想是在开头的4K空间(0x8000000-0x8000FFF)放置一个独立程序,主程序编译地址放在0x8001000后端。 楼主所说的stm32在flash开头的4K程序空间是可以设置成防擦除的,如何设置?而且主程序编译地址放在0x8001000后端,如何实现啊,望指教!!! 我最近也在做类似的事情,但是在跳转到功能程序出问题了。功能程序应该如何设置? 如果一个程序不从默认的Flash地址开始存储,需要设置哪些东西,在Keil 开发环境中。谢谢!

bootloader概述

对于嵌入式系统工作过一段时间的人来说,Bootloader应该是一个不算陌生的概念。 对于linux的系统来说,从软件角度通常分成4层: ●Bootloader ●Linux内核 ●文件系统 ●用户应用程序 1. Bootloader概念 Bootloader是处理从系统开机初始化硬件开始直到启动操作系统内核这段过程的一段程序,是系统中最早运行的程序,和硬件有很大的相关性。 从CPU的角度来说,上电后并非立刻就可以运行操作系统的,系统的相关硬件必须初始化(CPU工作模式的设置,内存初始化,关中断,关闭MMU/Cache等等等等),根据当前硬件条件判定当前是启动模式还是下载模式,然后走相应的功能分支。 所以一句话概括bootloader的工作——初始化系统的软硬件环境,使之满足操作系统的运行条件。当把一切软硬件环境都配置好之后,系统的

控制权才会交给OS的内核,这个时候Bootloader就功成身退了。 由于涉及到针对底层硬件的操作,所以很多代码会用汇编语言编写,也需要对系统硬件的工作有一个正确的完整的认识,这样对工程师的要求自然就高了。同时,Bootloader的可移植性就不会太好。 Bootloader有单阶段的也有多阶段的,目前比较多的是采用2个阶段,stage1和stage2。stage1一般都由汇编码编写,stage2一般由C语言编写. 大多数Bootloader都包含两种不同的操作模式: ●启动加载模式:这种模式也称自主模式,在这个模式下bootloader 从某个存储设备上加载操作系统到RAM,用户不需要介入这个过程 ●下载模式:这种模式下bootloader将通过串口/USB/网络等等手段从主机下载文件,写入存储设备。 现在的Android智能机就是一个很典型的例子,平时开机用户不需要介入,属于正常的启动加载模式,当用户刷机时显然就变成了下载模式。 2. 常见的Bootloader ●U-boot 这是现在使用最多的bootloader之一,是sourceforge上的一个开源项

U-Boot启动过程--详细版的完全分析

(一)U-Boot启动过程--详细版的完全分析 我们知道,bootloader是系统上电后最初加载运行的代码。它提供了处理器上电复位后最开始需要执行的初始化代码。 在PC机上引导程序一般由BIOS开始执行,然后读取硬盘中位于MBR(Main Boot Record,主引导记录)中的Bootloader(例如LILO或GRUB),并进一步引导操作系统的启动。 然而在嵌入式系统中通常没有像BIOS那样的固件程序,因此整个系统的加载启动就完全由bootloader来完成。它主要的功能是加载与引导内核映像 一个嵌入式的存储设备通过通常包括四个分区: 第一分区:存放的当然是u-boot 第二个分区:存放着u-boot要传给系统内核的参数 第三个分区:是系统内核(kernel) 第四个分区:则是根文件系统 如下图所示: u-boot是一种普遍用于嵌入式系统中的Bootloader。 Bootloader介绍 Bootloader是进行嵌入式开发必然会接触的一个概念,它是嵌入式学院<嵌入式工程师职业培训班>二期课程中嵌入式linux系统开发方面的重要内容。本篇文章主要讲解Bootloader 的基本概念以及内部原理,这部分内容的掌握将对嵌入式linux系统开发的学习非常有帮助!Bootloader的定义:Bootloader是在操作系统运行之前执行的一小段程序,通过这一小段程序,我们可以初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。意思就是说如果我们要想让一个操作系统在我们的板子上运转起来,我们就必须首先对我们的板子进行一些基本配置和初始化,然后才可以将操作系统引导进来运行。具体在Bootloader中完成了哪些操作我们会在后面分析到,这里我们先来回忆一下PC的体系结构:PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OS Boot Loader(比如LILO和GRUB等)一起组成的,BIOS在完成硬件检测和资源分配后,将硬盘MBR中的Boot Loader读到系统的RAM中,然后将控制权交给OS Boot Loader。Boot Loader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入

bootloader启动流程

MSM8909+Android5.1.1启动流程概述 PBL:APPS PBL(ApplicationPrimary Boot Loader),主引导加载程序 RPM:ResourcePower Manager,资源电源管理器 RPM(ResourcePower Manager)是高通MSM平台另外加的一块芯片,虽然与AP芯片打包在一起,但其是一个独立的ARM Core。之所以加这个东西,就是要控制整个电源相关的shared resources,比如ldo,clock。负责与SMP,MPM交互进入睡眠或者唤醒整个系统。 L2 TCM:Tightly-CoupledMemory,紧耦合内存 Some ARM SoC:s have a so-called TCM(Tightly-Coupled Memory). This is usually just a few (4-64) KiB of RAM insidethe ARM processor. Due to being embedded inside the CPU TheTCM has a Harvard-architecture, so there is an ITCM (instruction TCM) and aDTCM (data TCM). The DTCM can not contain any instructions, but the ITCM canactually contain data. CDT: Configuration Data Table,包含CDB0: platform info信息和CDB1: DDR配置参数。TZ: PIL:Peripheralimage loader MBA:Modem Boot Authenticator,调制解调器引导认证 HLOS:High-leveloperation system,高级操作系统 Pronto image: SMEM : shared memory RPC : remote procedure call QCSBL : qualcomm second bootloader OEMSBL : oem second bootloader AMSS : Advanced Mobile Subscriber Software SDI : System Debug Image QSEE : Qualcomm Secure Execution Environment TZBSP : TrustZone BSP SBL1:ScondaryBoot Loader Stage1 MSS:MobileSubscriber Software移动用户软件 在ARM的集成开发环境中,只读的代码段和常量被称作RO段(ReadOnly);可读写的全局变量和静态变量被称作RW段(ReadWrite);RW段中要被初始化为零的变量被称为ZI段(ZeroInit)

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