全部博文(668)
分类:
2008-05-31 08:32:47
简述:针对“如何在以S3C44B0X为核心的ARMSYS开发板上建立uClinux内核移植”的一个总结,其内容包括对Bootloader的功能分析和uClinux2.4.24发行版内核基础上针对S3C44B0X开发板进行修改的重点内容的逐一列举。
2.Bootloader
2.1Bootloader概述
Boot Loader 就是在操作系统内核运行之前运行的一段程序。通过这段程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。因此,正确建立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的Reset Exception Vector处开始执行,因此需要在这里放置Bootloader的第一条指令:b ResetHandler,跳转到标号为ResetHandler处进行第一阶段的硬件初始化,主要内容为:关Watchdog Timer,关中断,初始化PLL和时钟,初始化存储器控制器。比较重要的是PLL的输出频率要计算正确,ARMSYS中把它设置为64MHz;这实际上就是处理器的工作主频,这个时间参数在第二阶段计算SDRAM的刷新计数值和UART的波特率等参数时还要用到。
2.3.2建立二级异常中断矢量表
异常中断矢量表(Exception Vector Table)是Bootloader与uClinux内核发生联系关键的地方之一。即使uClinux内核已经得到处理器的控制权运行,一旦发生中断,处理器还是会自动跳转到从0x0地址开始的第一级异常中断矢量表中的某个表项(依据于中断类型)处读取指令运行。
在编写 Bootloader时,地址0x0处的一级异常中断矢量表只需简单地包含向二级异常中断矢量表的跳转指令就可以。这样,就能够正确地将发生的事件交给 uClinux的中断处理程序来处理。对于uClinux内核,它在RAM空间中基地址为0xc000000处建立了自己的二级异常中断矢量表,因此, Bootloader的第一级异常中断矢量表如下所示:
b ResetHandler ;Reset Handler
ldr pc,=0x0c000004 ;Undefined Instruction Handler
ldr pc,=0x0c000008 ;Software Interrupt Handler
ldr pc,=0x0c00000c ;Prefetch Abort Handler
ldr pc,=0x0c000010 ;Data Abort Handler
b .
ldr pc,=0x0c000018 ;IRQ Handler
ldr pc,=0x0c00001c ;FIQ Handler
LTORG
如果在Bootloader执行的全过程中都不必响应中断,那么上面的设置已能满足要求。但在我们的ARMSYS上提供了USB下载器,需要用到中断,那么Bootloader必须在同样的地址(0xc000000)处配置自己的二级异常中断矢量表,以便同uClinux兼容。这张表事先存放在 Flash Memory里,引导过程中由Bootloader将其复制到RAM地址0x0C000000:
存放矢量表:
;IRQ ==the program put this phrase to 0xc000000
ExceptionHanlderBegin
b .
ldr pc, MyHandleUndef ; HandlerUndef
ldr pc, MyHandleSWI ; HandlerSWI
ldr pc, MyHandlePabort ; HandlerPabort
ldr pc, MyHandleDabort ; HandlerDAbort
b . ; HandlerReserved
ldr pc, MyHandleIRQ ; HandlerIRQ
ldr pc, MyHandleFIQ ; HandlerFIQ
MyHandleUndef DCD HandleUndef ;reserve a word(32bit)
MyHandleSWI DCD HandleSWI
MyHandlePabort DCD HandlePabort
MyHandleDabort DCD HandleDabort
MyHandleIRQ DCD HandleIRQ
MyHandleFIQ DCD HandleFIQ
ExceptionHanlderEnd
建立二级矢量表:
;****************************************************
;* Setup IRQ handler *
;****************************************************
ldr r0,=(_IRQ_BASEADDRESS + 0x100)
ldr r2,=_IRQ_BASEADDRESS
add r3,r0, #0x100
0
CMP r0, r3
STRCC r2, [r0], #4;cc:Carry clear;save R2 to R0 address, R0 =R0+ 4。
BCC %B0
ldr r1,=_IRQ_BASEADDRESS
ldr r0,=ExceptionHanlderBegin ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
ldr r3,=ExceptionHanlderEnd
0
CMP r0, r3 ;put the vector table at _IRQ_BASEADDRESS(0xc000000)
LDRCC r2, [r0], #4
STRCC r2, [r1], #4
BCC %B0
ldr r1,=DIsrIRQ;put the IRQ judge program at _IRQ_BASEADDRESS+0x80(0xc000080)
ldr r0,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
ldr r3,=IsrIRQEnd
0
CMP r0, r3
LDRCC r2, [r0], #4
STRCC r2, [r1], #4
BCC %B0
ldr r1, =MyHandleIRQ ;MyHandleIRQ point to DIsrIRQ
ldr r0, =ExceptionHanlderBegin
ldr r4, =_IRQ_BASEADDRESS;
sub r0, r1, r0
add r0, r0,r4
ldr r1, =DIsrIRQ
str r1, [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 */
#define pISR_RESET (*(unsigned *)(_IRQ_BASEADDRESS+0x0))
#define pISR_UNDEF (*(unsigned *)(_IRQ_BASEADDRESS+0x4))
#define pISR_SWI (*(unsigned *)(_IRQ_BASEADDRESS+0x8))
#define pISR_PABORT (*(unsigned *)(_IRQ_BASEADDRESS+0xc))
#define pISR_DABORT (*(unsigned *)(_IRQ_BASEADDRESS+0x10))
#define pISR_RESERVED (*(unsigned *)(_IRQ_BASEADDRESS+0x14))
#define pISR_IRQ (*(unsigned *)(_IRQ_BASEADDRESS+0x18))
#define pISR_FIQ (*(unsigned *)(_IRQ_BASEADDRESS+0x1c))
#define pISR_ADC (*(unsigned *)(_IRQ_BASEADDRESS+0x100))//0x20))
#define pISR_RTC (*(unsigned *)(_IRQ_BASEADDRESS+0x104))//0x24))
#define pISR_UTXD1 (*(unsigned *)(_IRQ_BASEADDRESS+0x108))//0x28))
#define pISR_UTXD0 (*(unsigned *)(_IRQ_BASEADDRESS+0x10c))//0x2c))
#define pISR_SIO (*(unsigned *)(_IRQ_BASEADDRESS+0x110))//0x30))
#define pISR_IIC (*(unsigned *)(_IRQ_BASEADDRESS+0x114))//0x34))
#define pISR_URXD1 (*(unsigned *)(_IRQ_BASEADDRESS+0x118))//0x38))
#define pISR_URXD0 (*(unsigned *)(_IRQ_BASEADDRESS+0x11c))//0x3c))
#define pISR_TIMER5 (*(unsigned *)(_IRQ_BASEADDRESS+0x120))//0x40))
#define pISR_TIMER4 (*(unsigned *)(_IRQ_BASEADDRESS+0x124))//0x44))
#define pISR_TIMER3 (*(unsigned *)(_IRQ_BASEADDRESS+0x128))//0x48))
#define pISR_TIMER2 (*(unsigned *)(_IRQ_BASEADDRESS+0x12c))//0x4c))
#define pISR_TIMER1 (*(unsigned *)(_IRQ_BASEADDRESS+0x130))//0x50))
#define pISR_TIMER0 (*(unsigned *)(_IRQ_BASEADDRESS+0x134))//0x54))
#define pISR_UERR01 (*(unsigned *)(_IRQ_BASEADDRESS+0x138))//0x58))
#define pISR_WDT (*(unsigned *)(_IRQ_BASEADDRESS+0x13c))//0x5c))
#define pISR_BDMA1 (*(unsigned *)(_IRQ_BASEADDRESS+0x140))//0x60))
#define pISR_BDMA0 (*(unsigned *)(_IRQ_BASEADDRESS+0x144))//0x64))
#define pISR_ZDMA1 (*(unsigned *)(_IRQ_BASEADDRESS+0x148))//0x68))
#define pISR_ZDMA0 (*(unsigned *)(_IRQ_BASEADDRESS+0x14c))//0x6c))
#define pISR_TICK (*(unsigned *)(_IRQ_BASEADDRESS+0x150))//0x70))
#define pISR_EINT4567 (*(unsigned *)(_IRQ_BASEADDRESS+0x154))//0x74))
#define pISR_EINT3 (*(unsigned *)(_IRQ_BASEADDRESS+0x158))//0x78))
#define pISR_EINT2 (*(unsigned *)(_IRQ_BASEADDRESS+0x15c))//0x7c))
#define pISR_EINT1 (*(unsigned *)(_IRQ_BASEADDRESS+0x160))//0x80))
#define pISR_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。
完成这个步骤后,第一阶段的硬件初始化就完成了。
BL Main
跳转到C语言程序,开始第二阶段的初始化和系统引导。
2.3.5 C语言中的硬件初始化
继续对硬件进行初始化,主要包括对以下设备的初始化:GPIO,Cache,Interrupt Controller,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 内核映像文件首先存放在Flash Memory中,由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系统的内核函数,其中最重要的文件当属sched.c;同样,和体系结构相关的代码在arch/*/kernel中;
◎ drivers: 放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录:如,/block 下为块设备驱动程序,比如ide(ide.c)。
◎ 其他:例如mm ,这个目录包括所有独立于处理器体系结构的内存管理代码,如页式存储管理内存的分配和释放等;lib放置核心的库代码;net,核心与网络相关的代码; ipc,这个目录包含核心的进程间通讯的代码;fs,所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录支持一个文件系统,例如fat和 ext2;Scripts,此目录包含用于配置核心的脚本文件等。
tar xvzf uClinux-ARMSYS-20040801.tar.gz
解压结束后会在/home/下生成uClinux-dist目录。
5.2安装补丁
到以下地址下载补丁文件:
解压后产生patch文件,安装patch文件:
patch –p1 < uClinux-20040408-ARMSYS.patch
安装过程中可能会出现一些错误信息,可以手动地按照patch文件的内容在指定的文件处进行修改一下。
6.配置与编译
6.1安装编译环境
到以下地址下载arm-elf工具链:
将arm-elf-tools-20030314.sh拷贝到根目录,运行安装:
sh arm-elf-tools-20030314.sh
6.2内核配置
下面就可以开始配置uClinux的内核和用户选项了。打开终端。
# cd /home/uClinux-dist
# make menuconfig
进入uClinux配置(uClinux v3.1.0 Configuration),选中“Kernel/Library/Defaults Selectionà”敲空格进入。其中有两个选项:定制内核设置和定制用户选项设置:
[*] Customize Kernel Settings
[ ] Customize Vendor/User Settings
选中定制内核设置选项,按下ESC键退出,在询问是否保存时,选择Yes并回车。
终端将首先进入内核配置选单。我们在配置uClinux内核时,就可以通过对这些选项的选择和取消选择来设定内核所具有的功能项。这也是裁减uClinux内核的基本方法。
每个选项都对应着一个宏定义,make menuconfig执行结束后,自动将配置结果保存为.config文件,将前一次的配置结果备份为.config.old文件。
读者可到 处下载内核配置文件(其中包括对网卡驱动的配置),读者可对照进行配置。
6.3交叉编译
按下面的步骤对uClinux源码包进行编译:
# make dep
# make clean (非必要)
# make lib_only
# make user_only
# make romfs
# make image
# make
初次移植时,在make lib_only到make这5步编译过程中很可能产生错误,无法继续下去。如果产生了错误,可以尝试根据报告的错误内容修改一下源程序,这一过程将有助于你熟悉uClinux内核源程序的结构,或者可以跟我们联系 Support@hzlitai.com.cn 。
交叉编译成功后,在 uClinux-dist/目录下产生images目录,其中包含的3个文件:image.ram, image.rom和romfs.img就是我们可以使用的二进制文件。参考《uClinux的移植包在ARMSYS上的使用说明》的方法,下载或烧录这些二进制文件,并启动运行uClinux。
7.启动信息
正确启动信息的例子如下:
Linux version 2.4.24-uc0 (root@localhost) (gcc
version 2.95.3 20010315 (release)(ColdFire patches - 20010318 from
.net/coldfire/)(uClinux XIP and shared lib patches from
)) #165 五 10月 8 20:04:10 CST 2004
Processor: Samsung S3C44B0X revision 0
Architecture: S3C44B0X
On node 0 totalpages: 2048
zone(0): 0 pages.
zone(1): 2048 pages.
zone(2): 0 pages.
Kernel command line: root=/dev/rom0 init=/linuxrc
Calibrating delay loop... 31.84 BogoMIPS
Memory: 8MB = 8MB total
Memory: 6592KB available (1270K code, 155K data, 40K init)
Dentry cache hash table entries: 1024 (order: 1, 8192 bytes)
Inode cache hash table entries: 512 (order: 0, 4096 bytes)
Mount cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer cache hash table entries: 1024 (order: 0, 4096 bytes)
Page-cache hash table entries: 2048 (order: 1, 8192 bytes)
POSIX conformance testing by UNIFIX
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Initializing RT netlink socket
Starting kswapd
ttyS0 at I/O 0x1d00000 (irq = 3) is a S3C44B0
ttyS1 at I/O 0x1d04000 (irq = 2) is a S3C44B0
ne.c:v1.10 9/23/94 Donald Becker (becker@scyld.com)
Last modified Nov 1, 2000 by Paul Gortmaker
NE*000 ethercard probe at 0x8000000: 00 00 e8 12 34 56
eth0: NE1000 found at 0x8000000, using IRQ 22
Blkmem copyright 1998,1999 D. Jeff Dionne
Blkmem copyright 1998 Kenneth Albanowski
Blkmem 1 disk images:
0: C400000-C47CBFF [VIRTUAL C400000-C47CBFF] (RO)
RAMDISK driver initialized: 16 RAM disks of 1024K size 1024 blocksize
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: IC
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 512 bind 512)
VFS: Mounted root (romfs filesystem) readonly.
Freeing init memory: 40K
Shell invoked to run file: /etc/rc
Command: hostname Samsung
Command: /bin/expand /etc/ramfs.img /dev/ram0
Command: mount -t proc proc /proc
Command: mount -t ext2 /dev/ram0 /var
Command: mkdir /var/config
Command: mkdir /var/tmp
Command: mkdir /var/log
Command: mkdir /var/run
Command: mkdir /var/lock
Command: cat /etc/motd
Welcome to
____ _ _
/ __| ||_|
_ _| | | | _ ____ _ _ _ _
| | | | | | || | _ \| | | |\ \/ /
| |_| | |__| || | | | | |_| |/ \
| ___\____|_||_|_| |_|\____|\_/\_/
| |
|_|
For further information check:
Command: ifconfig lo 127.0.0.1
Command: route add -net 127.0.0.0 netmask 255.255.255.0 lo
Command: ifconfig eth0 192.168.253.2 netmask 255.255.255.0 up
Execution Finished, Exiting
Sash command shell (version 1.1.1)
/>
出现以上信息后,可以尝试从键盘输入ls、 ping命令,来查看系统的运行情况。我们还建议读者按照uClinux-dist\ Documentation下的Adding-User-Apps-HOWTO文档编写一个简单的Helloworld应用程序,看是否能够正确运行。