uClinux 启动顺序研究.docx
- 文档编号:15456446
- 上传时间:2023-07-04
- 格式:DOCX
- 页数:29
- 大小:97.89KB
uClinux 启动顺序研究.docx
《uClinux 启动顺序研究.docx》由会员分享,可在线阅读,更多相关《uClinux 启动顺序研究.docx(29页珍藏版)》请在冰点文库上搜索。
uClinux启动顺序研究
uClinux启动顺序研究
小组成员:
陈伟静1061000255
陈晓1061000256
关然1061000260
侯雪峰1061000261
摘要:
本文主要介绍了嵌入式系统uClinux的启动过程分析,并对相关的文件进行了注释说明,便于学习者更轻松容易的了解uClinux的启动过程和掌握改写的方法。
关键词:
uClinux;启动顺序;kernel;start_kernel
【Abstract】:
ThispaperintroducesuClinuxembeddedsystemsstartupprocessanalysis,anddocumentsrelatedtotheexplanatorynotestofacilitatelearnersunderstandingeasieruClinuxstartupprocessandtomasterthemethodofrewriting.
【keyword】:
uClinux;Bootsequence;kernel;start_kernel
引言:
32位ARM嵌入式处理器具有高性能、低功耗、高性价比的特性,已被广泛应用于消费电子产品、无线通信、控制和网络通信等领域。
uClinux是专门为无MMU处理器设计的嵌入式操作系统,已支持ARM、Motorola等微处理器。
目前采用ARM+uClinux作为嵌入式系统的一种开发模式非常普遍。
一个基于uClinux的完整的嵌入式系统由三个部分组成,即系统引导程序Bootloader、uClinux操作系统内核和文件系统。
嵌入式系统的启动引导技术是嵌入式系统开发的一个难点,系统启动引导的成功与否决定了应用程序的运行环境是否能正确建立,系统启动成功是应用正确运行的前提。
而uClinux内核的启动过程也是其中重要一环,在不同的嵌入式系统中,即使处理器相同,uClinux的启动控制部分都会完全不同,所以分析uClinux的启动过程,可以加快系统启动速度、正确建立应用环境。
正文:
uClinux启动之前的程序称作引导程序,它在整个系统中代码量较小,但是作用较大。
该引导程序包括两个方面;引导内核的Bootloader和内核自身的引导程序部分。
Bootloader是CPU开机后执行的第一个程序,它的任务就是将内核(压缩或是非压缩)装载到内核要求的地址。
内核引导程序部分有狭义和广义之分,狭义指内核运行前的一段代码,一般在压缩的内核映像前有一段解压缩程序,负责将内核解压缩到某个地址,从开始解压缩到将控制权交给解压缩好的内核,这一段代码称为内核的引导部分。
广义定义还要包括内核初始化部分,即直到有进程产生才算引导程序结束。
可将uClinux系统内核启动过程总结为一下几个阶段:
(1)指向复位地址入口处的Bootloader代码。
Bootloader完成一些基本的初始化,将系统的软硬件环境带到一个合适的状态;
(2)Bootloader将控制权交给操作系统内核的引导程序后,开始uClinux内核的加载;(3)uClinux内核加载引导完成,启动init进程,完成系统的引导过程。
下图为一个简单的内核启动过程图,主要分为bootloader和内核两部分。
内核启动过程图
●uClinux启动过程
嵌入式设备上电启动后,Flash中的程序获得控制权,Flash中的程序包括bootloader和一个压缩过的内核(另外还含有一个romfs的文件系统)。
Flash中的程序首先配置内存地址,将本身由原来系统默认的地址配置为0x0到0x200000(2MByte),SDRAM配置为0xl000000(16MByte)到0x2000000(32MByte),然后把Flash的内容整体拷贝SDRAM中。
拷贝完后,将SDRAM地址重新配置为0x0到0xl000000,F!
ash的地址改为0xl000000到0xl200000。
注意,这里Flash和RAM地址是同时修改的,修改地址后,接下来的指令就将从SDRAM中获得,由于SDRAM中的程序和原来Flash中的程序一样,并且地址也一样,所以不会影响指令的执行。
这里要说明一点的是,很多NOMMU的处理器上电后都是从地址0X0读程序并开始执行的,这是Flash地址,但处理器的中断向量地址表一般也都会放在0x0地址开始的RAM中,所以NOMMU的处理器一般都要经过上述的初始化过程,才可以使用自己编写的中断程序。
而MMU的处理器可以通过MMU将物理地址中的0X0地址换成逻辑的非0X0地址。
完成上面的工作后,SDRAM中的程序就解压缩内核到0x8000地址,最后跳到该地址执行。
当Bootloader将控制权交给内核的引导程序时,第一个执行的程序就是head.S,它完成了加载内核的大部分工作,初始完硬件相关的系统配置后,就把内核解压缩到一具体地址,完成在head-armv.S中设置好系统堆栈等参数后,系统跳转到解压缩后的内核地址,将不再返回,程序实际执行的是uClinux/init/main.c文件中的start_kernel函数,负责内核初始化部分;misc.c则提供加载内核所需要的子程序,其中解压内核的子程序是head.S调用的重要程序,另外内核的加载还须知道系统的硬件信息,该硬件信息在hardware.h中定义并被head.S所引用。
●head.S的流程及流程图:
(1)切换模式,关闭中断.
(2)首先程序要先给SYSCFG,EXTDBWTH,ROMCON0等一系列系统控制寄存器赋值,此时flash地址在0X0,DRAM地址在0X1000000.
(3)点亮I/O口的指示灯.
(4)把在flash上的image复制到DRAM上.
(5)执行remap,把flash地址映射为0X1000000,DRAM地址映射为0.
(6)打开cache和writebuffer.
(7)设置好64K堆栈.
(8)跳转到decompress_kernel函数,此处的跳转为带返回的跳转,以便于执行完此函数跳转回来.
●head.S的具体代码及注释:
head.S在uClinux-2.4.x\arch\armnommu\boot\compressed下,其源代码如下(删去部分无关代码):
(1)切换模式,关闭中断.
.section".start",#alloc,#execinstr//本段allocatable和executabl
/*
*sortoutdifferentcallingconventions
*/
.align
//程序的开始,由start标号标志
start:
.typestart,#function//指定start符号是函数类型符号
.rept8//延时,该指令共出现8次
movr0,r0
.endr//相当于8条空指令
b1f//跳转到1f
.word0x016f2818//Magicnumberstohelptheloader
.wordstart//完整的zImage运行和载入的地址
.word_edata//zImage底端位置
1:
movr7,r1//保存architectureID
movr8,#0//保存r0
#ifndef__ARM_ARCH_2__
//r1和r0中分别存放着由bootloader传递过来的architectureID和指向标记列表的指针。
//这里将这两个参数先保存
//读取状态寄存器cpsr并判断是否处理器处于supervisor模式——从u-boot进入kernel,系统已经处//于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。
之后是再次确认中断关闭,并完成cpsr写入
//Angel是ARM的调试协议,MULTI-ICE用的是RDI通讯协议,ANGLE需要在板子上有驻留程序,然后通过串口
//就可以调试了。
/*
*BootingfromAngel-needtoenterSVCmodeanddisable
*FIQs/IRQs(numericdefinitionsfromangelarm.hsource).
*Weonlydothisifwewereinusermodeonentry.
*/
mrsr0,cpsr//读取寄存器cpsr得到当前状态
tstr0,#3//测试T标志位
bnenot_angel//not_angel继续循环
movr0,#0x17//设置了功能号为0x17
swi0x123456//调用0x123456软中断,处理器进入管理模式
mrsr0,cpsr//读取状态寄存器cpsr到r0
orrr0,r0,#0xc0//组织angel运行
msrcpsr_c,r0//写入状态寄存器cpsr
#else
teqppc,#0x0c000003//关闭中断,将模式改变为0x0c000003对应的模式
#endif
/*
*Notethatsomecacheflushingandotherstuffmay
*beneededhere-isthereanAngelSWIcallforthis?
*/
/*
*somearchitecturespecificcodecanbeinserted
*bythelinkerhere,butitshouldpreserver7andr8.
*/
//链接器会把一些处理器相关的代码链接到这个位置,也就是arch/arm/boot/compressed/head-armv.S
//文件中的代码。
在那个文件里会对I/Dcache进行一些操作
.text
#ifdefCONFIG_ARCH_SAMSUNG
(2)首先程序要先给SYSCFG,EXTDBWTH,ROMCON0等一系列系统控制寄存器赋值,此时flash地址在0X0,DRAM地址在0X1000000.
/*setsystemparameters*///配置系统寄存器
ldrr0,=SYSCFG//将=SYSCFG数据中的地址放到r0中
ldrr1,=rSYSCFG//将=rSYSCFG数据中的地址放到r1中
strr1,[r0]//将r1中的数据保存到r0指向的地址中
/*setresetmemorymap*///修改默认的Flash和SDRAM地址
adrr0,SDRAM_SYSINIT_RESET//将SDRAM_SYSINIT_RESET地址放到r0
ldmiar0,{r1-r12}//将r0指向的多字数据加载到r1-r12上
ldrr0,=SYS_INIT_BASE//将=SYS_INIT_BASE数据中的地址放到r0中
stmiar0,{r1-r12}//将r1-r12上的数据存储在r0指向的地址上
//修改完成
(3)点亮I/O口的指示灯.
/*lightled*///使LED灯亮
ldrr0,=IOPMOD//将=IOPMOD数据中的地址放到r0中ldrr1,=0xFF//将=0xFF数据中的地址放到r1中
strr1,[r0]//将r1中的数据保存到r0指向的地址中
ldrr0,=IOPDATA//将=IOPDATA数据中的地址放到r0中
ldrr1,=0xEF//将=0xEF数据中的地址放到r1中
strr1,[r0]//将r1中的数据保存到r0指向的地址中
(4)把在flash上的image复制到DRAM上.
/*copyimagetoram*///从Flash拷贝代码到RAM中
ldrr0,=0x0//将=0x0数据中的地址放到r0中
ldrr1,=0x200000//将=0x200000数据中的地址放到r1中
ldrr2,=0x1000000//将=0x100000数据中的地址放到r2中
rom2ram_copy_loop:
ldrr3,[r0],#4//将r0数据中的地址+4后的数据放到r3中
strr3,[r2],#4//将r3中的数据放到r2数据中的地址+4中
subsr1,r1,#4//r1=r1-4
bnerom2ram_copy_loop//循环rom2ram_copy_loop
(5)执行remap,把flash地址映射为0X1000000,DRAM地址映射为0.
/*setbootmemroymap*///重新配置内存将RAM的起始地址改为0
adrr0,SDRAM_SYSINIT_BOOT//将SDRAM_SYSINIT_BOOT地址放到r0
//Flash的起始地址改为0X1000000(16M)
ldmiar0,{r1-r12}//将r0指向的多字数据加载到r1-r12上
ldrr0,=SYS_INIT_BASE//将=SYS_INIT_BASE数据中的地址放到r0中
stmiar0,{r1-r12}//将r1-r12上的数据存储在r0指向的地址上
//修改地址完成
#endif
1:
adrr2,LC0//将SDRAM_SYSINIT_BOOT地址放到r0//LC0本身是以0为基址的偏移,而adr的基//于pc寻址使得r2是相对于0x30008000的
ldmiar2,{r2,r3,r4,r5,sp}//将r2指向的多字数据加载到r2-sp上
movr0,#0//r0=0;
//上面这几行代码用于判断代码是否已经重定位到内存中通过当前运行时LC0的地址与链接器所链接的地
//址进行比较判断。
若相等则是运行在链接的地址上。
如果不是运行在链接的地址上,则下面的代码必须//进行重定位
//如果运行当前运行地址和链接地址相等,则不需进行重定位。
直接清除bss段
1:
//清除bss
strr0,[r2],#4//将r0中的数据放到r2数据中的地址+4中
strr0,[r2],#4//将r0中的数据放到r2数据中的地址+4中
strr0,[r2],#4//将r0中的数据放到r2数据中的地址+4中
strr0,[r2],#4//将r0中的数据放到r2数据中的地址+4中
cmpr2,r3//比较r2,r3
blt1b//跳转到1b
(6)打开cache和writebuffer.
#ifdefCONFIG_CPU_WITH_CACHE
#ifndefCONFIG_ARCH_SAMSUNG
#ifndefCONFIG_ARCH_S3C44B0
mrcp15,0,r6,c0,c0//获取processorID
blcache_on//跳转到cache_on
#endif
#endif
#ifdefCONFIG_ARCH_SAMSUNG
/*cache/writebufferon*///打开内部cache
ldrr0,=SYSCFG//将=SYSCFG数据中的地址放到r0中
ldrr2,[r0]//将r0数据中的地址放到r0中
orrr2,r2,#6//r2与r2+6或
strr2,[r0]//将r2中的数据放到r0数据中的地址中
#endif
#endif
(7)设置好64K堆栈.
movr1,sp//解压后起始地址设为malloc空间的结束地址addr2,sp,#0x10000//64kmax
#ifndefCONFIG_ARCH_SAMSUNG
teqr4,r5//r4是否等于r5,是否需要内核移动moveqr5,r2//相等则r2赋给r5,解压Imagemovner5,r4//不相等则r4赋给r5,解压到最后地址
#endif
#ifdef
CONFIG_ARCH_SAMSUNG//decompressafterimage
(8)跳转到decompress_kernel函数,此处的跳转为带返回的跳转,以便于执行完此函数跳转回来.
movr5,r2//将r5赋值给r0
#endif
movr0,r5//将r5赋值给r0
movr3,r7//将r7(archid)赋值给r3
blSYMBOL_NAME(decompress_kernel)//跳转到misc.c中的解压缩函数
teqr4,r5//r4是否等于r5
//其中寄存器r4和r5中存放的地址分别是内核运行地址和解压缩后的内核地址,通过比较这两个
//地址,可以知道是不是还需要进行内核的移动过程。
beqcall_kernel//跳转到kernel
//不需要移动内核,转到cal一kemel执行
addr0,r0,#127//r0加127后再付给r0
//开始准备移动内核
bicr0,r0,#127//匹配kernel长度
//到这里为止内核解压缩已完成,这时解压后的各寄存器中的值如下:
/*
*r0=解压后内核长度
*r1-r3=无用
*r4=内核执行地址
*r5=解压内核开始指令地址
*r6=processorID
*r7=architectureID
*r8-r14=无用
*/
//这里首先计算出解压后内核的大小,然后对把relocationcode复制到解压后内核的后面
//重定位结束后(把内核代码拷贝到0x80008000处)跳到解压后内核的起始处开始执行,在运行解压后内//核之前,先调用了cache_clean_flush这个函数。
call_kernel:
#ifdefCONFIG_CPU_WITH_CACHE
#ifndefCONFIG_ARCH_S3C44B0
blcache_clean_flush//跳转到清除cache
blcache_off//跳转到关闭cache
#endif
#endif
movr0,#0
movr1,r7//存储architecturenumber
movpc,r4//callkernel跳转到0x80008000解压后内核处
//最后程序把r4的值直接放到了Pc中,也就意味着程序不会再返回。
由于r4中放的是内核解压缩后的
//运行地址,所以接下来将进入内核初始化工作。
实际上,r4中存放的地址就是文件
//arch/armnommu/kernel/head-armv.s的地址,该文件是所有处理器架构中32位内核的入口点,主要是根//据具体的处理器型号完成堆栈设置,初始化BSS(清零),设置处理器类型等参数,然后跳转到具体理
//器型号的start-kemel处执行,从而完成将控制权交给了解压后的内核初始化程序。
最后程序把r4的值直接放到了pc中,也就意味着程序不会再返回。
由于r4中放的是内核解压缩后的运行地址,所以接下来将进入内核初始化工作。
实际上,r4中存放的地址就是文件arch/armnommu/kernel/head-armv.s的地址,该文件是所有处理器架构中32位内核的入口点,主要是根据具体的处理器型号完成堆栈设置,初始化BSS(清零),设置处理器类型等参数,然后跳转到具体处理器型号的start_kemel处执行,从而完成将控制权交给了解压后的内核初始化程序。
●内核的解压缩:
为了节省ROM空间,内核都是压缩过的,因此要有专门的解压缩程序解压缩内核。
真正完成这个功能的函数是decompress_kernel(),该函数在文件misc.c中定义,其中misc.c在目录uClinux-2.4.x\arch\armnommu\boot\compressed中,代码注释如下:
decompress_kernel(ulgoutput_start,ulgfree_mem_ptr_p,ulgfree_mem_ptr_end_p,
intarch_id)
{
output_data=(uch*)output_start;/*Pointstokernelstart*/
free_mem_ptr=free_mem_ptr_p;
free_mem_ptr_end=free_mem_ptr_end_p;
__machine_arch_type=arch_id;//变量赋值变量
proc_decomp_setup();//在include/asm-arm/proc-armv/uncompress.h文件中,主要刷新并起用cache,锁住交换缓存,这是一段嵌入的arm汇编代码
arch_decomp_setup();//它在include/asm-arm/arch-pxa/uncompress.h文件中,是一个空函数,用于扩展。
makecrc();//在lib/inflate.c中,主要将产生CRC-32table,进行循环冗余校验
puts("UncompressingLinux...");//输出
gunzip();//解压kernel,它也在lib/inflate.c中
puts("done,bootingthekernel.\n");//输出
returnoutput_ptr;//返回head.S
}
decompress_kernel解压缩函数过程
该函数设置了一些与解压缩有关的变量后,在解压缩之前显示字符串UncompressingLinux...,然后调用真正的解压缩函数gunzip()完成内核的解压缩,具体解压缩过程可以参考misc.c中的源代码。
●内核的初始化:
对拷贝到SDRAM的内核映像文件进行解压缩;最后跳转到执行调用内核函数call_kernel,调用call_kernel函数实际上是执行main.c中的start_kernel函数,该函数完成的功能包括处理器结构的初始化、中断的初始化、定时器的初始化、进程相关的初始化以及内存初始化等初始化工作完成内核初始化;最后内核创建一个init线程,在该线程中调用init进程,完成系统的启动。
main.c中的start_kernel函数主要完成初始化硬件设备并为程序的执行建立环境,列举功能如下:
◆setup_arch体系结构初始化,根据不同的体系结构进行不同的初始化;
◆parse_options分析内核命令行参数,uClinux启动时有时需命令行参数,这里将分析这些参数,以备将来使用;
◆trap_in
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- uClinux 启动顺序研究 启动 顺序 研究