bootloader相关

2019-07-13 01:05发布

本章基于Intel公司的pxa270芯片分析U-Boot的工作机制,介绍了嵌入式Linux交叉开发工具ELDK(Embedded Linux Development Kit),说明配置安装U-Boot到特定主板、交叉编译调试Linux的方法,分析了U-Boot的构架。

目录

[隐藏]

BootLoad启动方式

Bootloader的主要功能是引导操作系统启动,开放源代码的BootLoader种类很多,常用的BootLoader的功能说明如表1所示。 表1 常用BootLoader的功能说明 Bootloader名 是否终端监视 描    述 x86 ARM PowerPC LILO 否 Linux磁盘引导程序 是 否 否 GRUB 否 GNU的LILO替代程序 是 否 否 Loadlin 否 从DOS引导Linux 是 否 否 ROLO 否 从ROM引导Linux而不需要BIOS 是 否 否 Etherboot 否 通过以太网卡启动Linux系统的固件 是 否 否 LinuxBIOS 否 完全替代BUIS的Linux引导程序 是 否 否 BLOB 否 LART等硬件平台的引导程序 否 是 否 U-boot 是 通用引导程序 是 是 是 RedBoot 是 基于eCos的引导程序 是 是 是

网络启动方式

这种方式开发板不需要配置较大的存储介质,跟无盘工作站有点类似。但是使用这种启动方式之前,需要把Bootloader安装到板上的EPROM或者Flash中。Bootloader通过以太网接口远程下载Linux内核映像或者文件系统。第4章介绍的交叉开发环境就是以网络启动方式建立的。这种方式对于嵌入式系统开发来说非常重要。 使用这种方式也有前提条件,就是目标板有串口、以太网接口或者其他连接方式。串口一般可以作为控制台,同时可以用来下载内核影像和RAMDISK文件系统。串口通信传输速率过低,不适合用来挂接NFS文件系统。所以以太网接口成为通用的互连设备,一般的开发板都可以配置10M以太网接口。 对于PDA等手持设备来说,以太网的RJ-45接口显得大了些,而USB接口,特别是USB的迷你接口,尺寸非常小。对于开发的嵌入式系统,可以把USB接口虚拟成以太网接口来通讯。这种方式在开发主机和开发板两端都需要驱动程序。 另外,还要在服务器上配置启动相关网络服务。Bootloader下载文件一般都使用TFTP网络协议,还可以通过DHCP的方式动态配置IP地址。 DHCP/BOOTP服务为Bootloader分配IP地址,配置网络参数,然后才能够支持网络传输功能。如果Bootloader可以直接设置网络参数,就可以不使用DHCP。 TFTP服务为Bootloader客户端提供文件下载功能,把内核映像和其他文件放在/tftpboot目录下。这样Bootloader可以通过简单的TFTP协议远程下载内核映像到内存。如图6.1所示。 图6.1 网络启动示意图 大部分引导程序都能够支持网络启动方式。例如:BIOS的PXE(Preboot Execution Environment)功能就是网络启动方式;U-Boot也支持网络启动功能。

2.磁盘启动方式

传统的Linux系统运行在台式机或者服务器上,这些计算机一般都使用BIOS引导,并且使用磁盘作为存储介质。如果进入BIOS设置菜单,可以探测处理器、内存、硬盘等设备,可以设置BIOS从软盘、光盘或者某块硬盘启动。也就是说,BIOS并不直接引导操作系统。那么在硬盘的主引导区,还需要一个Bootloader。这个Bootloader可以从磁盘文件系统中把操作系统引导起来。 Linux传统上是通过LILO(LInux LOader)引导的,后来又出现了GNU的软件GRUB(GRand Unified Bootloader)。这2种Bootloader广泛应用在X86的Linux系统上。你的开发主机可能就使用了其中一种,熟悉它们有助于配置多种系统引导功能。 LILO软件工程是由Werner Almesberger创建,专门为引导Linux开发的。现在LILO的维护者是John Coffman,最新版本下载站点:http://lilo.go.dyndns.org。LILO有详细的文档,例如LILO套件中附带使用手册和参考手册。此外,还可以在LDP的"LILO mini-HOWTO"中找到LILO的使用指南。 GRUB是GNU计划的主要bootloader。GRUB最初是由Erich Boleyn为GNU Mach操作系统撰写的引导程序。后来有Gordon Matzigkeit和Okuji Yoshinori接替Erich的工作,继续维护和开发GRUB。GRUB的网站http://www.gnu.org/software/grub/上有对套件使用的说明文件,叫作《GRUB manual》。GRUB能够使用TFTP和BOOTP或者DHCP通过网络启动,这种功能对于系统开发过程很有用。 除了传统的Linux系统上的引导程序以外,还有其他一些引导程序,也可以支持磁盘引导启动。例如:LoadLin可以从DOS下启动Linux;还有ROLO、LinuxBIOS,U-Boot也支持这种功能。

3.Flash启动方式

大多数嵌入式系统上都使用Flash存储介质。Flash有很多类型,包括NOR Flash、NAND Flash和其他半导体盘。其中,NOR Flash(也就是线性Flash)使用最为普遍。 NOR Flash可以支持随机访问,所以代码是可以直接在Flash上执行的。Bootloader一般是存储在Flash芯片上的。另外,Linux内核映像和RAMDISK也可以存储在Flash上。通常需要把Flash分区使用,每个区的大小应该是Flash擦除块大小的整数倍。图6.2是Bootloader和内核映像以及文件系统的分区表。 图6.2 Flash存储示意图 Bootloader一般放在Flash的底端或者顶端,这要根据处理器的复位向量设置。要使Bootloader的入口位于处理器上电执行第一条指令的位置。 接下来分配参数区,这里可以作为Bootloader的参数保存区域。 再下来内核映像区。Bootloader引导Linux内核,就是要从这个地方把内核映像解压到RAM中去,然后跳转到内核映像入口执行。 然后是文件系统区。如果使用Ramdisk文件系统,则需要Bootloader把它解压到RAM中。如果使用JFFS2文件系统,将直接挂接为根文件系统。这两种文件系统将在第12章详细讲解。 最后还可以分出一些数据区,这要根据实际需要和Flash大小来考虑了。 这些分区是开发者定义的,Bootloader一般直接读写对应的偏移地址。到了Linux内核空间,可以配置成MTD设备来访问Flash分区。但是,有的Bootloader也支持分区的功能,例如:Redboot可以创建Flash分区表,并且内核MTD驱动可以解析出redboot的分区表。 除了NOR Flash,还有NAND Flash、Compact Flash、DiskOnChip等。这些Flash具有芯片价格低,存储容量大的特点。但是这些芯片一般通过专用控制器的I/O方式来访问,不能随机访问,因此引导方式跟NOR Flash也不同。在这些芯片上,需要配置专用的引导程序。通常,这种引导程序起始的一段代码就把整个引导程序复制到RAM中运行,从而实现自举启动,这跟从磁盘上启动有些相似。

U-Boot介绍

U-Boot功能介绍

PC机引导加载程序由BIOS和OS BootLoader(通常为GRUB)组成,嵌入式系统一般没有BIOS这样的固件程序,系统的引导加载完全由BootLoader完成。不同的系统,boot Loader是不同的。Boot Loader有许多开放源代码,但在标准Linux中没有,需要用户从网上下载。 BootLoader包括LILO、GRUB、Loadlin、BOLB、U-boot、RedBoot等多种,其中,GRUB是LILO的继任者,用于PC机,Loadlin用于从DOS装载Linux;BLOB来自LART(Luser Attitude Readjustment Tool)项目,用于引导加载基于StorngARM CPU的单个主板计算机;U-boot(Universal loader)是通用的开源引导程序,常用于基于ARM、PowerPC、MIPS构架的嵌入式Linux系统的引导,也用于NetBSD和VxWorks等多种操作系统的引导。 U-Boot支持的主要功能列出如下:
  • 系统引导功能。
  • 支持NFS挂载、RAMDISK 系统引导 压缩或非压缩形式的根文件系统。
  • 支持NFS挂载、从Flash中引导压缩或非压缩的Linux内核。
  • 具有强大的操作系统接口功能,可灵活设置、传递多个参数给操作系统,支持目标板环境参数的多种存储方式,如Flash、NVRAM、EEPROM。
  • 支持CRC32校验,可校验Flash中内核、RAMDISK镜像文件是否完好。
  • 支持串口、SDRAM、Flash、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等设备驱动。
  • 支持上电自检功能,如:SDRAM、Flash大小自动检测;SDRAM 故障检测;CPU型号检测等。
U-Boot有"启动加载"和"下载"两种操作模式,启动加载模式将操作系统加载到 RAM 中运行。下载模式是以某种通信方式从主机(Host)下载文件保存到目标机的 RAM 中,然后被 U-Boot 写到目标机上的FLASH中。这种模式下还提供一个简单的命令行接口。 U-Boot初始化目标板硬件,为嵌入式操作系统提供目标板硬件配置信息,完成嵌入式操作系统装载、引导和运行的固件程序。它能够将系统的软硬件紧密衔接在一起。 Intel基于XScale架构的pxa27X ARM系列处理器,最高主频可达624MHz,加入了Wireless MMX技术,广泛应用于PDA、智能手机、PMP等产品的开发中,硬件的详细说明请参考产品手册。 ARM嵌入式系统复位后通常都从地址 0x00000000 开始执行,Boot Loader 就从这里开始。 Boot Loader分为由汇编语言实现的部分(称为stage1)和用C语言实现部分(称为stage2)。stage1的功能是硬件设备初始化、准备 RAM、将C语言实现部分拷贝RAM、设置好堆栈,并跳转到C语言实现部分入口点。stage1中的初始化过程列出如下: (1)设置GPIO控制器,以便能控制外围设备。 (2)屏蔽所有的中断。Boot Loader 的执行过程中不必响应任何中断。 (3)设置 CPU时钟频率。 (4)RAM 初始化。设置系统的内存控制器等。 (5)关闭 CPU 内部指令/数据 cache。 (6)准备stage2的ram空间,拷贝stage2到ram,设置堆栈。堆栈设置在stage2_end-4的地方,向下增长。 (7)跳转到stage2的入口点即main函数开始执行stage2的代码。 跳转方法是使用了trampoline程序来包装了main函数,trampoline.S程序如下: .text   .globl _trampoline _trampoline: bl main /* if main ever returns we just call it again */ b _trampoline
 从这段代码可以看出,当从main函数中返回时,则再进入main函数。以防止程序异常中断。  执行完stage1后系统内存布局如图1所示。 第2章嵌入Linux 02.png
图1 执行完stage1后系统内存布局图
stage2的功能是初始化硬件、检测系统内存映射(memory map)、将 kernel 映像和根文件系统映像从 flash 上读到 RAM 中、设置启动参数、进入内核。进入stage2后,程序都是c语言实现的,stage2的第一个函数为main函数。

U-Boot安装

下载和编译U-Boot源代码,方法如下: #下载 $ cd /opt/eldk/usr/src $ wget ftp://ftp.denx.de/pub/u-boot/u-boot-1.3.2.tar.bz2 $ rm -f u-boot $ bunzip2 < u-boot-1.3.2.tar.bz2 | tar xf - $ ln -s u-boot-1.3.2 u-boot $ cd u-boot #编译 $ make canyonlands_config #生成配置文件 $ make all #编译 当目标板没有烧录U-Boot或其他固件时,用户可用带有BDM/JTAG接口的调试或烧录编程器(如:BDI2000)将U-Boot烧录到目标板的Flash中,如果目标板已有一个可运行的U-Boot,可不用烧录编程器,而直接通过U-Boot的命令将U-Boot映像文件下载到Flash中。 U-Boot映像文件是特殊格式的文件,它包括创建时间、操作系统、压缩类型、映像文件类型、映像文件名和CRC32校验和等信息。可由ELDK工具命令mkimage创建。

U-Boot命令介绍

在缺省配置下,U-Boot以交互模式运行,它在UART1端口的串行控制台上提供了简单的命令行操作,用户可用命令help查看可用的U-Boot命令,部分命令列出如下: => help ' - alias for 'help' askenv - get environment variables from stdin autoscr - run script from memory base - print or set address offset bdinfo - print Board Info structure boot - boot default, i.e., run 'bootcmd' bootd - boot default, i.e., run 'bootcmd' bootelf - Boot from an ELF image in memory bootm - boot application image from memory bootp - boot image via network using BootP/TFTP protocol bootstrap - program the I2C bootstrap EEPROM bootvx - Boot vxWorks from an ELF image cmp - memory compare coninfo - print console devices and information cp - memory copy crc32 - checksum calculation date - get/set/reset date & time dhcp - invoke DHCP client to obtain IP/boot params

U-Boot环境变量

U-Boot环境是保存在永久存储中的一块内存,当U-Boot启动时拷贝到RAM中,存储配置系统的环境变量,并被CRC32校验和保护。一些环境变量对U-Boot来说有特殊含义,可用这些环境变量配置U-Boot的行为。表1说明了环境变量的功能。
表1 U-Boot环境变量的说明
环境变量 功能说明 bootdelay 定义复位等待执行bootcmd变量对应命令的时间(以秒为单位)。 baudrate 定义串口控制台的波特率。 netmask 定义以太网接口的掩码。 ethaddr 定义以太网接口的MAC地址。 bootfile 用TFTP装载的映像的名字。 bootargs 定义传递给Linux内核的启动参数。 bootcmd 定义自动启动时执行的几条命令。 serverip 定义tftp服务器端的IP地址。 ipaddr 定义本地的IP地址。 autoload 如果设置为no,命令rarpb、bootp或dhcp将仅从BOOTP/DHCP服务器执行配置查找,而不使用TFTP装载任何映像。 autostart 如果设置为yes,使用命令rarpb、bootp、dhcp、tftp、disk或docb装载的映像将被命令bootm自动启动。 loadaddr 命令tftp或loads等的缺省装载地址。 U-Boot的环境变量都有缺省值,用户可以修改用于改变U-Boot的行为,环境变量存储于EEPROMak Flash这样的永久存储区。环境变量用U-Boot命令setenv设置,一些设置样例列出如下: =>setenv serverip 192.168.1.151 =>setenv ipaddr 192.168.1.152 =>setenv rootpath "/opt/ruiva/xscale/rootfs " =>setenv bootargs "root=/dev/nfs rw nfsroot=$(serverip):$(rootpath) ip=$(ipaddr) " console= ttyS0,115200 mem=64M #通过bootm命令传递给内核的启动参数 =>setenv kernel_addr 30000000 =>setenv nfscmd "tftp $(kernel_addr) uImage; bootm$(kernel_addr) " #将uImage下载到指定的地址 =>run nfscmd #运行nfscmd脚本

U-Boot脚本

U-Boot允许存储命令序列在纯文本文件中,用命令mkimage将该文件转换成脚本映像,该映像可用U-Boot命令autoscr执行。 例如:用户需要在许多目标板上运行下面命令序列,可将该命令序列存储在一个文本文件setenv-commands中,其内容如下: bash$ cat setenv-commands setenv loadaddr 00200000 echo ===== U-Boot settings ===== setenv u-boot /tftpboot/TQM860L/u-boot.bin setenv u-boot_addr 40000000 setenv load_u-boot 'tftp ${loadaddr} ${u-boot}' setenv install_u-boot 'protect off ${u-boot_addr} +${filesize};era ${u-boot_addr} +${filesize};cp.setenv update_u-boot run load_u-boot install_u-boot echo ===== Linux Kernel settings ===== setenv bootfile /tftpboot/TQM860L/uImage setenv kernel_addr 40040000 setenv load_kernel 'tftp ${loadaddr} ${bootfile};' setenv install_kernel 'era ${kernel_addr} +${filesize};cp.b ${loadaddr} ${kernel_addr} ${filesize}' setenv update_kernel run load_kernel install_kernel echo ===== Ramdisk settings ===== setenv ramdisk /tftpboot/TQM860L/uRamdisk setenv ramdisk_addr 40100000 setenv load_ramdisk 'tftp ${loadaddr} ${ramdisk};' setenv install_ramdisk 'era ${ramdisk_addr} +${filesize};cp.b ${loadaddr} ${ramdisk_addr} ${filesize}' setenv update_ramdisk run load_ramdisk install_ramdisk echo ===== Save new definitions ===== saveenv
转换成映像文件的方法列出如下: bash$ mkimage -T script -C none -n 'Demo Script File' -d setenv-commands setenv.img Image Name: Demo Script File Created: Mon Jun 6 13:33:14 2005 Image Type: PowerPC Linux Script (uncompressed) Data Size: 1147 Bytes = 1.12 kB = 0.00 MB Load Address: 0x00000000 Entry Point: 0x00000000 Contents: Image 0: 1139 Bytes = 1 kB = 0 MB 在目标板上,用户可以像其他映像文件一样使用tftp这样的命令进行装载,然后,使用命令autoscr执行该映像,方法如下: => tftp 100000 /tftpboot/TQM860L/setenv.img

U-Boot单独应用程序

U-Boot允许动态装载或运行 单个应用程序,该应用程序可以使用U-Boot资源,如:控制台I/O函数、内存分配或中断服务。编译U-Boot时,单个应用程序与U-Boot一起编译。

位图支持

通过添加CFG_CMD_BMP选项到CONFIG_COMMAND命令选项,可以在U-Boot中打开位图支持,这将添加命令bmp到U-Boot配置的命令列表中。命令bmp可以在屏幕上显示图像。图像传送与显示的命令列出如下: => tftp 100000 /tftpboot/LWMON/denk_startup.bmp TFTP from server 192.168.3.1; our IP address is 192.168.3.74 Filename '/tftpboot/LWMON/denk_startup.bmp'. Load address: 0x100000 Loading: ############################################################# done Bytes transferred = 308278 (4b436 hex) => bmp info 100000 Image size : 640 x 480 Bits per pixel: 8 Compression : 0 => bmp display 100000
许多用户期望在系统启动过程(约5~6秒)中看见图像,这种图像显示称为splash screen。U-Boot支持该功能。用户通过添加"#define CONFIG_SPLASH_SCREEN"到目标板的配置文件,就可打开splash screen支持,这意味着打开了位图支持。 目标板开机后,U-Boot将测试环境变量splashimage是否定义,它是否含有位图的地址,如果含有位图,目标板启动时将不显示启动信息,而是显示splash screen。安装splash screen位图的方法列出如下: => tftp 100000 /tftpboot/denx_startup.bmp TFTP from server 192.168.3.1; our IP address is 192.168.3.74 Filename '/tftpboot/denx_startup.bmp'. Load address: 0x100000 Loading: ############################################################# done Bytes transferred = 308278 (4b436 hex) => cp.b 100000 41F80000 $filesize Copy to Flash... done => setenv splashimage 41F80000 => saveenv Saving Environment to Flash... Un-Protected 1 sectors Erasing Flash... . done Erased 1 sectors Writing to Flash... done Protected 1 sectors => bmp info $splashimage Image size : 640 x 480 Bits per pixel: 8 Compression : 0
为了实现U-Boot Splash Screen特征的支持,Linux内核加入了配置选项CONFIG_FB_PRE_INIT_FB,它允许跳过帧缓冲区(frame buffer)的某一部分,并再使用由U-Boot固件建立的帧缓冲区内容。这样,系统启动过程中可以显示一个启动图像,该图像就掩盖了系统启动过程中屏幕输出。

2 ELDK工具介绍

ELDK(Embedded Linux Deveelopment Kit)包括GNU交叉开发工具(如:编译器、binutils、gdb等)、大量的预编译目标工具、目标系统需要的库、常用工具(如:ls、rm)、常用服务器(如:DHCP、ftp)、U-Boot等。通过ELDK,目标板可以运行在控制台,并通过主机连接调试、装载映像文件等。 ELDK使用的主机操作系统通常为Fedora Core、Red Hat Linux、SuSE Linux、Debian、Ubuntu、Suse Linux、Mandrake、Slackware、Gentoo Linux和FreeBSD等类型Linux操作系统,支持的目标构架为PPC、ARM、MIPS等。

下载与编译

EKDK提供了源代码包和不同构架平台的二进制代码,可以方便地交叉编译所有源代码或单个软件包,提供了下载、在线调试、在线运行等多种工作方式。用户可以下载源代码光盘或用版本管理工具git进行下载,用git下载的方法列出如下: $ cd /opt/eldk $ git clone git://www.denx.de/git/eldk/build.git build $ git clone git://www.denx.de/git/eldk/tarballs.git tarballs $ git clone git://www.denx.de/git/eldk/SRPMS.git SRPMS 编译整个ELDK工具与软件包的方法列出如下: bash$ TARGET_CPU_FAMILY_LIST="8xx 85xx" #CPU系列号 > /opt/eldk/build.sh -a ppc #CPU构架 > -n 2007-01-19 #编译结果存放目录名 > -p /opt/eldk/build/ppc-2007-01-19 #编译前缀,编译目录存放路径 > -r /opt/eldk/build/ppc-2007-01-19/results #输出存入目录 > -w /opt/eldk/build/ppc-2007-01-19/work #工作目录 > trg 47 47 #编译软件包的起始、结束编号,在文件tpckgs.lst与cpckgs.lst中
在ELDK工具编译成功或下载有ELDK工具可执行代码后,可单独编译不同平台的源文件或软件包,方法如下: bash$ export CROSS_COMPILE=ppc_8xx- #编译器前缀,组成不同构架编译器 bash$ ${CROSS_COMPILE}gcc -o hello_world hello_world.c #编译源文件 bash$ ${CROSS_COMPILE}rpm -iv .src.rpm #解压缩并安装源代码 bash$ ${CROSS_COMPILE}rpmbuild -ba .spec #编译源代码包

建立开发系统

开发时,主机需要与目标板交互,如:调试程序,控制目标板,下载程序映像到目标板,目标板的调试信息需要反回到主机等。为了与目标板交互,主机必须建立开发环境,如:建立串行终端,配置TFTP服务器、DHCP服务器和NFS服务器。 (1)配置串行终端 开发者在开发时需要访问目标板的串行终端端口,通过在主机上的终端对目标板进行控制台操作。通常,开发者将目标板的串行终端端口绑定在主机的一个串行端口,实现控制台的功能,开发者还需要在主机上使用一个终端仿真程序,如:cu或kermit。 命令cu是软件包UUCP的一部分,UUCP可用作串行终端,还具有简单的文件传输功能,可用于映像文件的下载。 依赖于目标板的波特率,需要改写UUCP的配置文件,典型的配置列出如下: •配置文件/etc/uucp/sys的内容列出如下: # # /dev/ttyS0 at 115200 bps: # system S0@115200 port serial0_115200 time any 配置文件/etc/uucp/port的内容列出如下: # # /dev/ttyS0 at 115200 bps: # port serial0_115200 type direct device /dev/ttyS0 speed 115200 hardflow false 接着,用户可用下面命令连接到串行线: $ cu S0@115200 $ ~. #断开连接 命令kermit代表了整个串行线和网络连接通信软件簇,文件~/.kermrc执行kermit的初始化,通过合适的初始化命令可用来定制kermit的行为,U-Boot推荐使用下面的设置: $cat ~/.kermrc: set line /dev/ttyS0 #串行端口 set speed 115200 #波特率 set carrier-watch off set handshake none set flow-control none robust set file type bin set file name lit set rec pack 1000 set send pack 1000 set window 5 命令kermit连接串行线的方法如下: $ kermit –c (2)配置TFTP服务器 使用 U-Boot装载Linux内核或应用程序的最快捷方式是通过Ethernet传递,U-Boot实现了TFTP协议,通过U-Boot的tftpboot命令下载文件到目标板中,主机应确信安装了TFTP后台程序/usr/sbin/in.tftpd。 (3)配置BOOTTP/DHCP服务器 BOOTP/DHCP服务器用于自动传递配置信息到目标机,目标机仅需要知道它自己的Ethernet硬件MAC地址,主机应安装DHCP包,配置DHCP服务器,一个配置样例列出如下: subnet 192.168.0.0 netmask 255.255.0.0 { option routers 192.168.1.1; option subnet-mask 255.255.0.0; option domain-name "local.net"; option domain-name-servers ns.local.net; host trgt { hardware ethernet 00:30:BF:01:02:D0; #目标板MAC地址 fixed-address 192.168.100.6; #目标板固定IP地址 option root-path "/opt/eldk-4.2/ppc_4xx"; option host-name "canyonlands"; #目标板的主机名 next-server 192.168.1.1; filename "/tftpboot/canyonlands/uImage "; } } 用上面的配置,DHCP服务器将回答来自目标板的请求,给出下面的信息:
  • 目标板位于子网192.168.0.0,子网掩码为255.255.0.0。
  • 目标板的主机名为Canyonlands,IP地址为192.168.100.0。
  • IP地址为192.168.1.1的主机给目标板提供启动映像(boot image),当目标板根文件系统通过NFS挂接在主机上,它还提供NFS服务器功能。
  • 主机提供文件/tftpboot/canyonlands/uImage作为目标板的启动映像。
  • 目标板可以挂接目录/opt/eldk-4.2/ppc_4xx在NFS服务作为根文件系统。
(3)配置NFS服务器 通过NFS,主机和目标板可以共享文件,NFS服务器对外暴露的目录可以挂接作目标板的根文件系统。主机应安装NFS服务器,并开启NFS服务。 NFS服务器的配置样例列出如下: $cat /etc/exports /opt/eldk-4.2/ppc_4xx 192.168.0.0/255.255.0.0(rw,no_root_squash,sync) 上述语句含义为暴露目录/opt/eldk-4.2/ppc_4xx,在子网192.168.0.0上的所有主机都可以读写操作该目录。

编译安装Linux内核

ELDK源代码包括了Linux内核,用户可以编译安装内核,方法如下: bash$ cd /opt/eldk/usr/src/linux-2.6-denx bash$ make mrproper bash$ make ARCH=powerpc CROSS_COMPILE=ppc_4xx- canyonlands_defconfig #用户设备目标板的配置 bash$ make ARCH=powerpc CROSS_COMPILE=ppc_4xx- INSTALL_MOD_PATH=/opt/eldk-4.2/ppc_4xx modules_install bash$ cp arch/powerpc/boot/uImage /tftpboot/uImage

启动嵌入式Linux

(1)启动内核 当Linux内核和扁平设备树块(Flattened Device Tree Blob,FDT blob)下载到目标板的系统内存(如:RAM、ROM、flash等)时,用户可用bootm命令启动系统。假设内核映像存放在地址0xFC000000处,FDT blob存放在地址0xFC1E0000处,则启动映像文件的命令列出如下: =>bootm FC000000 – FC1E0000 (2)编译blob Linux内核运行时,期望知道它运行的硬件信息,这些硬件信息按《开放固件规范》以设备树的形式描述,由于象U-Boot这样的BootLoader没有实现开放固件API(Open Firmware API),硬件信息将以扁平设备树的二进制形式传递给内核,这种二进制形式称为FDT blob或简称blob。在设备树信息文件(如:canyonlands.dts)由dtc编译器编译成二进制文件,ELDK包括了dtc编译器,编译方法列出如下: dtc -b 0 -V 17 -p 0x1000 -I dts -O dtb -f arch/powerpc/boot/dts/canyonlands.dts > /tftpboot/canyon 编译完后,可使用命令tftp将blob传输到目标机的内存中,接着,系统启动bootm将blob中的硬件信息传递给内核。 U-Boot还提供命令fdt修改blob。 (3)传递内核参数 用户可以传递附加信息(如:根设备或网络配置)给内核。U-Boot通过环境变量bootargs实现该功能。该变量的内容作为启动参数(或命令行参数)自动传递给内核,这样,可以让同一个内核映像有更多的配置,例如:仅改变变量bootargs的内容,就可以让同一个内核映像与initrd内存盘( ramdisk)映像一起启动,或者与在NFS之上的根文件系统一起启动。 例如:内核映像地址在0x200000,作为根文件系统的initrd ramdisk映像地址在0x400000,FDT blob地址在0x600000,则启动命令列出如下: => setenv bootargs root=/dev/ram rw => bootm 200000 400000 600000 假设NFS服务器地址为192.168.1.1,输出目录/opt/eldk-4.2/ppc_4xx作为目标板的根文件系统,目标板的地址为192.168.100.6,目标板主机名为canyonlands,子网掩码为255.255.255.0。现由在NFS上的根文件系统启动同样内核,则启动命令列出如下: => setenv bootargs root=/dev/nfs rw nfsroot=192.168.1.1:/opt/eldk-4.2/ppc_4xx ip=192.168.100.6:192.168.1.1 => bootm 200000 - 600000 内核启动后,可用下面命令查看启动的命令行: $ cat /proc/cmdline 用户还可用U-Boot环境变量存放所有需要的配置参数,方法如下: => setenv ipaddr 192.168.100.6 => setenv serverip 192.168.1.1 => setenv netmask 255.255.0.0 => setenv hostname canyonlands => setenv rootpath /opt/eldk-4.2/ppc_4xx => saveenv 用户可用这些变量构建传递到内核的启动参数,方法如下: => setenv nfsargs 'root=/dev/nfs rw nfsroot=${serverip}:${rootpath}' 下面可以定义bootargs环境变量,并一步步构建启动参数: => setenv ramargs setenv bootargs root=/dev/ram rw => setenv nfsargs 'setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath}' => setenv addip 'setenv bootargs ${bootargs} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::=> setenv ram_root 'run ramargs addip;bootm ${kernel_addr} ${ramdisk_addr} ${fdt_addr} ' => setenv nfs_root 'run nfsargs addip;bootm ${kernel_addr} - ${fdt_addr} ' 上面定义了ram_root和nfs_root两个变量,分别用于从ramdisk映像的根文件系统和NFS上的根文件系统启动。变量通过run命令执行。 (4)用Ramdisk映像单独操作 当应用程序开发完成时,用户希望独立运行嵌入式系统,而不依赖外部系统,如:NFS文件系统,目标板不再通过NFS挂接根文件系统,而是使用压缩的ramdisk映像,该ramdisk映像存储在flash中,在系统启动时装载到RAM中。 装载ramdisk映像到RAM,并将它写入到flash的方法如下: => setenv ramdisk_addr FC200000 => => setenv ram_ws 100000 => => prot off FC200000 FC3FFFFF Un-Protected 16 sectors => => era FC200000 FC3FFFFF ................ done Erased 16 sectors => => tftp 100000 /tftpboot/canyonlands/uRamdisk => imi 100000 ## Checking Image at 00100000 ... => => cp.b ${ram_ws} ${ramdisk_addr} ${filesize} Copy to Flash... done => => imi ${ramdisk_addr} => => saveenv Saving Environment to Flash... 为了告诉Linux内核使用ramdisk映像作为根文件系统,用户必须修改传递给内核的命令行参数,ramdisk映像地址作为命令bootm的第二个参数,第一个参数为内核映像的内存地址,第三个参数是FDT blob的内存地址。

Flash文件系统

(1)MTD 所有可用的Flash文件系统都基于内存技术设备(Memory Technology Devices,MTD)层,因此,用户配置内核时必须打开flash文件系统支持,配置结果如下: CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_GEN_PROBE=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_ROM=y CONFIG_MTD_canyonlands=y 目标板flash设备的分区设计由MTD映射例程(源代码在drivers/mtd/maps/)定义,canyonlands的分区设计列出如下: -bash-3.2# cat /proc/mtd dev: size erasesize name mtd0: 001e0000 00020000 "kernel" mtd1: 00020000 00020000 "dtb" mtd2: 00200000 00020000 "root" mtd3: 03b60000 00020000 "user" mtd4: 00040000 00020000 "env" mtd5: 00060000 00020000 "u-boot" mtd6: 02000000 00004000 "NAND 32MiB 3,3V 8-bit" mtd7: 00060000 00004000 "u-boot" mtd8: 00008000 00004000 "env" mtd9: 01f98000 00004000 "content" 用户还可以运行一些基本的测试,验证flash驱动程序例程和分区,方法如下: -bash-3.2# xxd /dev/mtd3 | head -4 0000000: 5468 7520 4a61 6e20 2031 2030 313a 3034 Thu Jan 1 01:04 0000010: 3a35 3120 4345 5420 3139 3730 0aff ffff :51 CET 1970.... 0000020: ffff ffff ffff ffff ffff ffff ffff ffff ................ 0000030: ffff ffff ffff ffff ffff ffff ffff ffff ................ 在MTD设备16进制转储中,用户可以鉴别一些字符串,验证U-Boot环境、一个Linux内核、ramdisk映像或一个空的分区。如:上面的转储显示表明该分区是空的,用户可以写数据到该分区,方法如下: -bash-3.2# date > /dev/mtd3 -bash-3.2# -bash-3.2# head -1 /dev/mtd3 Thu Jan 1 01:04:51 CET 1970 -bash-3.2# -bash-3.2# date > /dev/mtd3 上述命令中,第一次写是正确的,第二次写将得到一个错误,因为flash内存必须先擦除,再进行写操作。 用户可使用Linux命令eraseall擦除整个MTD分区,再进行写操作,方法如下: -bash-3.2# flash_eraseall /dev/mtd3 -bash-3.2# -bash-3.2# date > /dev/mtd3 -bash-3.2# -bash-3.2# head -1 /dev/mtd3 Thu Jan 1 01:04:51 CET 1970 (2)JFFS2 JFFS(Journalling Flash File System)是嵌入式设备flash上最合适的文件系统,JFFS2为嵌入式系统闪存的使用而设计,它是一个日志结构的文件系统,这意味着它对掉电、系统崩溃或其他意外关闭等系统异常具有有效保护措施,如:仅丢失正在写的数据,重启动后,不需要任何的文件系统检查。 配置内核时,需要打开JFFS配置选项,配置结果列出如下: CONFIG_JFFS_FS=y CONFIG_JFFS_FS_VERBOSE=0 对flash的直接读写或擦除操作一般使用MTD层的字符设备接口/dev/mtd*,文件系统操作(如:mount)必须使用块设备接口/dev/mtdblock*。JFFS2还提供了透明的压缩功能,让flash可以存储更多的内容。擦除、挂接并查看分区的方法列出如下: # eraseall /dev/mtd2 //擦除分区 Erased 4096 Kibyte @ 0 -- 100% complete. # mount -t jffs2 /dev/mtdblock2 /mnt //挂接文件系统 # mount //查看分区挂接情况 /dev/root on / type nfs (rw,v2,rsize=4096,wsize=4096,hard,udp,nolock,addr=10.0.0.2) proc on /proc type proc (rw) devpts on /dev/pts type devpts (rw) /dev/mtdblock2 on /mnt type jffs (rw) # df //查看磁盘使用情况 Filesystem 1k-blocks Used Available Use% Mounted on /dev/root 2087212 1232060 855152 60% / /dev/mtdblock2 3584 0 3584 0% /mnt 可以用工具mkfs.jffs2创建JFFS2文件系统映像,下面的样例给flash分区创建了3MB的JFFS2映像文件image.jffs2,并移入目录/tmp/flashtools/中的数据,然后,擦除分区/dev/mtd4,并将映像image.jffs2拷贝到分区mtd4中。最后,挂接分区mtd4。这些操作的命令列出如下: # mkfs.jffs2 --pad=3145728 --eraseblock=262144 --root=/tmp/flashtools/ --output image.jffs2 # eraseall /dev/mtd4 Erased 3072 Kibyte @ 0 -- 100% complete. # dd if=image.jffs2 of=/dev/mtd4 bs=256k 12+0 records in 12+0 records out # mount -t jffs2 /dev/mtdblock4 /mnt # df /mnt Filesystem 1k-blocks Used Available Use% Mounted on /dev/mtdblock4 3072 2488 584 81% /mnt (3)CramFS 用户有时希望文件系统是仅读的,可压缩仅读文件系统(Compressed ROM Filesystem) CramFs实现了文件系统仅读与压缩的功能。用工具mkcramfs创建可压缩仅读文件系统映像的方法列出如下: $ mkdir /tmp/test $ cp ... /tmp/test $ du -sk /tmp/test 64 /tmp/test $ mkcramfs /tmp/test test.cramfs.img Super block: 76 bytes erase eraseall mkfs.jffs lock unlock Directory data: 176 bytes -54.96% (-4784 bytes) erase -55.46% (-5010 bytes) eraseall -51.94% (-8863 bytes) mkfs.jffs -58.76% (-4383 bytes) lock -59.68% (-4215 bytes) unlock Everything: 24 kilobytes $ ls -l test.cramfs.img -rw-r--r-- 1 wd users 24576 Nov 10 23:44 test.cramfs.img 由于CramFs映像是压缩的,因此,输入的目录数据为64kB,而映像文件为24kB大小。 写CramFs映像到flash分区的方法列出如下: # cp test.cramfs.img /dev/mtd3 # mount -t cramfs /dev/mtdblock3 /mnt # mount /dev/root on / type nfs (rw,v2,rsize=4096,wsize=4096,hard,udp,nolock,addr=10.0.0.2) proc on /proc type proc (rw) devpts on /dev/pts type devpts (rw) /dev/mtdblock3 on /mnt type cramfs (rw) # ls -l /mnt total 54 -rwxr-xr-x 1 wd users 8704 Jan 9 16:32 erase -rwxr-xr-x 1 wd users 9034 Jan 1 01:00 eraseall -rwxr-xr-x 1 wd users 7459 Jan 1 01:00 lock -rwxr-xr-x 1 wd users 17063 Jan 1 01:00 mkfs.jffs

根文件系统的设计与编译

嵌入式系统的根文件系统(Root File System)设计包括存放的内容、文件系统类型、在哪里存放文件系统和如何启动它。 根文件系统通常存放在目标板的闪存(flash memory)中或CF、SD、MMC卡等中,可以使用像ELDK这样的Linux发布版本创建根文件系统的内容。 下面使用来自ELDK的SELF(Simple Embedded Linux Framework)映像创建根文件系统。 在ELDK软件包中,各种平台构架的SELF映像文件在目录/opt/eldk//images/下,文件名为ramdisk_image.gz,是个压缩的ramdisk映像。 由SELF创建根文件系统tar包的方法列出如下: //解压缩ramdisk映像 bash$ gzip -d -c -v /opt/eldk/ppc_8xx/images/ramdisk_image.gz >/tmp/ramdisk_image /opt/eldk/ppc_8xx/images/ramdisk_image.gz: 61.4% //下面的操作需要root权限 //挂接ramdisk映像 bash# mount -o loop /tmp/ramdisk_image /mnt/tmp //创建tar包,为了避免需要root权限,这里不包括在tar包中的设备(device)文件 bash# cd /mnt/tmp bash# tar -zc --exclude='dev/*' -f /tmp/rootfs.tar.gz * //需要时,为CramFs创建一个单独的tar包,用来包括设备条目 bash# tar -zcf /tmp/devices.tar.gz dev/ bash# cd /tmp //卸载ramdisk映像 bash# umount /mnt/tmp (1)根文件系统在ramdisk上 ramdisk常用于存放嵌入式系统的根文件系统,但很少情况将ramdisk映像作为嵌入式系统的根文件系统解决方案。 根文件系统包括在ramdisk中的优点是:编译简单;基于RAM,速度快;Linux内核支持完美;容易使用,甚至可将带有Linux内核的ramdisk放在一个映像文件中;根文件系统是可写的;每次重启动郈的初始状态容易恢复。 缺点是:每次必须将整个文件系统装入RAM,造成启动慢,需要附加存储器存放可写的永久数据。 在ramdisk映像中,根文件系统常用ext2文件系统类型,创建在ramdisk上根文件系统的方法说明如下:
  • 通过解开tar包创建目标板根文件系统内容的目录树
$ mkdir rootfs $ cd rootfs $ tar zxf /tmp/rootfs.tar.gz
  • 使用工具genext2fs创建ramdisk映像,可用设备表文本文件定义在产生的文件系统映像中应创建的设备,这意味着需要root权限。设备表rootfs_devices.tab部分内容列出如表2所示。
表2 设备表文件rootfs_devices.tab部分内容 name type mode uid gid major minor start inc count /dev d 755 0 0 - - - - - /dev/console c 640 0 0 5 1 - - - /dev/fb0 c 640 0 0 29 0 - - - 使用工具genext2fs 创建文件系统映像的方法如下: $ ROOTFS_DIR=rootfs # 根文件系统内容目录 $ ROOTFS_SIZE=3700 # 文件系统映像的大小 $ ROOTFS_FREE=100 # 期望的空闲空间 $ ROOTFS_INODES=380 # 节点(inode)数 $ ROOTFS_DEVICES=rootfs_devices.tab # 设备描述表文件 $ ROOTFS_IMAGE=ramdisk.img # 产生的文件系统映像 $ genext2fs -U -d ${ROOTFS_DIR} -D ${ROOTFS_DEVICES} -b ${ROOTFS_SIZE} -r ${ROOTFS_FREE} -i ${ROOTFS_INODES} ${ROOTFS_IMAGE}
  • 压缩文件系统映像,方法如下:
$ gzip -v9 ramdisk.img rootfs.img: 55.6% -- replaced with ramdisk.img.gz
  • 创建U-Boot使用的根文件系统映像uRamdisk,方法如下:
$ mkimage -T ramdisk -C gzip -n 'Test Ramdisk Image' > -d ramdisk.img.gz uRamdisk Image Name: Test Ramdisk Image Created: Sun Jun 12 16:58:06 2005 Image Type: PowerPC Linux RAMDisk Image (gzip compressed) Data Size: 1618547 Bytes = 1580.61 kB = 1.54 MB Load Address: 0x00000000 Entry Point: 0x00000000 We now have a root file system image uRamdisk that can be used with U-Boot. (2)根文件系统在JFFS2文件系统上 在嵌入式系统中,JFFS2作为根文件系统的优点是:文件系统使用压缩,可有效利用闪存空间;它是日志结构的文件系统,对无序的关机是强壮的;它是可写闪存文件系统。缺点是:挂接时间长;由于压缩/解压缩,造成读写操作时间长;垃圾回收线程可在任何时间启动,消耗CPU时间,阻塞对文件系统的访问。 尽管有上面一些不利因素,但基于JFFS2 的根文件系统容易编译,可有效利用可用的资源,并能可靠运行。 创建基于JFFS2的根文件系统的方法如下:
  • 通过解开tar包创建目标板根文件系统的内容,方法如下:
$ mkdir rootfs $ cd rootfs $ tar zxf /tmp/rootfs.tar.gz
  • 使用工具mkfs.jffs2创建一个JFFSW文件系统映像,方法如下:
$ ROOTFS_DIR=rootfs # 根文件系统内容目录 $ ROOTFS_EBSIZE=0x20000 # 闪存的擦除块大小 $ ROOTFS_ENDIAN=b # 目标板系统是大端字节序 $

热门文章