上周日,我终于将Linux-<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2.6.12在我的ARM板上跑起来了!从硬件的调试开始,硬件测试程序的编写,loader程序编写测试,u-boot移植,最后移植Linux、文件系统,并且成功启动系统。整个过程一波三折,有BUG的折磨,有问题的困扰,当然也有很多成功的喜悦。开发过程中,遇到问题之后只能求助google,也从网上的论坛和大家的开发笔记中学到了很多东西,生在网络的时代真好!
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
Linux系统移植成功宣告ARM项目第二个阶段成功结束。接下来的工作将紧紧围绕设备驱动程序的开发、GUI移植及开发工作。
在这一阶段中,涉及到了很多程序,例如loader,boot,u-boot,busybox以及linux,这些程序都要求我们去修改、移植。说起loader程序需要考虑at91rm9200的体系结构。At91rm9200内部拥有一个启动的一级bootloader,在CPU启动过程中如果没扫描到可启动的介质(通常为外部Flash),那么CPU将控制权交给内部的bootloader,并且将低端1M内存映射给bootloader所在的ROM。一级bootloader所作的工作比较有限,其初始化底层硬件以及DEBUG端口(UART),并且启动xmodem协议提示用户下载程序,此时用户终端将会显示CCC字符。下载的程序将会load到9200内部的SRAM,该SRAM只有16K的空间,所以装载不了较大的程序。并且此时SDRAM还没有被初始化,所以只能加载小程序至内部的SRAM空间。而用于引导linux的u-boot通常体积很大,在100K以上的数量级,所以此时我们需要一个二级bootloader为u-boot程序的下载、运行创造运行环境,这个二级bootloader就是loader程序。Loader程序体积很小,其功能单一,与内部的bootloader相比,其增加了SDRAM的初始化过程。Loader程序可以通过一级bootloader下载到9200内部的SRAM,然后将CPU交给loader程序,loader程序初始化DEBUG端口、SDRAM之后同样启动xmodem协议,请求用户下载u-boot。Loader程序的开发可以基于at91rm9200内置的service固件,该固件程序采用面向对象的程序设计思想,可以很容易使用,具体内容可以参考at91rm9200提供的datasheet。
在loader程序中需要制定u-boot加载内存的地址,通常u-boot程序被加载到SDRAM的高端地址空间(如果为32MB内存,通常的加载地址为0x21f00000)。U-boot程序是在ppc-boot的基础上修改的,是一个非常优秀的开源项目(http://www.denx.de/wiki/U-Boot/WebHome),支持非常多的CPU系列,对at91rm9200具有非常好的支持。移植u-boot程序本身不是很难,其程序框架做的比较好,用户只需根据自己板子的情况选择相近的board源码,对其进行简单修改就可以了。通常主要修改点在于flash驱动、网卡驱动。因为用户设计的board可能采用了比较个性化的芯片,所以,只能自己写驱动了,常用芯片的驱动在u-boot中都能找到。另外,在编译u-boot的过程中需要采用合适的gcc编译器,我用的u-boot版本是1.3.4,采用arm-linux-gcc3.3.2编译器。U-boot功能是非常强大的,将u-boot启动之后就可以通过网络下载文件系统或者linux kernel了,并且可以通过nfs,tftp等服务与开发用机进行数据交互了,使得arm开发平台的档次向上跨越了一个层次。
u-boot程序通过loader程序下载到指定SDRAM地址后就可以运行了。系统正常工作时是需要从flash中启动u-boot的,所以下一步工作是需要将u-boot烧录到nor flash中。如果手头有H-JTAG之类的武器,那么可以直接通过H-JTAG将u-boot烧录到flash中,但是,简单的方法是通过u-boot烧录u-boot,即通过上述方法启动的u-boot将u-boot.gz文件下载到flash中。具体方法是通过u-boot的loadb命令启动kermit协议下载u-boot.gz文件到指定的内存单元,然后通过cp命令将内存中的u-boot.gz数据拷贝到flash。当然,写flash数据的时候需要off写保护,并且erase相应的sector。问题讨论到这儿就该boot程序隆重登场了。
Boot程序就是用来引导flash中的u-boot程序的。实际上该程序不是必然存在的,注意前面下载的u-boot程序是一个gzip文件,也就说明该u-boot是经过GNU ZIP压缩处理过的。此时,需要boot程序对其进行引导。如果用户将一个没有经过压缩的u-boot程序放置到flash的可启动位置,那么boot程序就可以下岗了。Boot程序做的工作比较简单,CPU上电初始化之后,首先执行boot程序,初始化SDRAM等lowlevel硬件,然后其将指定位置的u-boot.gz程序load到内存,并且进行gunzip操作,接着将CPU转交给解压之后的u-boot程序。需要注意,boot程序需要知道u-boot的flash存储地址和解压存放的SDRAM地址,这两个地址就是SRC、DST地址,在移植boot程序的时候需要修改这两个宏定义。Boot程序和loader程序在本站都可以下载J。
u-boot在arm中run之后,就需要开始移植linux操作系统了。Linux的操作系统源码可以从linux的开源站点上下载(http://www.kernel.org/),然后还需要下载at91rm9200的linux patch,为linux源码打一个补丁,这些操作都可以参考网上非常多的blog。打好补丁的Linux可以采用make menuconfig进行配置,配置选项中有一个cmdline,这个comdline指定了linux系统启动过程中需要的initrd地址,以及挂载点等信息。这个参数考虑到了linux自启动的情况,在自启动时,不存在bootloader,没有人给linux内核传递启动信息,所以这个启动命令需要在linux编译的时候指定。在拥有bootloader情况下,u-boot会通过环境变量给linux传递启动信息,所以会覆盖掉linux内核中的启动命令。Linux内核编译成功之后,在arch/arm/boot目录下会存在Image和zImage两个文件,其中Image是未经压缩的文件,zImage是经过压缩过的文件。如果通过H-JTAG将Image文件直接下载到flash中,linux应该就能够起来(本人没有尝试过,但是通过skyeye启动过该文件);如果采用u-boot启动linux内核,那么需要通过mkimage工具对linux进行压缩,形成u-boot可引导的uImage.img文件。Mkimage工具在u-boot的tools下,google一下就可以知道该工具使用方法了。Mkimage时需要注意两个老生常谈的参数,-a和-e,-a是程序装载内存地址,-e是程序执行入口地址。-a和-e参数意义与kernel的压缩类型也存在关系。当-a和-e相等时,说明程序装载和执行入口地址相同,-a、-e指定的地址处是一个未经压缩处理的kernel,可以直接启动。在这种情况下,说明下载的内核是经过压缩处理的,这里的压缩处理有两层含义,一层是用户通过gzip工具对编译生成的kernel进行压缩(mkimage命令中通过-C指定压缩类型),另一层含义是直接是一个zImage压缩内核。压缩处理的内核不能直接tftp到-a、-e指定的位置,需要与-a、-e的位置保持一定距离。当-a与-e不相等时(-e后移0x40字节),说明内核是一个未经压缩处理(同样是两种压缩方法)的内核,tftp下载的地址可以与-a、-e保持一致。
Linux编译成功之后的一个重要话题就是文件系统了,考虑到嵌入式系统资源的紧缺性,可以采用busybox来做文件系统。busybox是嵌入式linux的“瑞士军刀”,它是一个神奇的box,封装了用户所需的绝大部分command,这也是一个开源项目,源代码可以从网上获取(http://www.busybox.net/)。嵌入式linux文件系统可以通过initrd和ramdisk来实现,这两个是有点容易混淆的概念。Ramdisk是Linux中将内存虚拟化成块设备的驱动,initrd是利用了ramdisk机制的一种启动盘。所以,制作文件系统的关键在于制作initrd,并且将busybox中的命令工具和用户启动脚本、程序拷贝到initrd中。制作initrd可以通过mkfs.ext2命令对一个file进行格式化,接着通过loop设备将一个文件虚拟化成块设备,然后mount到挂载点,随后进行数据的拷贝。文件系统制作完成之后下载到内存或者flash,然后通过u-boot进行加载,并且通过环境变量将initrd的信息告诉给linux kernel。Kernel会将initrd加载到ramdisk中,并且挂载到具体的挂载点,启动指定的init启动程序(通常为linuxrc),至此linux就可以起来了。
文章评论(0条评论)
登录后参与讨论