ARM开发板上uClinux内核移植.docx
《ARM开发板上uClinux内核移植.docx》由会员分享,可在线阅读,更多相关《ARM开发板上uClinux内核移植.docx(13页珍藏版)》请在冰豆网上搜索。
ARM开发板上uClinux内核移植
ARM开发板上uClinux内核移植
简述:
针对“如何在以S3C44B0X为核心的ARMSYS开发板上建立uClinux内核移植”的一个总结,其内容包括对Bootloader的功能分析和uClinux2.4.24发行版内核基础上针对S3C44B0X开发板进行修改的重点内容的逐一列举。
2.Bootloader2.1Bootloader概述BootLoader就是在操作系统内核运行之前运行的一段程序。
通过这段程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系
简述:
针对“如何在以S3C44B0X为核心的ARMSYS开发板上建立uClinux内核移植”的一个总结,其内容包括对Bootloader的功能分析和uClinux2.4.24发行版内核基础上针对S3C44B0X开发板进行修改的重点内容的逐一列举。
2.Bootloader
2.1Bootloader概述
BootLoader
就是在操作系统内核运行之前运行的一段程序。
通过这段程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
因此,正确建立uClinux的移植的前提条件是具备一个与uClinux配套、易于使用的
Bootloader。
ARMSYS开发板提供了这样一个uClinux专用的Bootloader,该Bootloader程序烧录在系统的地址0x0处,每次上电即运行,能够正确完成硬件系统的初始化和uClinux的引导。
理论上,uClinux引导时并非一定需要一个独立于内核的Bootloader。
然而,将Bootloader与内核分开设计能够使软件架构更加清晰,也有助于灵活地支持多种引导方式,实现一些有用的辅助功能。
ARMSYS提供的Bootloader的主要任务可以概括如下:
1.硬件初始化;
2.从主机下载新的内核映像和文件系统映像;
3.烧写NorFlash和Nandflash;
4.加载uClinux内核映像并启动运行;
5.提供串行超级终端上的人机操作界面。
2.2存储空间分布
Bootloader采用默认的存储空间分布地址来加载uClinux内核、文件系统,并按照正确引导uClinux的运行。
在ARMSYS的Bootloader中,默认的存储空间分布如下表:
内容 起始地址
存储介质
Bootloader程序空间 0x00000000
Flash
压缩内核映像 0x00010000
Flash
ROM文件系统映像 0x000e0000
Flash
内核运行地址 0x0c008000
SDRAM
压缩内核解压地址 0x0c100000
SDRAM
文件系统加载 0x0c700000 SDRAM
这个存储空间的分配方式也不是固定不变的,可以通过修改Bootloader中的相关代码来改变。
2.3Bootloader的工作
完整的Bootloader引导流程可描述如下:
硬件初始化阶段一
◎硬件初始化
◎复制二级中断异常矢量表
◎初始化各种处理器模式
◎复制RO和RW,清零ZI
(跳转到C代码入口函数)
硬件初始化阶段二
◎初始化本阶段使用到的硬件设备;
◎建立人机界面
◎
实现映像文件的下载和烧录工具
◎实现映像文件的加载和运行工具
下面对上述各步骤进行逐一说明,并对与uClinux相关的内容详细加以说明。
2.3.1硬件初始化
板子上电或复位后,程序从位于地址0x0的ResetException
Vector处开始执行,因此需要在这里放置Bootloader的第一条指令:
b
ResetHandler,跳转到标号为ResetHandler处进行第一阶段的硬件初始化,主要内容为:
关Watchdog
Timer,关中断,初始化PLL和时钟,初始化存储器控制器。
比较重要的是PLL的输出频率要计算正确,ARMSYS中把它设置为64MHz;这实际上就是处理器的工作主频,这个时间参数在第二阶段计算SDRAM的刷新计数值和UART的波特率等参数时还要用到。
2.3.2建立二级异常中断矢量表
异常中断矢量表(ExceptionVector
Table)是Bootloader与uClinux内核发生联系关键的地方之一。
即使uClinux内核已经得到处理器的控制权运行,一旦发生中断,处理器还是会自动跳转到从0x0地址开始的第一级异常中断矢量表中的某个表项(依据于中断类型)处读取指令运行。
在编写Bootloader时,地址0x0处的一级异常中断矢量表只需简单地包含向二级异常中断矢量表的跳转指令就可以。
这样,就能够正确地将发生的事件交给
uClinux的中断处理程序来处理。
对于uClinux内核,它在RAM空间中基地址为0xc000000处建立了自己的二级异常中断矢量表,因此,
Bootloader的第一级异常中断矢量表如下所示:
bResetHandler;ResetHandler
ldr
pc,=0x0c000004;UndefinedInstructionHandler
ldrpc,=0x0c000008;Software
InterruptHandler
ldrpc,=0x0c00000c;PrefetchAbortHandler
ldr
pc,=0x0c000010;DataAbortHandler
b.
ldrpc,=0x0c000018;IRQ
Handler
ldrpc,=0x0c00001c;FIQHandler
LTORG
如果在Bootloader执行的全过程中都不必响应中断,那么上面的设置已能满足要求。
但在我们的
ARMSYS上提供了USB下载器,需要用到中断,那么Bootloader必须在同样的地址(0xc000000)处配置自己的二级异常中断矢量表,以便同uClinux兼容。
这张表事先存放在
FlashMemory里,引导过程中由Bootloader将其复制到RAM地址0x0C000000:
存放矢量表:
;IRQ==theprogramputthisphraseto0xc000000
ExceptionHanlderBegin
b.
ldrpc,MyHandleUndef;HandlerUndef
ldr
pc,MyHandleSWI;HandlerSWI
ldrpc,MyHandlePabort;HandlerPabort
ldr
pc,MyHandleDabort;HandlerDAbort
b.;HandlerReserved
ldrpc,
MyHandleIRQ;HandlerIRQ
ldrpc,MyHandleFIQ;HandlerFIQ
MyHandleUndefDCDHandleUndef;reserveaword(32bit)
MyHandleSWIDCD
HandleSWI
MyHandlePabortDCDHandlePabort
MyHandleDabortDCD
HandleDabort
MyHandleIRQDCDHandleIRQ
MyHandleFIQDCDHandleFIQ
ExceptionHanlderEnd
建立二级矢量表:
;****************************************************
;*SetupIRQhandler
*
;****************************************************
ldr
r0,=(_IRQ_BASEADDRESS+0x100)
ldrr2,=_IRQ_BASEADDRESS
addr3,r0,
#0x100
0
CMPr0,r3
STRCCr2,[r0],#4;cc:
Carryclear;saveR2toR0
address,R0=R0+4。
BCC%B0
ldrr1,=_IRQ_BASEADDRESS
ldrr0,=ExceptionHanlderBegin;ifthereisn't
'subspc,lr,#4'at0x18,0x1c
ldrr3,=ExceptionHanlderEnd
0
CMPr0,r3
;putthevectortableat_IRQ_BASEADDRESS(0xc000000)
LDRCCr2,[r0],
#4
STRCCr2,[r1],#4
BCC%B0
ldrr1,=DIsrIRQ;puttheIRQjudgeprogramat
_IRQ_BASEADDRESS+0x80(0xc000080)
ldrr0,=IsrIRQ;ifthereisn't'subs
pc,lr,#4'at0x18,0x1c
ldrr3,=IsrIRQEnd
0
CMPr0,r3
LDRCCr2,
[r0],#4
STRCCr2,[r1],#4
BCC%B0
ldrr1,=MyHandleIRQ;MyHandleIRQpointtoDIsrIRQ
ldrr0,
=ExceptionHanlderBegin
ldrr4,=_IRQ_BASEADDRESS;
subr0,r1,r0
add
r0,r0,r4
ldrr1,=DIsrIRQ
strr1,[r0]
定义Handlexxx:
^(_IRQ_BASEADDRESS)
HandleReset#4
HandleUndef#4
HandleSWI#
4
HandlePabort#4
HandleDabort#4
HandleReserved#4
HandleIRQ#
4
HandleFIQ#4
^(_IRQ_BASEADDRESS+0x80)
DIsrIRQ#4
;IntVectorTable
^
(_IRQ_BASEADDRESS+0x100)
HandleADC#4
HandleRTC#4
HandleUTXD1#
4
HandleUTXD0#4
HandleSIO#4
HandleIIC#4
HandleURXD1#
4
HandleURXD0#4
HandleTIMER5#4
HandleTIMER4#4
HandleTIMER3#
4
HandleTIMER2#4
HandleTIMER1#4
HandleTIMER0#4
HandleUERR01#
4
HandleWDT#4
HandleBDMA1#4
HandleBDMA0#4
HandleZDMA1#
4
HandleZDMA0#4
HandleTICK#4
HandleEINT4567#4
HandleEINT3#
4
HandleEINT2#4
HandleEINT1#4
HandleEINT0#4
将异常中断矢量重构到SDRAM,这样的好处就是可以在其它的功能程序内对中断处理程序的地址任意赋值。
为此,我们在44b.h文件中定义:
/*ISR*/
#definepISR_RESET(*(unsigned
*)(_IRQ_BASEADDRESS+0x0))
#definepISR_UNDEF(*(unsigned
*)(_IRQ_BASEADDRESS+0x4))
#definepISR_SWI(*(unsigned
*)(_IRQ_BASEADDRESS+0x8))
#definepISR_PABORT(*(unsigned
*)(_IRQ_BASEADDRESS+0xc))
#definepISR_DABORT(*(unsigned
*)(_IRQ_BASEADDRESS+0x10))
#definepISR_RESERVED(*(unsigned
*)(_IRQ_BASEADDRESS+0x14))
#definepISR_IRQ(*(unsigned
*)(_IRQ_BASEADDRESS+0x18))
#definepISR_FIQ(*(unsigned
*)(_IRQ_BASEADDRESS+0x1c))
#definepISR_ADC(*(unsigned*)(_IRQ_BASEADDRESS+0x100))//0x20))
#define
pISR_RTC(*(unsigned*)(_IRQ_BASEADDRESS+0x104))//0x24))
#definepISR_UTXD1
(*(unsigned*)(_IRQ_BASEADDRESS+0x108))//0x28))
#definepISR_UTXD0
(*(unsigned*)(_IRQ_BASEADDRESS+0x10c))//0x2c))
#definepISR_SIO(*(unsigned
*)(_IRQ_BASEADDRESS+0x110))//0x30))
#definepISR_IIC(*(unsigned
*)(_IRQ_BASEADDRESS+0x114))//0x34))
#definepISR_URXD1(*(unsigned
*)(_IRQ_BASEADDRESS+0x118))//0x38))
#definepISR_URXD0(*(unsigned
*)(_IRQ_BASEADDRESS+0x11c))//0x3c))
#definepISR_TIMER5(*(unsigned
*)(_IRQ_BASEADDRESS+0x120))//0x40))
#definepISR_TIMER4(*(unsigned
*)(_IRQ_BASEADDRESS+0x124))//0x44))
#definepISR_TIMER3(*(unsigned
*)(_IRQ_BASEADDRESS+0x128))//0x48))
#definepISR_TIMER2(*(unsigned
*)(_IRQ_BASEADDRESS+0x12c))//0x4c))
#definepISR_TIMER1(*(unsigned
*)(_IRQ_BASEADDRESS+0x130))//0x50))
#definepISR_TIMER0(*(unsigned
*)(_IRQ_BASEADDRESS+0x134))//0x54))
#definepISR_UERR01(*(unsigned
*)(_IRQ_BASEADDRESS+0x138))//0x58))
#definepISR_WDT(*(unsigned
*)(_IRQ_BASEADDRESS+0x13c))//0x5c))
#definepISR_BDMA1(*(unsigned
*)(_IRQ_BASEADDRESS+0x140))//0x60))
#definepISR_BDMA0(*(unsigned
*)(_IRQ_BASEADDRESS+0x144))//0x64))
#definepISR_ZDMA1(*(unsigned
*)(_IRQ_BASEADDRESS+0x148))//0x68))
#definepISR_ZDMA0(*(unsigned
*)(_IRQ_BASEADDRESS+0x14c))//0x6c))
#definepISR_TICK(*(unsigned
*)(_IRQ_BASEADDRESS+0x150))//0x70))
#definepISR_EINT4567(*(unsigned
*)(_IRQ_BASEADDRESS+0x154))//0x74))
#definepISR_EINT3(*(unsigned
*)(_IRQ_BASEADDRESS+0x158))//0x78))
#definepISR_EINT2(*(unsigned
*)(_IRQ_BASEADDRESS+0x15c))//0x7c))
#definepISR_EINT1(*(unsigned
*)(_IRQ_BASEADDRESS+0x160))//0x80))
#definepISR_EINT0(*(unsigned
*)(_IRQ_BASEADDRESS+0x164))//0x84))
例如,我们要使用到Exint4567中断,定义好中断处理程序Meint4567Isr()后,仅需要一条语句:
pISR_EINT4567=(int)MEint4567Isr;
就能使中断发生后正确跳转到我们编写的处理程序上。
2.3.3初始化各种处理器模式
ARM7TDMI支持7种Operation
Mode:
User,FIQ,IRQ,Supervisor,Abort,System和Undefined。
Bootloader需要依次切换到每种模式,初始化其程序状态寄存器(SPSR)和堆栈指针(SP)。
2.3.4复制RO和RW,清零ZI
一个ARM由RO,RW和ZI三个段组成,其中RO为代码段,RW是已初始化的全局变量,ZI是未初始化的全局变量(对于GNU工具,对应的概念是
TEXT,DATA和BSS)。
Bootloader要将RW段复制到RAM中,并将ZI段清零。
编译器使用下列符号来记录各段的起始和结束地址:
|Image$$RO$$Base|:
RO段起始地址
|Image$$RO$$Limit|
:
RO段结束地址加1
|Image$$RW$$Base|:
RW段起始地址
|Image$$RW$$Limit|
:
ZI段结束地址加1
|Image$$ZI$$Base|:
ZI段起始地址
|Image$$ZI$$Limit|:
ZI段结束地址加1
需要注意的是,这些标号的值是根据链接器中设置的中ro-base和rw-base的设置来计算的,我们的Bootloader的对应设置是:
ro-base=
0xc000000,rw-base=0xc5f0000。
完成这个步骤后,第一阶段的硬件初始化就完成了。
BLMain
跳转到C语言程序,开始第二阶段的初始化和系统引导。
2.3.5C语言中的硬件初始化
继续对硬件进行初始化,主要包括对以下设备的初始化:
GPIO,Cache,InterruptController,Watchdog
Timer和UARTs。
S3C44B0X处理器内置data/instruction合一的8KB
Cache,且允许按地址范围设置两个Non-Cacheable区间。
合理的配置是打开对RAM区间的Cache,关闭对其它地址区间(非存储器设备,I/O设备
)的Cache。
所有硬件初始化完毕之后,开中断。
2.3.6建立人机界面
引导过程的最后一步是在串行终端上建立人机界面,并等待用户输入命令。
若接收到用户输入,则显示菜单模式或命令行模式的交互界面,等待用户进一步的命令。
这里就不对此详细讨论了。
2.4加载uClinux内核
ARMSYS
提供的Bootloader支持两种uClinux启动运行方式:
直接从SDRAM中的内核映像中运行;从flash将压缩格式的内核映像加载到
SDRAM,再从SDRAM运行。
前者需要利用Bootloader提供的对映像文件下载的工具;后者则需要利用Bootloader提供的
flash烧录工具进行烧录,然后再加载运行。
压缩格式的uClinux内核映像文件都是由开头的一段自解压代码和后面的压缩数据部分组成。
对于Kernel而言,由于是以压缩格式存放,因次只能以非XIP方式执行。
自解压类型的uClinux
内核映像文件首先存放在FlashMemory中,由Bootloader加载到SDRAM中的0xc100000地址处,然后将控制权交给它。
可执行的uClinux
Kernel将被解压到最终的执行空间,然后开始运行。
压缩格式Image所占据的临时SDRAM空间可在随后由uClinux回收利用。
可以从flash拷贝到SDRAM解压运行,自然同样也可以直接下载到SDRAM运行。
这对于调试内核都是非常方便的。
对于压缩格式的内核映像文件(image.rom和image.ram)都可以直接下载到SDRAM的特定地址处,并从该地址开始运行(参考2.2节)。
2.5调用Kernel
Bootloader调用uClinux内核的方法是直接跳转到Kernel的第一条指令处。
采用C语句:
((void
(*)(void))ram_addr)();
2.6工具
ARMSYS
的Bootloader在人机界面上提供了8个功能项目,其中包括支持从主机通过USB口下载文件到目标板的SDRAM和Nandflash上;用
SDRAM中的数据烧写Flash
Memory。
由于USB口下载速度快,利用这些功能项能够轻松地调试uClinux的内核(具体使用方法参考《uClinux移植包在ARMSYS上的使用说明》一文)。
对uClinux专用Bootloader的介绍到此,下面开始对uClinux的内核部分的移植进行说明。
3.uClinux2.4.24内核组成
◎arch:
arch目录下有多个子目录,它的每一个子目录都代表内核支持的一种CPU体系结构,每个子目录中又进一步分解为boot、mm、
kernel等子目录,分别包含与系统引导、内存管理、系统调用的进入和返回、终端处理以及其它内核中依赖于CPU和系统结构的底层代码。
与ARM处理器(不带有MMU)相关的代码放在目录arch/armnommu下,与S3C44B0X相关的代码则放在目录arch/armnommu/match-
S3C44B0X。
◎include:
include子目录包括编译核心所需要的大部分头文件。
与平台无关的头文件在
include/linux子目录下,与ARM处理器(不带MMU)相关的头文件在include/asm-armnommu子目录下,与
S3C44B0X相关的代码在include/asm-armnommu/arch-S3C44B0X目录下;
◎
init:
这个目录包含核心的初始化代码(注意:
不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的一个非常好的起点。
◎
kernel:
主要的核心代码,此目录下的文件实现了大多数linux