当前位置: 首页 > 工具软件 > uclinux > 使用案例 >

[原创]uClinux中重要地址关系与uClinux移植关键点分析

冯驰
2023-12-01
 
由于参加的学校科研项目的需要,研究了uClinux系统在ARM7处理器上的移植,主要内容是uboot与uClinux的移植。虽然网上有许多这方面的研究文章,不少书籍也有详细介绍,但是对于一些对移植工作至关重要的地址和步骤并没有做出深入的分析,让人知其然不知其所然,对于移植过程中遇到的问题也难以理解。为此,作者通过对代码的详细阅读和具体的实验验证,得出了一些结论,希望对读者理解uClinux系统移植有所帮助。至于移植uboot与uClinux的具体步骤并不是这里的重点,如有需要可以阅读相关书籍和网页。
硬件平台:
ARM7:S3C44B0X
SDRAM:8M,映射在0x0c000000~0x0c07ffff,
FLASJ;Nor Flash 2M,映射在0x00000000~0x00001ffff;Nand flash 32M
软件平台:uboot-1.1.2,uClinux-20040408
开发平台:Windows XP,Cygwin
 
首先,结合详细的地址来说明系统的启动顺序。一旦对这个过程有了清晰的理解就对整个系统的运行有了充分的认识,光凭这点就可以很容易移植自己的uClinux系统。
 
1.系统上电,存放在起始物理地址0x00000000的uboot开始运行,这个阶段uboot
将设置cpu的一些运行的最基本参数,包括cpu的主频和中断向量。之后,将自己从nor flash复制到由TEXT_BASE定义的SDRAM起始地址上。
其中TEXT_BASE在u-boot-1.1.2/board/dz51/dz51_board/config.mk中定义,这里定义为 TEXT_BASE = 0x0C200000 ,这个地址已经是SDRAM的映射地址。 这个参数用于 uboot 编译 链接时定位代码运行位置 同时这个参数也是 uboot 复制到 SD RAM 中的起始位置。
复制完自身后uboot跳转到RAM中的uboot代码去运行,这个过程又称为重定位(relocate).
 
2.在uboot的shell界面下,如果输入启动内核的命令则uboot首先把启动参数写进SDRAM的特定地址中,具体的地址值由u-boot-1.1.2/lib_arm/armlinux.c文件中的setup_start_tag函数的
params = (struct tag *) bd->bi_boot_params;指定,然后把位于nor flash上的内核映象复制到SDRAM上。
内核映象在nor flash中的起始地址由u-boot-1.1.2/include/configs/figo.h(figo.h是作者自定义的配置头文件)中的 #define CONFIG_BOOTCOMMAND "bootm 0x50000" 语句进行定义 这里定义为 0x50000 所以烧写内核到 nor flash 中时也要采用这个地址值。内核映象被复制到SDRAM中的起始由u-boot-1.1.2/include/configs/dz51.h中的 CFG_LOAD_ADDR定义,这个地址应该和内核中的ZTEXTADDR值一致,这个宏在
uClinux-dist/linux-2.4.x/arch/armnommu/boot/makefile 中定义 ,这里定义为
ZRELADDR=0x0c008000。这个值的意义是告诉内核这个地址是压缩格式内核映象的起始 也是 head.S 代码的起始位置 ,head.S 和解压缩函数代码 ( misc.c ) 编译后的 运行地址应该以这个地址为起始地址。
注:压缩内核映象与非压缩映象的的区别:前者是由head.S+misc.c+非压缩内核编译链接并压缩而成,是为减少存放使用映象所使用的nor flash空间而采取的策略,并不能直接运行;后者是真正意义上的可执行内核映象。
3.uboot跳转到SDRAM中压缩格式内核的地址上执行,跳转的具体位置由zImage转为uImage时
mkimage -A arm -O linux -C gzip -a 0x20008000 -e 0x20008040 -d zImage uImage
的-e项决定,这个参数的意义是跳过了uboot加在zImage前面的64bytes后的位置,这个位置也是zImage的起始位置。这时uboot会把zImage的内容复制到-a指向的位置上,复制结束后将跳转到-a指向的位置执行。此后uboot将与内核完全没有关系,压缩格式内核映象正式接管cpu!
注:uImage与zImage的区别:前者是linux编译后默认的映象格式,不能直接被uboot直接引导;后者是使用uboot自带的mkimage工具将zImage格式映象进行转换后的结果,能被uboot引导,具体的转换命令是:
mkimage -A arch -O kernel -C zip-type -a adderss -e entry-address -d input-file output-file
4. 压缩内核的head.S部分首先被执行,这部分代码的主要作用是做一些cpu设置,接着运行紧贴在head.S后面的解压缩程序misc.c。解压缩程序把真正的内核代码部分从当前前存放在SDRAM的位置解压缩到内核的最终启动位置。
内核在当前SDRAM中的位置紧贴在misc.c代码的后面。内核的最终启动位置就是上面所提到 ZRELADDR 这个值是内核的最终运行位置。
解压缩过程结束后程序指针将跳转到内核的最终运行位置上继续执行。
 
5.至此,真正的内核已经接管了cpu,haed-armv.S程序开始运行,此后将执行start_kernel()等C语言程序,一系列的初始化函数将被执行。
 
6.在初始化的后期,将通过mount_root()函数建立根节点,并挂载根文件系统。挂载的过程简单介绍如下:
如果启动参数有定义initrd项并且initrd映象已经由uboot程序下载到RAM中的某个位置A,那么内核通过读取uboot传递过来的initrd项参数知道initrd位于A,接着在SDRAM中B开始的地方格式化一个RAMDISK,B的值和RAMDISK的大小由uClinux配置时进行定义,并将initrd映象从A复制到一个RAMDISK设备中,然后把它安装为临时的根文件系统,接着执行initrd文件系统下的linuxrc文件,此文件会一步一步告诉内核应该将哪一个快设备挂载为最终的文件系统,这个快设备应该已经存在了一个文件系统映象的。如果没有这个文件内核依然会经过initrd文件系统挂载真正的文件系统,当然大多情况下initrd就是最终的文件系统。
但是本项目的文件系统映象属于另一种情况,它的文件系统不是与内核映象分离的而是被安排在内核映象zImage的内部,也就是说zImage包括了内核和文件系统两部分。文件系统在zImage中的位置由uClinux-dist/linux-2.4.x/drivers/block/blkmem.c中的romfs_data定义。这个位置相当于上面情况的A。
          
7.挂接完文件系统后系统将开始一个用户线程,如执行上面提到的linuxrc脚本,最终系统进入shell界面,至此,系统完全启动了!
 
充分理解启动过程后,还要理解uboot与uClinux的链接关系。在移植过程中许多人都发现了这样一个问题。就是单独移植uboot或者单独移植uClinux都不难,但是要让uboot成功地引导uClinux却不是一件容易的事情,作者称之为uboot与uClinux的链接问题。在实现uboot与uclinux的链接过程中,有4个至为关键的问题必须解决:
1. 两者地址的分配与统一。
2. 两者串口速率、cpu频率的统一。
3. 启动参数的设置。
4. 中断向量的设置。
只要完成了这个4个方面的修改就可以成功把两者链接起来。下面逐一讲解这个4点的实现细节。
1.地址的分配与统一。
   上面已经仔细地分析了内核启动过程中的地址关系,并且也给出了各个地址定义所在的文件,只要做相应的修改即可。如果地址分配出现冲突那么会出现使用uboot下载内核时下载到一部分时死机、内核启动时输出乱码等情况。
2.        串口速率与cpu频率的统一。
内核的CPU频率在uClinux-dist/linux-2.4.x/.config中定义,uboot中的CPU定义在u-boot-1.1.2/cpu/s3c44b0/start.S中有定义,二者必须一致。如果2着之一不统一那么内核启动时输出乱码。
Uboot的串口频率设置在u-boot-1.1.2/cpu/s3c44b0/serial.c中,内核的串口频率在uClinux-dist/linux-2.4.x/drivers/char/serial_s3c44b0x.c中定义,二者必须一致。
3.        启动参数的设置。
启动参数究竟要怎样设置与内核的具体要求有关,不过可以把主要的项目都写上,有内核自己选择具体用哪些参数。Uboot中参数的设置在u-boot-1.1.2/lib_arm/armlinux.c中。这些启动参数在RAM中的位置又是怎么传递给内核的呢?这是通过cpu的寄存器来完成的,uboot在启动内核前一些寄存器要填写一定的值,其中就包括要把启动参数在RAM中的位置写进其中一个寄存器中。
4.        中断向量表的设置。
Uboot的u-boot-1.1.2/cpu/s3c44b0/start.S程序中首先会设置好中断向量表,中断向量表从RAM的起始位置开始。这段程序如果不正常的话那么内核启动到一定时候后死机。分析代码可知,内核启动到start_kernel函数的sti()子函数后就会死机,sti()是开中断函数,分析这个函数的代码可以判断是中断向量表的设置问题,根据S3C44B0X使用手册正确设置好后便可以正常启动了。
 类似资料: