參考來源
http://www.denx.de/wiki/publish/DULG/DULG-tqm8xxl.html
7. Booting Embedded Linux
7.1. Introduction
In principle, if you have a Linux kernel image somewhere in system memory (RAM, ROM, flash...), then all you need to boot the system is the bootm
command. Assume a Linux kernel image has been stored at address 0x40080000 - then you can boot this image with the following command:
=> bootm 40080000
7.2. Passing Kernel Arguments
In nearly all cases, you will want to pass additional information to the Linux kernel; for instance, information about the root device or network configuration.
In U-Boot, this is supported using the bootargs
environment variable. Its contents are automatically passed to the Linux kernel as boot arguments (or "command line" arguments). This allows the use of the same Linux kernel image in a wide range of configurations. For instance, by just changing the contents of the bootargs
variable you can use the very same Linux kernel image to boot with an initrd
ramdisk image, with a root filesystem over NFS, with a CompactFlash disk or from a flash filesystem.
As one example, to boot the Linux kernel image at address 0x200000 using the initrd
ramdisk image at address 0x400000 as root filesystem, you can use the following commands:
=> setenv bootargs root=/dev/ram rw => bootm 200000 400000
To boot the same kernel image with a root filesystem over NFS, the following command sequence can be used. This example assumes that your NFS server has the IP address "10.0.0.2" and exports the directory "/opt/eldk/ppc_8xx" as root filesystem for the target. The target has been assigned the IP address "10.0.0.99" and the hostname "tqm". A netmask of "255.0.0.0" is used:
=> setenv bootargs root=/dev/nfs rw nfsroot=10.0.0.2:/opt/eldk/ppc_8xx ip=10.0.0.99:10.0.0.2:10.0.0.2:255.0.0.0:tqm::off => bootm 200000
Please see also the files Documentation/initrd.txt
and Documentation/nfsroot.txt
in your Linux kernel source directory for more information about which options can be passed to the Linux kernel.
Note: Once your system is up and running, if you have a simple shell login, you can normally examine the boot arguments that were used by the kernel for the most recent boot with the command:
$ cat /proc/cmdline
7.3. Boot Arguments Unleashed
Passing command line arguments to the Linux kernel allows for very flexible and efficient configuration which is especially important in Embedded Systems. It is somewhat strange that these features are nearly undocumented everywhere else. One reason for that is certainly the very limited capabilities of other boot loaders.
It is especially U-Boot's capability to easily define, store, and use environment variables that makes it such a powerful tool in this area. In the examples above we have already seen how we can use for instance the root
and ip
boot arguments to pass information about the root filesystem or network configuration. The ip
argument is not only useful in configurations with root filesystem over NFS; if the Linux kernel has the CONFIG_IP_PNP
configuration enabled (IP kernel level autoconfiguration), this can be used to enable automatic configuration of IP addresses of devices and of the routing table during kernel boot, based on either information supplied on the kernel command line or by BOOTP or RARP protocols.
The advantage of this mechanism is that you don't have to spend precious system memory (RAM and flash) for network configuration tools like ifconfig
or route
- especially in Embedded Systems where you seldom have to change the network configuration while the system is running.
We can use U-Boot environment variables to store all necessary configuration parameters:
=> setenv ipaddr 10.0.0.99 => setenv serverip 10.0.0.2 => setenv netmask 255.0.0.0 => setenv hostname tqm => setenv rootpath /opt/eldk/ppc_8xx => saveenv
Then you can use these variables to build the boot arguments to be passed to the Linux kernel:
=> setenv nfsargs 'root=/dev/nfs rw nfsroot=${serverip}:${rootpath}'
Note how apostrophes are used to delay the substitution of the referenced environment variables. This way, the current values of these variables get inserted when assigning values to the "bootargs"
variable itself later, i. e. when it gets assembled from the given parts before passing it to the kernel. This allows us to simply redefine any of the variables (say, the value of "ipaddr"
if it has to be changed), and the changes will automatically propagate to the Linux kernel.
Note: You cannot use this method directly to define for example the "bootargs"
environment variable, as the implicit usage of this variable by the "bootm"
command will not trigger variable expansion - this happens only when using the "setenv"
command.
In the next step, this can be used for a flexible method to define the "bootargs"
environment variable by using a function-like approach to build the boot arguments step by step:
=> 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}::off' => setenv ram_root 'run ramargs addip;bootm ${kernel_addr} ${ramdisk_addr}' => setenv nfs_root 'run nfsargs addip;bootm ${kernel_addr}'
In this setup we define two variables, ram_root
and nfs_root
, to boot with root filesystem from a ramdisk image or over NFS, respecively. The variables can be executed using U-Boot's run
command. These variables make use of the run
command itself:
- First, either
run ramargs
orrun nfsargs
is used to initialize thebootargs
environment variable as needed to boot with ramdisk image or with root over NFS. - Then, in both cases,
run addip
is used to append theip
parameter to use the Linux kernel IP autoconfiguration mechanism for configuration of the network settings. - Finally, the
bootm
command is used with two resp. one address argument(s) to boot the Linux kernel image with resp. without a ramdisk image. (We assume here that the variableskernel_addr
andramdisk_addr
have already been set.)
This method can be easily extended to add more customization options when needed.
If you have used U-Boot's network commands before (and/or read the documentation), you will probably have recognized that the names of the U-Boot environment variables we used in the examples above are exactly the same as those used with the U-Boot commands to boot over a network using DHCP or BOOTP. That means that, instead of manually setting network configuration parameters like IP address, etc., these variables will be set automatically to the values retrieved with the network boot protocols. This will be explained in detail in the examples below.
7.4. Networked Operation with Root Filesystem over NFS
You can use the printenv
command on the Target to find out which commands get executed by U-Boot to load and boot the Linux kernel:
=> printenv bootcmd=bootp; setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; bootm bootdelay=5 baudrate=115200 stdin=serial stdout=serial stderr=serial ...
After Power-On or reset the system will initialize and then wait for a key-press on the console port. The duration of this countdown is determined by the contents of the bootdelay
environment variable (default: 5 seconds).
If no key is pressed, the command (or the list of commands) stored in the environment variable bootcmd
is executed. If you press a key, you get a prompt at the console port which allows for interactive command input.
In the example above the following commands are executed sequentially:
bootp setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off bootm
These commands take the following effect (pay attention for the modification of environment variables by these commands):
bootp
: This command uses the BOOTP protocol to ask a boot server for information about our system and to load a boot image (which will usually be a Linux kernel image). Since no aguments are passed to this command, it will use a default address to load the kernel image (0x100000 or the last address used by other operations).
=> bootp BOOTP broadcast 1 ARP broadcast 0 TFTP from server 10.0.0.2; our IP address is 10.0.0.99 Filename '/tftpboot/TQM8xxL/uImage'. Load address: 0x100000 Loading: ######################################################################################## done => printenv bootcmd=bootp; setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; bootm bootdelay=5 baudrate=115200 stdin=serial stdout=serial stderr=serial bootfile=/tftpboot/TQM8xxL/uImage gatewayip=10.0.0.2 netmask=255.0.0.0 hostname=tqm rootpath=/opt/eldk/ppc_8xx ipaddr=10.0.0.99 serverip=10.0.0.2 dnsip=10.0.0.2 ...
The Target sends a BOOTP request on the network, and (assuming there is a BOOTP server available) receives a reply that contains the IP address (ipaddr=10.0.0.99
) and other network information for the target (hostname=tqm
, serverip=10.0.0.2
, gatewayip=10.0.0.2
, netmask=255.0.0.0
).
Also, the name of the boot image (bootfile=
/tftpboot/TQM8xxL/uImage
) and the root directory on a NFS server (rootpath=/opt/eldk/ppc_8xx
) was transmitted.
U-Boot then automatically downloaded the bootimage from the server using TFTP.
You can use the command iminfo
(Image Info, or short imi
) to verify the contents of the loaded image:
=> imi 100000 ## Checking Image at 00100000 ... Image Name: Linux-2.4.4 Created: 2002-04-07 21:31:59 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 605429 Bytes = 591 kB = 0 MB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK =>
This tells you that we loaded a compressed Linux kernel image, and that the file was not corrupted, since the CRC32 checksum is OK.
setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} \ ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off
This command defines the environment variable bootargs
. (If an old definition exists, it is deleted first). The contents of this variable is passed as command line to the LInux kernel when it is booted (hence the name). Note how U-Boot uses variable substitution to dynamically modify the boot arguments depending on the information we got from the BOOTP server.
To verify, you can run this command manually:
=> setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off => printenv ... bootargs=root=/dev/nfs rw nfsroot=10.0.0.2:/opt/eldk/ppc_8xx ip=10.0.0.99:10.0.0.2:10.0.0.2:255.0.0.0:tqm::off ...
This command line passes the following information to the Linux kernel:
root=/dev/nfs rw
: the root filesystem will be mounted using NFS, and it will be writable.nfsroot=10.0.0.2:/opt/eldk/ppc_8xx
: the NFS server has the IP address 10.0.0.2, and exports the directory /opt/eldk/ppc_8xx for our system to use as root filesystem.ip=10.0.0.99:10.0.0.2:10.0.0.2:255.0.0.0:tqm::off
: the target has the IP address10.0.0.99
; the NFS server is10.0.0.2
; there is a gateway at IP address10.0.0.2
; the netmask is255.0.0.0
and our hostname istqm
. The first ethernet interface (eth0
) willbe used, and the Linux kernel will immediately use this network configuration and not try to re-negotiate it (IP autoconfiguration isoff
).
See Documentation/nfsroot.txt in you Linux kernel source directory for more information about these parameters and other options.
bootm
: This command boots an operating system image that resides somewhere in the system memory (RAM or flash - them
in the name is for memory). In this case we do not pass any memory address for the image, so the load address 0x100000 from the previous TFTP transfer is used:
=> run flash_nfs ## Booting image at 40040000 ... Image Name: Linux-2.4.4 Created: 2002-04-07 21:31:59 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 605429 Bytes = 591 kB = 0 MB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Linux version 2.4.4 (wd@larry.denx.de) (gcc version 2.95.3 20010111 (prerelease/franzo/20010111)) #1 Sun Apr 7 23:28:08 MEST 2002 On node 0 totalpages: 16384 zone(0): 16384 pages. zone(1): 0 pages. zone(2): 0 pages. Kernel command line: root=/dev/nfs rw nfsroot=10.0.0.2:/opt/hardhat/devkit/ppc/8xx/target ip=10.0.0.99:10.0.0.2::255.0.0.0:tqm:eth0:off panic=1 Decrementer Frequency: 3125000 Calibrating delay loop... 49.86 BogoMIPS Memory: 62580k available (1164k kernel code, 564k data, 52k init, 0k highmem) Dentry-cache hash table entries: 8192 (order: 4, 65536 bytes) Buffer-cache hash table entries: 4096 (order: 2, 16384 bytes) Page-cache hash table entries: 16384 (order: 4, 65536 bytes) Inode-cache hash table entries: 4096 (order: 3, 32768 bytes) POSIX conformance testing by UNIFIX Linux NET4.0 for Linux 2.4 Based upon Swansea University Computer Society NET3.039 Starting kswapd v1.8 CPM UART driver version 0.03 ttyS0 on SMC1 at 0x0280, BRG1 ttyS1 on SMC2 at 0x0380, BRG2 pty: 256 Unix98 ptys configured block: queued sectors max/low 41520kB/13840kB, 128 slots per queue RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize Uniform Multi-Platform E-IDE driver Revision: 6.31 ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx PCMCIA slot B: phys mem e0000000...ec000000 (size 0c000000) No card in slot B: PIPR=ff00ff00 eth0: CPM ENET Version 0.2 on SCC1, 00:d0:93:00:28:81 JFFS version 1.0, (C) 1999, 2000 Axis Communications AB JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.^M Amd/Fujitsu Extended Query Table v1.0 at 0x0040 number of JEDEC chips: 1 0: offset=0x0,size=0x8000,blocks=1 1: offset=0x8000,size=0x4000,blocks=2 2: offset=0x10000,size=0x10000,blocks=1 3: offset=0x20000,size=0x20000,blocks=31 Amd/Fujitsu Extended Query Table v1.0 at 0x0040 number of JEDEC chips: 1 0: offset=0x0,size=0x8000,blocks=1 1: offset=0x8000,size=0x4000,blocks=2 2: offset=0x10000,size=0x10000,blocks=1 3: offset=0x20000,size=0x20000,blocks=31 TQM flash bank 0: Using static image partition definition Creating 4 MTD partitions on "TQM8xxL Bank 0": 0x00000000-0x00040000 : "ppcboot" 0x00040000-0x00100000 : "kernel" 0x00100000-0x00200000 : "user" 0x00200000-0x00400000 : "initrd" TQM flash bank 1: Using static file system partition definition Creating 2 MTD partitions on "TQM8xxL Bank 1": 0x00000000-0x00200000 : "cramfs" 0x00200000-0x00400000 : "jffs" NET4: Linux TCP/IP 1.0 for NET4.0 IP Protocols: ICMP, UDP, TCP IP: routing cache hash table of 512 buckets, 4Kbytes TCP: Hash tables configured (established 4096 bind 4096) NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. Looking up port of RPC 100003/2 on 10.0.0.2 Looking up port of RPC 100005/2 on 10.0.0.2 VFS: Mounted root (nfs filesystem). Freeing unused kernel memory: 52k init modprobe: modprobe: Can't locate module char-major-4 INIT: version 2.78 booting Activating swap... Checking all file systems... Parallelizing fsck version 1.19 (13-Jul-2000) Mounting local filesystems... not mounted anything Cleaning: /etc/network/ifstate. Setting up IP spoofing protection: rp_filter. Configuring network interfaces: done. Starting portmap daemon: portmap. Cleaning: /tmp /var/lock /var/run. INIT: Entering runlevel: 2 Starting internet superserver: inetd. MontaVista Software's Hard Hat Linux 2.0 tqm login: root PAM-securetty[76]: Couldn't open /etc/securetty PAM_unix[76]: (login) session opened for user root by LOGIN(uid=0) Last login: Fri Feb 1 02:30:32 2030 on console Linux tqm 2.4.4 #1 Sun Apr 7 23:28:08 MEST 2002 ppc unknown login[76]: ROOT LOGIN on `console' root@tqm:~#
7.5. Boot from Flash Memory
The previous section described how to load the Linux kernel image over ethernet using TFTP. This is especially well suited for your development and test environment, when the kernel image is still undergoing frequent changes, for instance because you are modifying kernel code or configuration.
Later in your development cycle you will work on application code or device drivers, which can be loaded dynamically as modules. If the Linux kernel remains the same then you can save the time needed for the TFTP download and put the kernel image into the flash memory of your TQM8xxL board.
The U-Boot command flinfo
can be used to display information about the available on-board flash on your system:
=> fli Bank # 1: FUJITSU AM29LV160B (16 Mbit, bottom boot sect) Size: 4 MB in 35 Sectors Sector Start Addresses: 40000000 (RO) 40008000 (RO) 4000C000 (RO) 40010000 (RO) 40020000 (RO) 40040000 40060000 40080000 400A0000 400C0000 400E0000 40100000 40120000 40140000 40160000 40180000 401A0000 401C0000 401E0000 40200000 40220000 40240000 40260000 40280000 402A0000 402C0000 402E0000 40300000 40320000 40340000 40360000 40380000 403A0000 403C0000 403E0000 Bank # 2: FUJITSU AM29LV160B (16 Mbit, bottom boot sect) Size: 4 MB in 35 Sectors Sector Start Addresses: 40400000 40408000 4040C000 40410000 40420000 40440000 40460000 40480000 404A0000 404C0000 404E0000 40500000 40520000 40540000 40560000 40580000 405A0000 405C0000 405E0000 40600000 40620000 40640000 40660000 40680000 406A0000 406C0000 406E0000 40700000 40720000 40740000 40760000 40780000 407A0000 407C0000 407E0000 =>
From this output you can see the total amount of flash memory, and how it is divided in blocks (Erase Units or Sectors). The RO
markers show blocks of flash memory that are write protected (by software) - this is the area where U-Boot is stored. The remaining flash memory is available for other use.
For instance, we can store the Linux kernel image in flash starting at the start address of the next free flash sector. Before we can do this we must make sure that the flash memory in that region is empty - a Linux kernel image is typically around 600...700 kB, so to be on the safe side we dedicate the whole area from 0x40080000 to 0x4027FFFF for the kernel image. Keep in mind that with flash memory only whole erase units can be cleared.
After having deleted the target flash area, you can download the Linux image and write it to flash. Below is a transcript of the complete operation with a final iminfo
command to check the newly placed Linux kernel image in the flash memory.
Note: Included topic DULGData.tqm8xxlInstallKernelTftp does not exist yet
Note how the filesize
variable (which gets set by the TFTP transfer) is used to automatically adjust for the actual image size.
Now we can boot directly from flash. All we need to do is passing the in-flash address of the image (40080000) with the bootm
command; we also make the definition of the bootargs
variable permanent now:
=> setenv bootcmd bootm 40080000 => setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off
Use printenv
to verify that everything is OK before you save the environment settings:
=> printenv bootdelay=5 baudrate=115200 stdin=serial stdout=serial stderr=serial bootcmd=bootm 40080000 bootargs=root=/dev/nfs rw nfsroot=10.0.0.2:/opt/eldk/ppc_8xx ip=10.0.0.99:10.0.0.2:10.0.0.2:255.0.0.0:tqm::off .... => saveenv
To test booting from flash you can now reset the board (either by power-cycling it, or using the U-Boot command reset
), or you can manually call the boot
command which will run the commands in the bootcmd
variable:
Note: Included topic DULGData.tqm8xxlLinuxBootSelf does not exist yet
7.6. Standalone Operation with Ramdisk Image
When your application development is completed, you usually will want to run your Embedded System standalone, i. e. independent from external resources like NFS filesystems. Instead of mounting the root filesystem from a remote server you can use a compressed ramdisk image, which is stored in flash memory and loaded into RAM when the system boots.
Ramdisk images for tests can be found in the ftp://ftp.denx.de/pub/LinuxPPC/usr/src/SELF/images/ directories.
Load the ramdisk image into RAM and write it to flash as follows:
Note: Included topic DULGData.tqm8xxlUBootInstallRamdisk does not exist yet
To tell the Linux kernel to use the ramdisk image as root filesystem you have to modify the command line arguments passed to the kernel, and to pass two arguments to the bootm
command, the first is the memory address of the Linux kernel image, the second that of the ramdisk image:
Note: Included topic DULGData.tqm8xxlLinuxBootSelf does not exist yet
- 9. Advanced Topics
- 9.1. Flash Filesystems
- 9.2. The TMPFS Virtual Memory Filesystem
- 9.3. Using PC Cards for Flash Disks, CompactFlash, and IDE Harddisks
- 9.4. Adding Swap Space
- 9.5. Splash Screen Support in Linux
- 9.6. Root File System: Design and Building
- 9.7. Root File System Selection
- 9.8. Overlay File Systems
- 9.9. The Persistent RAM File system (PRAMFS)
9. Advanced Topics
This section lists some advanced topics of interest to users of U-Boot and Linux.
9.1. Flash Filesystems
9.1.1. Memory Technology Devices
All currently available flash filesystems are based on the Memory Technology Devices MTD layer, so you must enable (at least) the following configuration options to get flash filesystem support in your system:
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_tqm8xxl=y
Note: this configuration uses CFI conformant AMD flash chips; you may need to adjust these settings on other boards.
The layout of your flash devices ("partitioning") is defined by the mapping routines for your board in the Linux MTD sources (see drivers/mtd/maps/). The configuration for the TQM8xxL looks like this:
/* partition definition for first flash bank * also ref. to "drivers\char\flash_config.c" */ static struct mtd_partition tqm8xxl_partitions[] = { { name: "ppcboot", offset: 0x00000000, size: 0x00020000, /* 128KB */ mask_flags: MTD_WRITEABLE, /* force read-only */ }, { name: "kernel", /* default kernel image */ offset: 0x00020000, size: 0x000e0000, mask_flags: MTD_WRITEABLE, /* force read-only */ }, { name: "user", offset: 0x00100000, size: 0x00100000, }, { name: "initrd", offset: 0x00200000, size: 0x00200000, } }; /* partition definition for second flahs bank */ static struct mtd_partition tqm8xxl_fs_partitions[] = { { name: "cramfs", offset: 0x00000000, size: 0x00200000, }, { name: "jffs", offset: 0x00200000, size: 0x00200000, //size: MTDPART_SIZ_FULL, } };
This splits the available flash memory (8 MB in this case) into 6 separate "partitions":
- uboot: size: 128 kB; used to store the U-Boot firmware
- kernel: size: 896kB; used to store the (compressed) Linux kernel image
- user: size: 1 MB; not used
- initrd: size: 2 MB; used to store a (compressed) ramdisk image
- cramfs: size: 2 MB; used for a compressed ROM filesystem (read-only)
- jffs: size: 2 MB; used for a flash filesystem (using JFFS)
When you boot a system with this configuration you will see the following kernel messages on the console:
Note: Included topic DULGData.tqm8xxlLinuxMtdBoot does not exist yet
Another way to check this information when the system is running is using the proc
filesystem:
Note: Included topic DULGData.tqm8xxlLinuxProcMtd does not exist yet
Now we can run some basic tests to verify that the flash driver routines and the partitioning works as expected:
# xd /dev/mtd0 | head -4 0 27051956 7fe5f641 3be91e9d 0008061f |' V A; | 10 00000000 00000000 7667315e 05070201 | vg1^ | 20 4c696e75 782d322e 342e3400 00000000 |Linux-2.4.4 | 30 00000000 00000000 00000000 00000000 | | # xd /dev/mtd1 | head -4 0 27051956 6735cb88 3be79508 000d11bf |' Vg5 ; | 10 00000000 00000000 7d5cbfc8 05070301 | }\ | 20 4170706c 69636174 696f6e20 72616d64 |Application ramd| 30 69736b20 696d6167 65000000 00000000 |isk image | # xd /dev/mtd6 | head -10 0 6a0358f7 626f6f74 64656c61 793d3500 |j X bootdelay=5 | 10 62617564 72617465 3d393630 30006c6f |baudrate=9600 lo| 20 6164735f 6563686f 3d310063 6c6f636b |ads_echo=1 clock| 30 735f696e 5f6d687a 3d310065 74686164 |s_in_mhz=1 ethad| 40 64723d30 303a6362 3a62643a 30303a30 |dr=00:cb:bd:00:0| 50 303a3131 006e6673 61726773 3d736574 |0:11 nfsargs=set| 60 656e7620 626f6f74 61726773 20726f6f |env bootargs roo| 70 743d2f64 65762f6e 66732072 77206e66 |t=/dev/nfs rw nf| 80 73726f6f 743d2428 73657276 65726970 |sroot=$(serverip| 90 293a2428 726f6f74 70617468 29007261 |):$(rootpath) ra| # xd /dev/mtd7 0 ffffffff ffffffff ffffffff ffffffff | | *** same *** 80000
In the tex-dumps of the MTD devices you can identify some strings that verify that we indeed see an U-Boot environment, a Linux kernel, a ramdisk image and an empty partition to play wih.
The last output shows the partition to be empty. We can try write some data into it:
# date >/dev/mtd7 # xd /dev/mtd7 0 57656420 4e6f7620 20372031 353a3339 |Wed Nov 7 15:39| 10 3a313220 4d455420 32303031 0affffff |:12 MET 2001 | 20 ffffffff ffffffff ffffffff ffffffff | | *** same *** 80000 | | # sleep 10 ; date >/dev/mtd7 Last[3] is 3aa73020, datum is 3a343020 date: write error: Input/output error
As you can see it worked the first time. When we tried to write the (new date) again, we got an error. The reason is that the date has changed (probably at least the seconds) and flash memory cannot be simply overwritten - it has to be erased first.
You can use the eraseall
Linux commands to erase a whole MTD partition:
# xd /dev/mtd7 0 57656420 4e6f7620 20372031 353a3339 |Wed Nov 7 15:39| 10 3a303020 4d455420 32303031 0affffff |:00 MET 2001 | 20 ffffffff ffffffff ffffffff ffffffff | | *** same *** 80000 | | # eraseall /dev/mtd7 Erased 512 Kibyte @ 0 -- 100% complete. # xd /dev/mtd7 0 ffffffff ffffffff ffffffff ffffffff | | *** same *** 80000 | | # date >/dev/mtd7 # xd /dev/mtd7 0 57656420 4e6f7620 20372031 353a3432 |Wed Nov 7 15:42| 10 3a313920 4d455420 32303031 0affffff |:19 MET 2001 | 20 ffffffff ffffffff ffffffff ffffffff | | *** same *** 80000
We have now sufficient proof that the MTD layer is working as expected, so we can try creating a flash filesystem.
9.1.2. Journalling Flash File System
At the moment it seems that the Journalling Flash File System JFFS is the best choice for filesystems in flash memory of embedded devices. You must enable the following configuration options to get JFFS support in your system:
CONFIG_JFFS_FS=y CONFIG_JFFS_FS_VERBOSE=0
If the flash device is erased, we can simply mount it, and the creation of the JFFS filesystem is performed automagically.
Note: For simple accesses like direct read or write operations or erasing you use the character device interface (/dev/mtd*) of the MTD layer, while for filesystem operations like mounting we must use the block device interface (/dev/mtdblock*).
# eraseall /dev/mtd2 Erased 4096 Kibyte @ 0 -- 100% complete. # mount -t jffs /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
Now you can access the files in the JFFS filesystem in the /mnt directory.
9.1.3. Second Version of JFFS
Probably even more interesting for embedded systems is the second version of JFFS, JFFS2, since it not only fixes a few design issues with JFFS, but also adds transparent compression, so that you can save a lot of precious flash memory.
The mkfs.jffs2
tool is used to create a JFFS2 filesystem image; it populates the image with files from a given directory. For instance, to create a JFFS2 image for a flash partition of 3 MB total size and to populate it with the files from the /tmp/flashtools directory you would use:
# 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
Note: Especially when you are running time-critical applications on your system you should carefully study if the behaviour of the flash filesystem might have any negative impact on your application. After all, a flash device is not a normal harddisk. This is especially important when your flash filesystem gets full; JFFS2 acts a bit weird then:
- You will note that an increasing amount of CPU time is spent by the filesystem's garbage collection kernel thread.
- Access times to the files on the flash filesystem may increase drastically.
- Attempts to truncate a file (to free space) or to rename it may fail:
... # cp /bin/bash file cp: writing `file': No space left on device # >file bash: file: No space left on device # mv file foo mv: cannot create regular file `foo': No space left on device
You will have to use rm to actually delete a file in this situation.
This is especially critical when you are using the flash filesystem to store log files: when your application detects some abnormal condition and produces lots of log messages (which usually are especially important in this situation) the filesystem may fill up and cause extreme long delays - if your system crashes, the most important messages may never be logged at all.
9.1.4. Compressed ROM Filesystem
In some cases it is sufficent to have read-only access to some files, and if the files are big enough it becomes desirable to use some method of compression. The Compressed ROM Filesystem CramFs might be a solution here.
Please note that CramFs has - beside the fact that it is a read-only filesystem - some severe limitations (like missing support for timestamps, hard links, and 16/32 bit uid/gids), but there are many situations in Embedded Systems where it's still useful.
To create a CramFs filesystem a special tool mkcramfs
is used to create a file which contains the CramFs image. Note that the CramFs filesystem can be written and read only by kernels with PAGE_CACHE_SIZE == 4096, and some versions of the mkcramfs
program may have other restrictions like that the filesystem must be written and read with architectures of the same endianness. Especially the endianness requirement makes it impossible to build the CramFs image on x86 PC host when you want to use it on a PowerPC target. The endianness problem has been fixed in the version of mkcramfs
that comes with the ELDK.
In some cases you can use a target system running with root filesystem mounted over NFS to create the CramFs image on the native system and store it to flash for further use.
Note: The normal version of the mkcramfs
program tries to initialize some entries in the filesystem's superblock with random numbers by reading /dev/random; this may hang permanently on your target because there is not enough input (like mouse movement) to the entropy pool. You may want to use a modified version of mkcramfs
which does not depend on /dev/random.
To create a CramFs image, you put all files you want in the filesystem into one directory, and then use the mkcramfs= program as follows:
$ 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
As you can see, the CramFs image test.cramfs.img takes just 24 kB, while the input directory contained 64 kB of data. Savings of some 60% like in this case are typical CramFs.
Now we write the CramFs image to a partition in flash and test it:
# 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 -rwxr-xr-x 1 wd users 7063 Jan 1 01:00 unlock
Note that all the timestamps in the CramFs filesyste are bogus, and so is for instance the output of the df
command for such filesystems:
# df /mnt Filesystem 1k-blocks Used Available Use% Mounted on /dev/mtdblock3 0 0 0 - /mnt
9.2. The TMPFS Virtual Memory Filesystem
The tmpfs
filesystem, formerly known as shmfs
, is a filesystem keeping all files in virtual memory.
Everything in tmpfs is temporary in the sense that no files will be created on any device. If you unmount a tmpfs instance, everything stored therein is lost.
tmpfs puts everything into the kernel internal caches and grows and shrinks to accommodate the files it contains and is able to swap unneeded pages out to swap space. It has maximum size limits which can be adjusted on the fly via 'mount -o remount ...'
If you compare it to ramfs
(which was the template to create tmpfs
) you gain swapping and limit checking. Another similar thing is the RAM disk (/dev/ram*), which simulates a fixed size hard disk in physical RAM, where you have to create an ordinary filesystem on top. Ramdisks cannot swap and you do not have the possibility to resize them.
9.2.1. Mount Parameters
tmpfs has a couple of mount options:
size
: The limit of allocated bytes for this tmpfs instance. The default is half of your physical RAM without swap. If you oversize your tmpfs instances the machine will deadlock since the OOM handler will not be able to free that memory.nr_blocks
: The same as size, but in blocks of PAGECACHE_SIZE.nr_inodes
: The maximum number of inodes for this instance. The default is half of the number of your physical RAM pages.
These parameters accept a suffix k, m or g for kilo, mega and giga and can be changed on remount.
To specify the initial root directory you can use the following mount options:
mode
: The permissions as an octal numberuid
: The user idgid
: The group id
These options do not have any effect on remount. You can change these parameters with chmod(1)
, chown(1)
and chgrp(1)
on a mounted filesystem.
So the following mount command will give you a tmpfs instance on /mytmpfs which can allocate 12MB of RAM/SWAP and it is only accessible by root.
mount -t tmpfs -o size=12M,mode=700 tmpfs /mytmpfs
9.2.2. Kernel Support for tmpfs
In order to use a tmpfs
filesystem, the CONFIG_TMPFS
option has to be enabled for your kernel configuration. It can be found in the Filesystems
configuration group. You can simply check if a running kernel supports tmpfs
by searching the contents of /proc/fileysystems:
bash# grep tmpfs /proc/filesystems nodev tmpfs bash#
9.2.3. Usage of tmpfs in Embedded Systems
In embedded systems tmpfs
is very well suited to provide read and write space (e.g. /tmp and /var) for a read-only root file system such as CramFs described in section 9.1.4. Compressed ROM Filesystem. One way to achieve this is to use symbolic links. The following code could be part of the startup file /etc/rc.sh of the read-only ramdisk:
#!/bin/sh ... # Won't work on read-only root: mkdir /tmpfs mount -t tmpfs tmpfs /tmpfs mkdir /tmpfs/tmp /tmpfs/var # Won't work on read-only root: ln -sf /tmpfs/tmp /tmpfs/var / ...
The commented out sections will of course fail on a read-only root filesystem, so you have to create the /tmpfs
mount-point and the symbolic links in your root filesystem beforehand in order to successfully use this setup.
9.3. Using PC Cards for Flash Disks, CompactFlash, and IDE Harddisks
If your board is equipped with a PC-Card adapter (also known as PCMCIA adapter) you can use this for miscellaneous types of mass storage devices like Flash Disks, CompactFlash, and IDE Harddisks.
Please note that there are other options to operate such devices on Embedded PowerPC Systems (for instace you can use the PCMCIA controller builtin to the MPC8xx CPUs to build a direct IDE interface, or you can use some external controller to provide such an interface). The following description does not cover such configurations. Only the solution which uses a standard PC Card Slot is described here.
9.3.1. PC Card Support in U-Boot
When PC Card support is enabled in your U-Boot configuration the target will try to detect any PC Cards in the slot when booting. If no card is present you will see a message like this:
PPCBoot 1.1.1 (Nov 11 2001 - 18:06:06) CPU: XPC862PZPnn0 at 48 MHz: 16 kB I-Cache 8 kB D-Cache FEC present Board: ICU862 Board DRAM: 32 MB FLASH: 16 MB In: serial Out: serial Err: serial PCMCIA: No Card found
Depending on the type of PC Card inserted the boot messages vary; for instance with a Flash Disk card you would see:
... PCMCIA: 3.3V card found: SunDisk SDP 5/3 0.6 Fixed Disk Card IDE interface [silicon] [unique] [single] [sleep] [standby] [idle] [low power] Bus 0: OK Device 0: Model: SanDisk SDP3B-8 Firm: Vdd 1.02 Ser#: fq9bu499900 Type: Removable Hard Disk Capacity: 7.7 MB = 0.0 GB (15680 x 512) ...
With a CompactFlash Card you get:
... PCMCIA: 3.3V card found: CF 128MB CH Fixed Disk Card IDE interface [silicon] [unique] [single] [sleep] [standby] [idle] [low power] Bus 0: OK Device 0: Model: CF 128MB Firm: Rev 1.01 Ser#: 1969C32AA0210002 Type: Removable Hard Disk Capacity: 122.3 MB = 0.1 GB (250368 x 512) ...
Even more exotic memory devices (like the "MemoryStick as used in some Digital Cameras") will usually work without problems:
... PCMCIA: 3.3V card found: SONY MEMORYSTICK(128M) 1.0 Fixed Disk Card IDE interface [silicon] [unique] [single] [sleep] [standby] [idle] [low power] Bus 0: .OK Device 0: Model: MEMORYSTICK 128M 16K Firm: SONY1.00` Ser#: Type: Removable Hard Disk Capacity: 123.8 MB = 0.1 GB (253696 x 512) ...
And with a harddisk adapter you would see:
... PCMCIA: 5.0V card found: ARGOSY PnPIDE D5 Bus 0: OK Device 0: Model: IBM-DKLA-24320 Firm: KL4AA43A Ser#: YD2YD246800 Type: Hard Disk Capacity: 4126.10 MB = 4.0 GB (8452080 x 512) ...
Note that most other cards will be detected by U-Boot, but not supported otherwise, for instance:
... PCMCIA: 5.0V card found: ELSA AirLancer MC-11 Version 01.01 Network Adapter Card ...
or
... PCMCIA: 5.0V card found: Elsa MicroLink 56k MC Internet 021 A Serial Port Card ...
9.3.2. PC Card Support in Linux
The standard way to use PC Cards in a Linux system is to install the "PCMCIA Card Services" package. This is a quite complex set of kernel modules and tools that take care of things like automatic detection and handling of "card insert" or "remove" events, identification of the inserted cards, loading the necessary device drivers, etc. This is a very powerful package, but for embedded applications it has several serious disadvantages:
- Memory footprint - the package consists of a lot of tools and modules that take a lot of space both in the root filesystem and in system RAM when running
- Chicken and Egg Problem - the package loads the needed device drivers as kernel modules, so it needs a root filesystem on another device; that means that you cannot easily put the root filesystem on a PC Card.
For "disk" type PC Cards (FlashDisks, CompactFlash, Hard Disk Adapters - basicly anything that looks like an ordinary IDE drive) an alternative solution is available: direct support within the Linux kernel. This has the big advantage of minimal memory footprint, but of course it comes with a couple of disadvantages, too:
- It works only with "disk" type PC Cards - no support for modems, network cards, etc; for these you still need the PCMCIA Card Services package.
- There is no support for "hot plug", i. e. you cannot insert or remove the card while Linux is running. (Well, of course you
can
do this, but either you willnot be able to access any card inserted, or when you remove a card you will most likely crash the system. Don't do it - you have been warned!) - The code relies on initialization of the PCMCIA controller by the firmware (of course U-Boot will do exactly what's required).
On the other hand these are no real restrictions for use in an Embedded System.
To enable the "direct IDE support" you have to select the following Linux kernel configuration options:
CONFIG_IDE=y CONFIG_BLK_DEV_IDE=y CONFIG_BLK_DEV_IDEDISK=y CONFIG_IDEDISK_MULTI_MODE=y CONFIG_BLK_DEV_MPC8xx_IDE=y CONFIG_BLK_DEV_IDE_MODES=y
and, depending on which partition types and languages you want to support:
CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y CONFIG_MSDOS_PARTITION=y CONFIG_NLS=y CONFIG_NLS_DEFAULT="y" CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_15=y
With these options you will see messages like the following when you boot the Linux kernel:
... Uniform Multi-Platform E-IDE driver Revision: 6.31 ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx PCMCIA slot B: phys mem e0000000...ec000000 (size 0c000000) Card ID: CF 128MB CH Fixed Disk Card IDE interface [silicon] [unique] [single] [sleep] [standby] [idle] [low power] hda: probing with STATUS(0x50) instead of ALTSTATUS(0x41) hda: CF 128MB, ATA DISK drive ide0 at 0xc7000320-0xc7000327,0xc3000106 on irq 13 hda: 250368 sectors (128 MB) w/16KiB Cache, CHS=978/8/32 Partition check: hda: hda1 hda2 hda3 hda4 ...
You can now access your PC Card "disk" like any normal IDE drive. If you start with a new drive, you have to start by creating a new partition table. For PowerPC systems, there are two commonly used options:
9.3.2.1. Using a MacOS Partition Table
A MacOS partition table is the "native" partition table format on PowerPC systems; most desktop PowerPC systems use it, so you may prefer it when you have PowerPC development systems around.
To format your "disk" drive with a MacOS partition table you can use the pdisk
command:
We start printing the help menu, re-initializing the partition table and then printing the new, empty partition table so that we know the block numbers when we want to create new partitions:
# pdisk /dev/hda Edit /dev/hda - Command (? for help): ? Notes: Base and length fields are blocks, which vary in size between media. The base field can be <nth>p; i.e. use the base of the nth partition. The length field can be a length followed by k, m, g or t to indicate kilo, mega, giga, or tera bytes; also the length can be <nth>p; i.e. use the length of the nth partition. The name of a partition is descriptive text. Commands are: h help p print the partition table P (print ordered by base address) i initialize partition map s change size of partition map c create new partition (standard MkLinux type) C (create with type also specified) n (re)name a partition d delete a partition r reorder partition entry in map w write the partition table q quit editing (don't save changes) Command (? for help): i map already exists do you want to reinit? [n/y]: y Command (? for help): p Partition map (with 512 byte blocks) on '/dev/hda' #: type name length base ( size ) 1: Apple_partition_map Apple 63 @ 1 2: Apple_Free Extra 1587536 @ 64 (775.2M) Device block size=512, Number of Blocks=1587600 (775.2M) DeviceType=0x0, DeviceId=0x0
At first we create two small partitions that will be used to store a Linux boot image; a compressed Linux kernel is typically around 400 ... 500 kB, so chosing a partition size of 2 MB is more than generous. 2 MB coresponds to 4096 disk blocks of 512 bytes each, so we enter:
Command (? for help): C First block: 64 Length in blocks: 4096 Name of partition: boot0 Type of partition: PPCBoot Command (? for help): p Partition map (with 512 byte blocks) on '/dev/hda' #: type name length base ( size ) 1: Apple_partition_map Apple 63 @ 1 2: PPCBoot boot0 4096 @ 64 ( 2.0M) 3: Apple_Free Extra 1583440 @ 4160 (773.2M) Device block size=512, Number of Blocks=1587600 (775.2M) DeviceType=0x0, DeviceId=0x0
To be able to select between two kernel images (for instance when we want to do a field upgrade of the Linux kernel) we create a second boot partition of exactly the same size:
Command (? for help): C First block: 4160 Length in blocks: 4096 Name of partition: boot1 Type of partition: PPCBoot Command (? for help): p Partition map (with 512 byte blocks) on '/dev/hda' #: type name length base ( size ) 1: Apple_partition_map Apple 63 @ 1 2: PPCBoot boot0 4096 @ 64 ( 2.0M) 3: PPCBoot boot1 4096 @ 4160 ( 2.0M) 4: Apple_Free Extra 1579344 @ 8256 (771.2M) Device block size=512, Number of Blocks=1587600 (775.2M) DeviceType=0x0, DeviceId=0x0
Now we create a swap partition - 64 MB should be more than sufficient for our Embedded System; 64 MB means 64*1024*2 = 131072 disk blocks of 512 bytes:
Command (? for help): C First block: 8256 Length in blocks: 131072 Name of partition: swap Type of partition: swap Command (? for help): p Partition map (with 512 byte blocks) on '/dev/hda' #: type name length base ( size ) 1: Apple_partition_map Apple 63 @ 1 2: PPCBoot boot0 4096 @ 64 ( 2.0M) 3: PPCBoot boot1 4096 @ 4160 ( 2.0M) 4: swap swap 131072 @ 8256 ( 64.0M) 5: Apple_Free Extra 1448272 @ 139328 (707.2M) Device block size=512, Number of Blocks=1587600 (775.2M) DeviceType=0x0, DeviceId=0x0
Finally, we dedicate all the remaining space to the root partition:
Command (? for help): C First block: 139328 Length in blocks: 1448272 Name of partition: root Type of partition: Linux Command (? for help): p Partition map (with 512 byte blocks) on '/dev/hda' #: type name length base ( size ) 1: Apple_partition_map Apple 63 @ 1 2: PPCBoot boot0 4096 @ 64 ( 2.0M) 3: PPCBoot boot1 4096 @ 4160 ( 2.0M) 4: swap swap 131072 @ 8256 ( 64.0M) 5: Linux root 1448272 @ 139328 (707.2M) Device block size=512, Number of Blocks=1587600 (775.2M) DeviceType=0x0, DeviceId=0x0
To make our changes permanent we must write the new partition table to the disk, before we quit the pdisk
program:
Command (? for help): w Writing the map destroys what was there before. Is that okay? [n/y]: y hda: [mac] hda1 hda2 hda3 hda4 hda5 hda: [mac] hda1 hda2 hda3 hda4 hda5 Command (? for help): q
Now we can initialize the swap space and the filesystem:
# mkswap /dev/hda4 Setting up swapspace version 1, size = 67104768 bytes # mke2fs /dev/hda5 mke2fs 1.19, 13-Jul-2000 for EXT2 FS 0.5b, 95/08/09 Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) 90624 inodes, 181034 blocks 9051 blocks (5.00%) reserved for the super user First data block=0 6 block groups 32768 blocks per group, 32768 fragments per group 15104 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840 Writing inode tables: done Writing superblocks and filesystem accounting information: done
9.3.2.2. Using a MS-DOS Partition Table
The MS-DOS partition table is especially common on PC type computers, which these days means nearly everywhere. You will prefer this format if you want to exchange your "disk" media with any PC type host system.
The fdisk
command is used to create MS-DOS type partition tables; to create the same partitioning scheme as above you would use the following commands:
# fdisk /dev/hda Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel Building a new DOS disklabel. Changes will remain in memory only, until you decide to write them. After that, of course, the previous content won't be recoverable. The number of cylinders for this disk is set to 1575. There is nothing wrong with that, but this is larger than 1024, and could in certain setups cause problems with: 1) software that runs at boot time (e.g., old versions of LILO) 2) booting and partitioning software from other OSs (e.g., DOS FDISK, OS/2 FDISK) Command (m for help): m Command action a toggle a bootable flag b edit bsd disklabel c toggle the dos compatibility flag d delete a partition l list known partition types m print this menu n add a new partition o create a new empty DOS partition table p print the partition table q quit without saving changes s create a new empty Sun disklabel t change a partition's system id u change display/entry units v verify the partition table w write table to disk and exit x extra functionality (experts only)
Command (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 1 First cylinder (1-1575, default 1): Using default value 1 Last cylinder or +size or +sizeM or +sizeK (1-1575, default 1575): +2M Command (m for help): p Disk /dev/hda: 16 heads, 63 sectors, 1575 cylinders Units = cylinders of 1008 * 512 bytes Device Boot Start End Blocks Id System /dev/hda1 1 5 2488+ 83 Linux
Command (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 2 First cylinder (6-1575, default 6): Using default value 6 Last cylinder or +size or +sizeM or +sizeK (6-1575, default 1575): +2M Command (m for help): p Disk /dev/hda: 16 heads, 63 sectors, 1575 cylinders Units = cylinders of 1008 * 512 bytes Device Boot Start End Blocks Id System /dev/hda1 1 5 2488+ 83 Linux /dev/hda2 6 10 2520 83 Linux
Command (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 3 First cylinder (11-1575, default 11): Using default value 11 Last cylinder or +size or +sizeM or +sizeK (11-1575, default 1575): +64M Command (m for help): t Partition number (1-4): 3 Hex code (type L to list codes): 82 Changed system type of partition 3 to 82 (Linux swap) Command (m for help): p Disk /dev/hda: 16 heads, 63 sectors, 1575 cylinders Units = cylinders of 1008 * 512 bytes Device Boot Start End Blocks Id System /dev/hda1 1 5 2488+ 83 Linux /dev/hda2 6 10 2520 83 Linux /dev/hda3 11 141 66024 82 Linux swap
Note that we had to use the t
command to mark this partition as swap space.
Command (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 4 First cylinder (142-1575, default 142): Using default value 142 Last cylinder or +size or +sizeM or +sizeK (142-1575, default 1575): Using default value 1575 Command (m for help): p Disk /dev/hda: 16 heads, 63 sectors, 1575 cylinders Units = cylinders of 1008 * 512 bytes Device Boot Start End Blocks Id System /dev/hda1 1 5 2488+ 83 Linux /dev/hda2 6 10 2520 83 Linux /dev/hda3 11 141 66024 82 Linux swap /dev/hda4 142 1575 722736 83 Linux
Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. hda: hda1 hda2 hda3 hda4 hda: hda1 hda2 hda3 hda4 WARNING: If you have created or modified any DOS 6.x partitions, please see the fdisk manual page for additional information. Syncing disks.
Now we are ready to initialize the partitions:
# mkswap /dev/hda3 Setting up swapspace version 1, size = 67604480 bytes # mke2fs /dev/hda4 mke2fs 1.19, 13-Jul-2000 for EXT2 FS 0.5b, 95/08/09 Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) 90432 inodes, 180684 blocks 9034 blocks (5.00%) reserved for the super user First data block=0 6 block groups 32768 blocks per group, 32768 fragments per group 15072 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840 Writing inode tables: done Writing superblocks and filesystem accounting information: done
9.3.3. Using PC Card "disks" with U-Boot and Linux
U-Boot provides only basic functionality to access PC Card based "disks": you can print the partition table and read and write blocks (addressed by absolute block number), but there is no support to create new partitions or to read files from any type of filesystem.
[Such features could be easily added as U-Boot extensions aka "standalone programs", but so far it has not been implemented yet.]
As usual, you can get some information about the available IDE commands using the help
command in U-Boot:
=> help ide ide reset - reset IDE controller ide info - show available IDE devices ide device [dev] - show or set current device ide part [dev] - print partition table of one or all IDE devices ide read addr blk# cnt ide write addr blk# cnt - read/write `cnt' blocks starting at block `blk#' to/from memory address `addr'
That means you will have to partition the "disk" on your host system; U-Boot can be configured for DOS and MacOS type partition tables. Since U-Boot cannot read files from a filesystem you should create one (or more) small partitions (maybe 1 MB or so) if you want to boot from the "disk".
For example on a 128 MB CompactFlash card we could create the following partiton table under Linux:
# fdisk /dev/hda hda: hda1 hda2 hda3 hda4 Command (m for help): p Disk /dev/hda: 8 heads, 32 sectors, 978 cylinders Units = cylinders of 256 * 512 bytes Device Boot Start End Blocks Id System /dev/hda1 1 17 2160 83 Linux /dev/hda2 18 34 2176 83 Linux /dev/hda3 35 803 98432 83 Linux /dev/hda4 804 978 22400 82 Linux swap Command (m for help): q # mkswap /dev/hda4 Setting up swapspace version 1, size = 22933504 bytes
Here we have two small boot partitions (/dev/hda1 and /dev/hda2, 2 MB each), one big partition to hold a filesystem (/dev/hda3, 99 MB), and a swap partition (/dev/hda4, 22 MB). We also initialized /dev/hda4 as swap space.
U-Boot will recognize this partition table as follows:
=> ide part Partition Map for IDE device 0 -- Partition Type: DOS Partition Start Sector Num Sectors Type 1 32 4320 83 2 4352 4352 83 3 8704 196864 83 4 205568 44800 82
We can now load a Linux kernel image over ethernet and store it both of the boot partitions:
=> tftp 100000 /tftpboot/uImage ARP broadcast 1 TFTP from server 10.0.0.2; our IP address is 10.0.0.99 Filename '/tftpboot/uImage'. Load address: 0x100000 Loading: ################################################################# ############################################## done Bytes transferred = 566888 (8a668 hex) => ide write 100000 0x20 0x800 IDE write: device 0 block # 32, count 2048 ... 2048 blocks written: OK => ide write 100000 0x1100 0x800 IDE write: device 0 block # 4352, count 2048 ... 2048 blocks written: OK
This requires a little more explanation: as you can see from the output of the help ide
command, the write
subcommand takes 3 arguments: a memory address from where the data are read, an (absolute) block number on the disk where the writing starts, and a number of disk blocks.
Since U-Boot expects all input in hex notation we have to perform some calculation: partition 1 starts at block (or sector) number 32, which is 0x20; partition 2 starts at block number 4352 = 0x1100.
We used a block count of 0x800 = 2048 in both cases - this means we wrote 2048 block of 512 bytes each, or a 1024 kB - much more than the actual size of the LInux kernel image - but the partition is big enough and we are on the safe side, so we didn't bother to calculate the exact block count.
To boot from a disk you can use the diskboot
command:
=> help diskboot diskboot loadAddr dev:part
The diskboot
command (or short disk
) expects a load address in RAM, and a combination of device and partition numbers, separated by a colon. It then reads the image from disk and stores it in memory. We can now boot it using the bootm
command [to automatically boot the image define the U-Boot environment autostart
with the value =yes=].
=> disk 400000 0:1 Loading from IDE device 0, partition 1: Name: hda1 Type: PPCBoot Image Name: Linux-2.4.4 Created: 2001-11-11 18:11:11 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 566824 Bytes = 553 kB = 0 MB Load Address: 00000000 Entry Point: 00000000 => bootm 400000 ## Booting image at 00400000 ... Image Name: Linux-2.4.4 Created: 2001-11-11 18:11:11 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 566824 Bytes = 553 kB = 0 MB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Linux version 2.4.4 (wd@denx.denx.de) (gcc version 2.95.2 19991024 (release)) #1 Sun Nov 11 19:05:47 MET 2001 On node 0 totalpages: 8192 ...
We can use the same method that we used to store a Linux kernel image to a disk partition to load a filesystem image into another partiton - as long as the image fits into physical RAM - but usually it's easier to initialize the filesystem either on the host system (swapping the PC Card between host and target is easy enough), or you can use the configuration with root filesystem over NFS to populate the filesystem on the target.
You only have to set the bootargs
variable to boot Linux with root filesystem on disk, for instance:
=> setenv bootargs root=/dev/hda3 => setenv autostart yes => disk 400000 0:1 Loading from IDE device 0, partition 1: Name: hda1 Type: PPCBoot Image Name: Linux-2.4.4 Created: 2001-11-11 18:11:11 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 566824 Bytes = 553 kB = 0 MB Load Address: 00000000 Entry Point: 00000000 Automatic boot of image at addr 0x00400000 ... ## Booting image at 00400000 ... Image Name: Linux-2.4.4 Created: 2001-11-11 18:11:11 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 566824 Bytes = 553 kB = 0 MB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Linux version 2.4.4 (wd@denx.denx.de) (gcc version 2.95.2 19991024 (release)) #1 Sun Nov 11 19:05:47 MET 2001 On node 0 totalpages: 8192 zone(0): 8192 pages. zone(1): 0 pages. zone(2): 0 pages. Kernel command line: root=/dev/hda3 ip=10.0.0.99:10.0.0.2::255.0.0.0:tqm::off panic=1 Decrementer Frequency: 3000000 Calibrating delay loop... 47.82 BogoMIPS Memory: 30548k available (1088k kernel code, 488k data, 48k init, 0k highmem) Dentry-cache hash table entries: 4096 (order: 3, 32768 bytes) Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes) Page-cache hash table entries: 8192 (order: 3, 32768 bytes) Inode-cache hash table entries: 2048 (order: 2, 16384 bytes) POSIX conformance testing by UNIFIX Linux NET4.0 for Linux 2.4 Based upon Swansea University Computer Society NET3.039 Starting kswapd v1.8 CPM UART driver version 0.03 ttyS0 on SMC1 at 0x0280, BRG1 ttyS1 on SMC2 at 0x0380, BRG2 pty: 256 Unix98 ptys configured block: queued sectors max/low 20226kB/6742kB, 64 slots per queue RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize Uniform Multi-Platform E-IDE driver Revision: 6.31 ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx PCMCIA slot B: phys mem e0000000...ec000000 (size 0c000000) Card ID: CF 128MB CH Fixed Disk Card IDE interface [silicon] [unique] [single] [sleep] [standby] [idle] [low power] hda: probing with STATUS(0x50) instead of ALTSTATUS(0x41) hda: CF 128MB, ATA DISK drive ide0 at 0xc7000320-0xc7000327,0xc3000106 on irq 13 hda: 250368 sectors (128 MB) w/16KiB Cache, CHS=978/8/32 Partition check: hda: hda1 hda2 hda3 hda4 eth0: FEC ENET Version 0.2, FEC irq 3, MII irq 4, addr 00:cb:bd:00:00:11 JFFS version 1.0, (C) 1999, 2000 Axis Communications AB Amd/Fujitsu Extended Query Table v1.1 at 0x0040 number of JEDEC chips: 1 ICU862 flash bank 0: Using static image partition definition Creating 8 MTD partitions on "ICU862 Bank 0": 0x00000000-0x00100000 : "kernel" 0x00100000-0x00400000 : "initrd" 0x00400000-0x00800000 : "jffs" 0x00800000-0x00c00000 : "cramfs" 0x00c00000-0x00f00000 : "jffs2" 0x00f00000-0x00f40000 : "ppcboot" 0x00f40000-0x00f80000 : "environment" 0x00f80000-0x01000000 : "spare" NET4: Linux TCP/IP 1.0 for NET4.0 IP Protocols: ICMP, UDP, TCP, IGMP IP: routing cache hash table of 512 buckets, 4Kbytes TCP: Hash tables configured (established 2048 bind 2048) NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. hda: hda1 hda2 hda3 hda4 hda: hda1 hda2 hda3 hda4 VFS: Mounted root (ext2 filesystem) readonly. Freeing unused kernel memory: 48k init init started: BusyBox v0.51 (2001.11.06-02:06+0000) multi-call binary BusyBox v0.51 (2001.11.06-02:06+0000) Built-in shell (lash) Enter 'help' for a list of built-in commands. #
9.4. Adding Swap Space
If you are running out of system RAM, you can add virtual memory by using swap space. If you reserved a swap partition on your disk drive, you have to initialize it once using the mkswap
command:
# fdisk -l /dev/hda Disk /dev/hda: 16 heads, 63 sectors, 1575 cylinders Units = cylinders of 1008 * 512 bytes Device Boot Start End Blocks Id System /dev/hda1 1 5 2488+ 83 Linux /dev/hda2 6 10 2520 83 Linux /dev/hda3 11 141 66024 82 Linux swap /dev/hda4 142 1575 722736 83 Linux # mkswap /dev/hda3 Setting up swapspace version 1, size = 67604480 bytes
Then, to activate it, you use the swapon
command like this:
# free total used free shared buffers cached Mem: 14628 14060 568 8056 100 11664 -/+ buffers/cache: 2296 12332 Swap: 0 0 0 # free total used free shared buffers cached Mem: 14628 14060 568 8056 100 11664 -/+ buffers/cache: 2296 12332 Swap: 0 0 0 # swapon /dev/hda3 Adding Swap: 66016k swap-space (priority -2) # free total used free shared buffers cached Mem: 14628 14084 544 8056 100 11648 -/+ buffers/cache: 2336 12292 Swap: 66016 0 66016
If you forgot to reserve (sufficient) space in a separate partition on your disk, you can still use an ordinary file for swap space. You only have to create a file of appropriate size, and initialize it as follows:
# mount /dev/hda4 /mnt # df Filesystem 1k-blocks Used Available Use% Mounted on /dev/root 2087212 1378824 708388 67% / /dev/hda4 711352 20 675196 1% /mnt # dd if=/dev/zero of=/mnt/swapfile bs=1024k count=64 64+0 records in 64+0 records out # mkswap /mnt/swapfile Setting up swapspace version 1, size = 67104768 bytes
Then activate it:
# free total used free shared buffers cached Mem: 14628 14084 544 6200 96 11788 -/+ buffers/cache: 2200 12428 Swap: 0 0 0 # swapon /mnt/swapfile Adding Swap: 65528k swap-space (priority -3) # free total used free shared buffers cached Mem: 14628 14084 544 6200 96 11752 -/+ buffers/cache: 2236 12392 Swap: 65528 0 65528
9.5. Splash Screen Support in Linux
To complement the U-Boot Splash Screen feature the new configuration option "CONFIG_8xx_PRE_INIT_FB"
was added to the Linux kernel. This allows the Linux kernel to skip certain parts of the framebuffer initialization and to reuse the framebuffer contents that was set up by the U-Boot firmware. This allows to have an image displayed nearly immediately after power-on, so the delay needed to boot the Linux kernel is masked to the user.
The current implementation has some limitations:
- We did not succeed in reusing the previously allocated framebuffer contents directly. Instead, Linux will allocate a new framebuffer, copy the contents, and then switch the display. This adds a minimal delay to the boot time, but is otherwise invisible to the user.
- Linux manages its own colormap, and we considered it too much effort to keep the same settings as used by U-Boot. Instead we use the "trick" that U-Boot will fill the color map table backwards (top down). This works pretty well for images which use no more than 200...255 colors. If the images uses more colors, a bad color mapping may result.
We strongly recommend to convert all images that will be loaded as Linux splash screens to use no more than 225 colors. The"ppmquant"
tool can be used for this purpose (see Bitmap Support in U-Boot for details). - Usually there will be a Linux device driver that is used to adjust the brightness and contrast of the display. When this driver starts, a visible change of brightness will happen if the default settings as used by U-Boot differ.
We recommend to store settings of brightness and contrast in U-Boot environment variables that can be shared between U-Boot and Linux. This way it is possible (assuming adequate driver support) to adjust the display settings correctly already in U-Boot and thus to avoid any flicker of the display when Linux takes over control.
9.6. Root File System: Design and Building
It is not an easy task to design the root file system for an embedded system. There are three major problems to be solved:
- what to put in it
- which file system type to use
- where to store and how to boot it
For now we will assume that the contents of the root file system is aready known; for example, it is given to us as a directory tree or a tarball which contains all the required files.
We will also assume that our system is a typical resource-limited embedded system so we will especially look for solutions where the root file system can be stored on on-board flash memory or other flash memory based devices like CompactFlash or SD cards, MMC or USB memory sticks.
So our focus here is on the second item: the options we have for chosing a file system type and the consequences this has.
In all cases we will base our experiments on the same content of the root filesystem; we use the images of the SELF (Simple Embedded Linux Framework) that come with the ELDK. In a first step we will transform the SELF images into a tarball to meet the requirements mentioned above:
In a ELDK installation, the SELF images can be found in the /opt/eldk/<architecture>/images/
directory. There is already a compressed ramdisk image in this directory, which we will use (ramdisk_image.gz
):
- Uncompress ramdisk image:
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%
Note: The following steps require root permissions! - Mount ramdisk image:
bash# mount -o loop /tmp/ramdisk_image /mnt/tmp
- Create tarball; to avoid the need for
root
permissions in the following steps we don't include the device files in our tarball:bash# cd /mnt/tmp bash# tar -zc --exclude='dev/*' -f /tmp/rootfs.tar.gz *
- Instead, we create a separate tarball which contains only the device entries so we can use them when necessary (with
cramfs
):bash# tar -zcf /tmp/devices.tar.gz dev/ bash# cd /tmp
- Unmount ramdisk image:
bash# umount /mnt/tmp
We will use the /tmp/rootfs.tar.gz
tarball as master file in all following experiments.
9.6.1. Root File System on a Ramdisk
Ram disks are used very often to hold the root file system of embedded systems. They have several advantages:
- well-known
- well-supported by the Linux kernel
- simple to build
- simple to use - you can even combine the ramdisk with the Linux kernel into a single image file
- RAM based, thus pretty fast
- writable file system
- original state of file system after each reboot = easy recovery from accidental or malicious data corruption etc.
On the other hand, there are several disadvantages, too:
- big memory footprint: you always have to load the complete filesystem into RAM, even if only small parts of are actually used
- slow boot time: you have to load (and uncompress) the whole image before the first application process can start
- only the whole image can be replaced (not individual files)
- additional storage needed for writable persistent data
Actually there are only very few situations where a ramdisk image is the optimal solution. But because they are so easy to build and use we will discuss them here anyway.
In almost all cases you will use an ext2
file system in your ramdisk image. The following steps are needed to create it:
- Create a directory tree with the content of the target root filesystem. We do this by unpacking our master tarball:
$ mkdir rootfs $ cd rootfs $ tar zxf /tmp/rootfs.tar.gz
- We use the
genext2fs
tool to create the ramdisk image as this allows to use a simple text file to describe which devices shall be created in the generated file system image. That means that noroot
permissions are required at all. We use the following device tablerootfs_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 - - - /dev/full c 640 0 0 1 7 - - - /dev/hda b 640 0 0 3 0 - - - /dev/hda b 640 0 0 3 1 1 1 16 /dev/kmem c 640 0 0 1 2 - - - /dev/mem c 640 0 0 1 1 - - - /dev/mtd c 640 0 0 90 0 0 2 16 /dev/mtdblock b 640 0 0 31 0 0 1 16 /dev/mtdchar c 640 0 0 90 0 0 1 16 /dev/mtdr c 640 0 0 90 1 0 2 16 /dev/nftla b 640 0 0 93 0 - - - /dev/nftla b 640 0 0 93 1 1 1 8 /dev/nftlb b 640 0 0 93 16 - - - /dev/nftlb b 640 0 0 93 17 1 1 8 /dev/null c 640 0 0 1 3 - - - /dev/ptyp c 640 0 0 2 0 0 1 10 /dev/ptypa c 640 0 0 2 10 - - - /dev/ptypb c 640 0 0 2 11 - - - /dev/ptypc c 640 0 0 2 12 - - - /dev/ptypd c 640 0 0 2 13 - - - /dev/ptype c 640 0 0 2 14 - - - /dev/ptypf c 640 0 0 2 15 - - - /dev/ram b 640 0 0 1 0 0 1 2 /dev/ram b 640 0 0 1 1 - - - /dev/rtc c 640 0 0 10 135 - - - /dev/tty c 640 0 0 4 0 0 1 4 /dev/tty c 640 0 0 5 0 - - - /dev/ttyS c 640 0 0 4 64 0 1 8 /dev/ttyp c 640 0 0 3 0 0 1 10 /dev/ttypa c 640 0 0 3 10 - - - /dev/ttypb c 640 0 0 3 11 - - - /dev/ttypc c 640 0 0 3 12 - - - /dev/ttypd c 640 0 0 3 13 - - - /dev/ttype c 640 0 0 3 14 - - - /dev/ttypf c 640 0 0 3 15 - - - /dev/zero c 640 0 0 1 5 - - -
A description of the format of this table is part of the manual page for thegenext2fs
tool, genext2fs(8). - We can now create an
ext2
file system image using thegenext2fs
tool:$ ROOTFS_DIR=rootfs # directory with root file system content $ ROOTFS_SIZE=3700 # size of file system image $ ROOTFS_FREE=100 # free space wanted $ ROOTFS_INODES=380 # number of inodes $ ROOTFS_DEVICES=rootfs_devices.tab # device description file $ ROOTFS_IMAGE=ramdisk.img # generated file system image $ genext2fs -U \ -d ${ROOTFS_DIR} \ -D ${ROOTFS_DEVICES} \ -b ${ROOTFS_SIZE} \ -r ${ROOTFS_FREE} \ -i ${ROOTFS_INODES} \ ${ROOTFS_IMAGE}
- Compress the file system image:
$ gzip -v9 ramdisk.img rootfs.img: 55.6% -- replaced with ramdisk.img.gz
- Create an U-Boot image file from it:
$ 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.
9.6.2. Root File System on a JFFS2 File System
JFFS2 (Journalling Flash File System version 2) was specifically designed for use on flash memory devices in embedded systems. It is a log-structured file system which means that it is robust against loss of power, crashes or other unorderly shutdowns of the system ("robust" means that data that is just being written when the system goes down may be lost, but the file system itself does not get corrupted and the system can be rebootet without need for any kind of file system check).
Some of the advantages of using JFFS2 as root file system in embedded systems are:
- file system uses compression, thus making efficient use of flash memory
- log-structured file system, thus robust against unorderly shutdown
- writable flash file system
Disadvantages are:
- long mount times (especially older versions)
- slow when reading: files to be read get uncompressed on the fly which eats CPU cycles and takes time
- slow when writing: files to be written get compressed, which eats CPU cycles and takes time, but it may even take much longer until data gets actually stored in flash if the file system becomes full and blocks must be erased first or - even worse - if garbage collection becomes necessary
- The garbage collector thread may run at any time, consuming CPU cycles and blocking accesses to the file system.
Despite the aforementioned disadvantages, systems using a JFFS2 based root file system are easy to build, make efficient use of the available resources and can run pretty reliably.
To create a JFFS2 based root file system please proceed as follows:
- Create a directory tree with the content of the target root filesystem. We do this by unpacking our master tarball:
$ mkdir rootfs $ cd rootfs $ tar zxf /tmp/rootfs.tar.gz
- We can now create a
JFFS2
file system image using themkfs.jffs2
tool:$ ROOTFS_DIR=rootfs # directory with root file system content $ ROOTFS_EBSIZE=0x20000 # erase block size of flash memory $ ROOTFS_ENDIAN=b # target system is big endian $ ROOTFS_DEVICES=rootfs_devices.tab # device description file $ ROOTFS_IMAGE=jffs2.img # generated file system image $ mkfs.jffs2 -U \ -d ${ROOTFS_DIR} \ -D ${ROOTFS_DEVICES} \ -${ROOTFS_ENDIAN} \ -e ${ROOTFS_EBSIZE} \ -o ${ROOTFS_IMAGE} mkfs.jffs2: skipping device_table entry '/dev': no parent directory!
Note: When you intend to write the JFFS2
file system image to a NAND flash device, you should also add the "-n"
(or "--no-cleanmarkers"
) option, as cleanmarkers are not needed then.
When booting the Linux kernel prints the following messages showing the default partition map which is used for the flash memory on the TQM8xxL boards:
TQM flash bank 0: Using static image partition definition Creating 7 MTD partitions on "TQM8xxL0": 0x00000000-0x00040000 : "u-boot" 0x00040000-0x00100000 : "kernel" 0x00100000-0x00200000 : "user" 0x00200000-0x00400000 : "initrd" 0x00400000-0x00600000 : "cramfs" 0x00600000-0x00800000 : "jffs" 0x00400000-0x00800000 : "big_fs"
We use U-Boot to load and store the JFFS2 image into the last partition and set up the Linux boot arguments to use this as root device:
- Erase flash:
=> era 40400000 407FFFFF ................. done Erased 35 sectors
- Download JFFS2 image:
=> tftp 100000 /tftpboot/TQM860L/jffs2.img Using FEC ETHERNET device TFTP from server 192.168.3.1; our IP address is 192.168.3.80 Filename '/tftpboot/TQM860L/jffs2.img'. Load address: 0x100000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ######## done Bytes transferred = 2033888 (1f08e0 hex)
- Copy image to flash:
=> cp.b 100000 40400000 ${filesize} Copy to Flash... done
- set up boot arguments to use flash partition 6 as root device:
=> setenv mtd_args setenv bootargs root=/dev/mtdblock6 rw rootfstype=jffs2 => printenv addip addip=setenv bootargs ${bootargs} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}:${netdev}:off panic=1 => setenv flash_mtd 'run mtd_args addip;bootm ${kernel_addr}' => run flash_mtd Using FEC ETHERNET device TFTP from server 192.168.3.1; our IP address is 192.168.3.80 Filename '/tftpboot/TQM860L/uImage'. Load address: 0x200000 Loading: ################################################################# ################################################################# ########### done Bytes transferred = 719233 (af981 hex) ## Booting image at 40040000 ... Image Name: Linux-2.4.25 Created: 2005-06-12 16:32:24 UTC Image Type: PowerPC Linux Kernel Image (gzip compressed) Data Size: 782219 Bytes = 763.9 kB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK Uncompressing Kernel Image ... OK Linux version 2.4.25 (wd@xpert) (gcc version 3.3.3 (DENX ELDK 3.1.1 3.3.3-9)) #1 Sun Jun 12 18:32:18 MEST 2005 On node 0 totalpages: 4096 zone(0): 4096 pages. zone(1): 0 pages. zone(2): 0 pages. Kernel command line: root=/dev/mtdblock6 rw rootfstype=jffs2 ip=192.168.3.80:192.168.3.1::255.255.255.0:tqm860l:eth1:off panic=1 Decrementer Frequency = 187500000/60 Calibrating delay loop... 49.86 BogoMIPS ... NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. VFS: Mounted root (jffs2 filesystem). Freeing unused kernel memory: 56k init BusyBox v0.60.5 (2005.03.07-06:54+0000) Built-in shell (msh) Enter 'help' for a list of built-in commands. # ### Application running ... # mount rootfs on / type rootfs (rw) /dev/mtdblock6 on / type jffs2 (rw) /proc on /proc type proc (rw) # df / Filesystem 1k-blocks Used Available Use% Mounted on rootfs 4096 2372 1724 58% /
9.6.3. Root File System on a cramfs File System
cramfs is a compressed, read-only file system.
Advantages are:
- file system uses compression, thus making efficient use of flash memory
- Allows for quick boot times as only used files get loaded and uncompressed
Disadvantages are:
- only the whole image can be replaced (not individual files)
- additional storage needed for writable persistent data
mkcramfs
tool does not support device table, so we need root permissions to create the required device files
To create a cramfs
based root file system please proceed as follows:
- Create a directory tree with the content of the target root filesystem. We do this by unpacking our master tarball:
$ mkdir rootfs $ cd rootfs $ tar -zxf /tmp/rootfs.tar.gz
- Create the required device files. We do this here by unpacking a special tarball which holds only the device file entries. Note: this requires root permissions!
# cd rootfs # tar -zxf /tmp/devices.tar.gz
- Many tools require some storage place in a filesystem, so we must provide at least one (small) writable filesystem. For all data which may be lost when the system goes down, a
"tmpfs"
filesystem is the optimal choice. To create such a writable tmpfs filesystem we add the following lines to the/etc/rc.sh
script:# mount TMPFS because root-fs is readonly /bin/mount -t tmpfs -o size=2M tmpfs /tmpfs
Some tools require write permissions on some device nodes (for example, to change ownership and permissions), or dynamically (re-) create such files (for example,/dev/log
which is usually a Unix Domain socket). The files are placed in a writable filesystem; in the root filesystem symbolic links are used to point to their new locations:dev/ptyp0 → /tmpfs/dev/ptyp0 dev/ttyp0 → /tmpfs/dev/ttyp0 dev/ptyp1 → /tmpfs/dev/ptyp1 dev/ttyp1 → /tmpfs/dev/ttyp1 dev/ptyp2 → /tmpfs/dev/ptyp2 dev/ttyp2 → /tmpfs/dev/ttyp2 dev/ptyp3 → /tmpfs/dev/ptyp3 dev/ttyp3 → /tmpfs/dev/ttyp3 dev/ptyp4 → /tmpfs/dev/ptyp4 dev/ttyp4 → /tmpfs/dev/ttyp4 dev/ptyp5 → /tmpfs/dev/ptyp5 dev/ttyp5 → /tmpfs/dev/ttyp5 dev/ptyp6 → /tmpfs/dev/ptyp6 dev/ttyp6 → /tmpfs/dev/ttyp6 dev/ptyp7 → /tmpfs/dev/ptyp7 dev/ttyp7 → /tmpfs/dev/ttyp7 dev/ptyp8 → /tmpfs/dev/ptyp8 dev/ttyp8 → /tmpfs/dev/ttyp8 dev/ptyp9 → /tmpfs/dev/ptyp9 dev/ttyp9 → /tmpfs/dev/ttyp9 dev/ptypa → /tmpfs/dev/ptypa dev/ttypa → /tmpfs/dev/ttypa dev/ptypb → /tmpfs/dev/ptypb dev/ttypb → /tmpfs/dev/ttypb dev/ptypc → /tmpfs/dev/ptypc dev/ttypc → /tmpfs/dev/ttypc dev/ptypd → /tmpfs/dev/ptypd dev/ttypd → /tmpfs/dev/ttypd dev/ptype → /tmpfs/dev/ptype dev/ttype → /tmpfs/dev/ttype dev/ptypf → /tmpfs/dev/ptypf dev/ttypf → /tmpfs/dev/ttypf tmp → /tmpfs/tmp var → /tmpfs/var dev/log → /var/log/log In case you use dhclient also: etc/dhclient.conf → /tmpfs/var/lib/dhclient.conf etc/resolv.conf → /tmpfs/var/lib/resolv.conf
To place the corresponding directories and device files in thetmpfs
file system, the following code is added to the/etc/rc.sh
script:mkdir -p /tmpfs/tmp /tmpfs/dev \ /tmpfs/var/lib/dhcp /tmpfs/var/lock /tmpfs/var/run while read name minor do mknod /tmpfs/dev/ptyp$name c 2 $minor mknod /tmpfs/dev/ttyp$name c 3 $minor done <<__EOD__ 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 __EOD__ chmod 0666 /tmpfs/dev/*
- We can now create a
cramfs
file system image using themkcramfs
tool:$ ROOTFS_DIR=rootfs # directory with root file system content $ ROOTFS_ENDIAN="-r" # target system has reversed (big) endianess $ ROOTFS_IMAGE=cramfs.img # generated file system image PATH=/opt/eldk/usr/bin:$PATH mkcramfs ${ROOTFS_ENDIAN} ${DEVICES} ${ROOTFS_DIR} ${ROOTFS_IMAGE} Swapping filesystem endian-ness bin dev etc ... -48.78% (-86348 bytes) in.ftpd -46.02% (-16280 bytes) in.telnetd -45.31% (-74444 bytes) xinetd Everything: 1864 kilobytes Super block: 76 bytes CRC: c166be6d warning: gids truncated to 8 bits. (This may be a security concern.)
- We can use the same setup as before for the JFFS2 filesystem, just changing the bootargument to
"rootfstype=cramfs"
9.6.4. Root File System on a Read-Only ext2 File System
When storing the root file system in on-board flash memory it seems only natural to look for special falsh filesystems like JFFS2, or for other file system types that are designed for such environments like cramfs. It seems to be a bad idea to use a standard ext2
file system because it contains neither any type of wear levelling which is needed for writable file systems in flash memory, nor is it robust against unorderly shutdowns.
The situation changes if we use an ext2
file system which we mount read-only. Such a configuration can be very useful in some situations.
Advantages:
- very fast
- low RAM memory footprint
Disadvantages:
- high flash memory footprint because no compression
To create an ext2
image that can be used as a read-only root file system the following steps are necessary:
- Create a directory tree with the content of the target root filesystem. We do this by unpacking our master tarball:
$ mkdir rootfs $ cd rootfs $ tar -zxf /tmp/rootfs.tar.gz
- Like with the
cramfs
root file system, we use"tmpfs"
for cases where a writable file system is needed and add the following lines to the/etc/rc.sh
script:# mount TMPFS because root-fs is readonly /bin/mount -t tmpfs -o size=2M tmpfs /tmpfs
We also create the same symbolic links for device files that must be placed in a writable filesystem:dev/ptyp0 → /tmpfs/dev/ptyp0 dev/ttyp0 → /tmpfs/dev/ttyp0 dev/ptyp1 → /tmpfs/dev/ptyp1 dev/ttyp1 → /tmpfs/dev/ttyp1 dev/ptyp2 → /tmpfs/dev/ptyp2 dev/ttyp2 → /tmpfs/dev/ttyp2 dev/ptyp3 → /tmpfs/dev/ptyp3 dev/ttyp3 → /tmpfs/dev/ttyp3 dev/ptyp4 → /tmpfs/dev/ptyp4 dev/ttyp4 → /tmpfs/dev/ttyp4 dev/ptyp5 → /tmpfs/dev/ptyp5 dev/ttyp5 → /tmpfs/dev/ttyp5 dev/ptyp6 → /tmpfs/dev/ptyp6 dev/ttyp6 → /tmpfs/dev/ttyp6 dev/ptyp7 → /tmpfs/dev/ptyp7 dev/ttyp7 → /tmpfs/dev/ttyp7 dev/ptyp8 → /tmpfs/dev/ptyp8 dev/ttyp8 → /tmpfs/dev/ttyp8 dev/ptyp9 → /tmpfs/dev/ptyp9 dev/ttyp9 → /tmpfs/dev/ttyp9 dev/ptypa → /tmpfs/dev/ptypa dev/ttypa → /tmpfs/dev/ttypa dev/ptypb → /tmpfs/dev/ptypb dev/ttypb → /tmpfs/dev/ttypb dev/ptypc → /tmpfs/dev/ptypc dev/ttypc → /tmpfs/dev/ttypc dev/ptypd → /tmpfs/dev/ptypd dev/ttypd → /tmpfs/dev/ttypd dev/ptype → /tmpfs/dev/ptype dev/ttype → /tmpfs/dev/ttype dev/ptypf → /tmpfs/dev/ptypf dev/ttypf → /tmpfs/dev/ttypf tmp → /tmpfs/tmp var → /tmpfs/var dev/log → /var/log/log In case you use dhclient also: etc/dhclient.conf → /tmpfs/var/lib/dhclient.conf etc/resolv.conf → /tmpfs/var/lib/resolv.conf
To place the corresponding directories and device files in thetmpfs
file system, the following code is added to the/etc/rc.sh
script:mkdir -p /tmpfs/tmp /tmpfs/dev \ /tmpfs/var/lib/dhcp /tmpfs/var/lock /tmpfs/var/run while read name minor do mknod /tmpfs/dev/ptyp$name c 2 $minor mknod /tmpfs/dev/ttyp$name c 3 $minor done <<__EOD__ 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 __EOD__ chmod 0666 /tmpfs/dev/*
- Like we did for the ramdisk, we now create an
ext2
file system image using thegenext2fs
tool:$ ROOTFS_DIR=rootfs # directory with root file system content $ ROOTFS_SIZE=3700 # size of file system image $ ROOTFS_FREE=100 # free space wanted $ ROOTFS_INODES=380 # number of inodes $ ROOTFS_DEVICES=rootfs_devices.tab # device description file $ ROOTFS_IMAGE=ext2.img # generated file system image $ genext2fs -U \ -d ${ROOTFS_DIR} \ -D ${ROOTFS_DEVICES} \ -b ${ROOTFS_SIZE} \ -r ${ROOTFS_FREE} \ -i ${ROOTFS_INODES} \ ${ROOTFS_IMAGE}
- We can again use the same setup as before for the JFFS2 filesystem, just changing the bootargument to
"rootfstype=ext2"
(or simply omit it completely as this is the default anyway), and we must change the"rw"
argument into"ro"
to mount our root file system really read-only:... Linux version 2.4.25 (wd@xpert) (gcc version 3.3.3 (DENX ELDK 3.1.1 3.3.3-9)) #1 Sun Jun 12 18:32:18 MEST 2005 On node 0 totalpages: 4096 zone(0): 4096 pages. zone(1): 0 pages. zone(2): 0 pages. Kernel command line: root=/dev/mtdblock6 ro rootfstype=ext2 ip=192.168.3.80:192.168.3.1::255.255.255.0:tqm860l:eth1:off panic=1 Decrementer Frequency = 187500000/60 Calibrating delay loop... 49.86 BogoMIPS ...
9.6.5. Root File System on a Flash Card
Using an ext2
file system on a flash memory card (like CompactFlash, SD, MMC or a USB memory stick) is standard technology. To avoid unnecessary flash wear it is a good idea to mount the root file system read-only, or at least using the "noatime"
mount option.
For our test we can use the "ext2.img"
file from the previous step without changes:
- In this test we use a standard CompactFlash card which comes with a single partition on it. We use U-Boot to copy the
ext2
file system image into this partition:=> tftp 100000 /tftpboot/TQM860L/ext2.img Using FEC ETHERNET device TFTP from server 192.168.3.1; our IP address is 192.168.3.80 Filename '/tftpboot/TQM860L/ext2.img'. Load address: 0x100000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ########################## done Bytes transferred = 3788800 (39d000 hex) => ide part Partition Map for IDE device 0 -- Partition Type: DOS Partition Start Sector Num Sectors Type 1 32 500704 6 => ide write 100000 20 1ce8 IDE write: device 0 block # 32, count 7400 ... 7400 blocks written: OK
Note that the"ide write"
command takes parameters as hex numbers, and the write count is in terms of disk blocks of 512 bytes each. So we have to use 0x20 for the starts sector of the first partition, and 3788800 / 512 = 7400 = 0x1CE8 for the block count. - We now prepare the Linux boot arguments to take this partition as read-only root device:
=> setenv cf_args setenv bootargs root=/dev/hda1 ro => setenv flash_cf 'run cf_args addip;bootm ${kernel_addr}' => setenv bootcmd run flash_cf
- ...and boot the system:
... Linux version 2.4.25 (wd@xpert) (gcc version 3.3.3 (DENX ELDK 3.1.1 3.3.3-9)) #1 Sun Jun 12 18:32:18 MEST 2005 On node 0 totalpages: 4096 zone(0): 4096 pages. zone(1): 0 pages. zone(2): 0 pages. Kernel command line: root=/dev/hda1 ro ip=192.168.3.80:192.168.3.1::255.255.255.0:tqm860l:eth1:off panic=1 Decrementer Frequency = 187500000/60 Calibrating delay loop... 49.86 BogoMIPS ...
9.6.6. Root File System in a Read-Only File in a FAT File System
This is a more complicated example that shows that - depending on project requirements - many other alternatives for chosing a root file system for your embedded system exist.
The szenario is as follows: on your embedded device you use a cheap and popular storage medium like CompactFlash, MMC or SD cards or USB memory sticks to store both the Linux kernel and your root file system. You want to distribute software updates over the internet: your customers can download the file from your web site, or you sent the images by email. Your customers may use any flash card or memory stick they happen to find, so you have no information about brand or size of the storage device.
Unfortunately most of your customers use Windows systems. And they don't want to be bothered with long instructions how to create special partitions on the storage device or how to write binary images or things like that. A simple "copy file" operation is nearly exhausting their capabilities.
What to do? Well, if copying a file is all your customers can do we should not ask for more. Storage devices like CompactFlash cards etc. typically come with a single partition on it, which holds a FAT
or VFAT
file system. This cannot be used as a Linux root file system directly, so we have to use some trickery.
Here is one possible solution: Your software distribution consistes of two files: The first file is the Linux kernel with a minimal ramdisk image attached (using the multi-file image format for U-Boot); U-Boot can load and boot such files from a FAT or VFAT file system. The second file is your root file system. For convenience and speed we use again an image of an ext2
file system. When Linux boots, it will initially use the attached ramdisk as root file system. The programs in this ramdisk will mount the FAT or VFAT file system - read-only. Then we can use a loop device (see losetup(8)) to associate the root file system image with a block device which can be used as a mount point. And finally we use pivot_root(8) to change the root file system to our image on the CF card.
This sounds not so complicated, and actually it is quite simple once you understand what needs to be done. Here is a more detailed description:
- The root file system image is easy: as mantioned before, we will use an
ext2
file system image, and to avoid wearing the flash storage device we will use it in read-only mode - we did a read-onlyext2
root file system image before, and here we can just re-use the existing image file. - The initial ramdisk image that performs the pivot_root step must be created from scratch, but we already know how to create ramdisk images, so we just have to figure out what to put in it.
The most important tool here isnash
, a script interpreter that was specifically designed for such purposes (see nash(8)). We don't need any additional tools, and if we use static linking, that thenash
binary plus a small script to control it is all we need for our initial ramdisk.
To be precise, we need a couple of (empty) directories (bin
,dev
,etc
,lib
,loopfs
,mnt
,proc
, andsysroot
), thebin/nash
binary, thelinuxrc
script and a symbolic linksbin
pointing tobin
:drwxr-xr-x 2 wd users 4096 Apr 13 01:11 bin -rwxr-xr-x 1 wd users 469512 Apr 11 22:47 bin/nash drwxr-xr-x 2 wd users 4096 Apr 12 00:04 dev drwxr-xr-x 2 wd users 4096 Apr 12 00:04 etc drwxr-xr-x 2 wd users 4096 Apr 12 00:04 lib -rwxr-xr-x 1 wd users 511 Apr 13 01:28 linuxrc drwxr-xr-x 2 wd users 4096 Apr 12 00:04 loopfs drwxr-xr-x 2 wd users 4096 Apr 12 00:09 mnt drwxr-xr-x 2 wd users 4096 Apr 12 00:04 proc lrwxrwxrwx 1 wd users 3 Jun 12 18:54 sbin -> bin drwxr-xr-x 2 wd users 4096 Apr 12 00:04 sysroot
- We also need only a minimal device table for creating the initial ramdisk:
#<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count> /dev d 755 0 0 - - - - - /dev/console c 640 0 0 5 1 - - - /dev/hda b 640 0 0 3 0 - - - /dev/hda b 640 0 0 3 1 1 1 8 /dev/loop b 640 0 0 7 0 0 1 4 /dev/null c 640 0 0 1 3 - - - /dev/ram b 640 0 0 1 0 0 1 2 /dev/ram b 640 0 0 1 1 - - - /dev/tty c 640 0 0 4 0 0 1 4 /dev/tty c 640 0 0 5 0 - - - /dev/ttyS c 640 0 0 4 64 0 1 4 /dev/zero c 640 0 0 1 5 - - -
- To create the initial ramdisk we perform the usual steps:
$ INITRD_DIR=initrd $ INITRD_SIZE=490 $ INITRD_FREE=0 $ INITRD_INODES=54 $ INITRD_DEVICES=initrd_devices.tab $ INITRD_IMAGE=initrd.img $ genext2fs -U \ -d ${INITRD_DIR} \ -D ${INITRD_DEVICES} \ -b ${INITRD_SIZE} \ -r ${INITRD_FREE} \ -i ${INITRD_INODES} \ ${INITRD_IMAGE} $ gzip -v9 ${INITRD_IMAGE}
The result is a really small (233 kB) compressed ramdisk image. - Assuming you already have your Linux kernel image, you can now use
mkimage
to build an U-Boot multi-file image that combines the Linux kernel and the initial ramdisk:$ LINUX_KERNEL=linuxppc_2_4_devel/arch/ppc/boot/images/vmlinux.gz $ mkimage -A ppc -O Linux -T multi -C gzip \ > -n 'Linux with Pivot Root Helper' \ > -d ${LINUX_KERNEL}:${INITRD_IMAGE}.gz linux.img Image Name: Linux with Pivot Root Helper Created: Mon Jun 13 01:48:11 2005 Image Type: PowerPC Linux Multi-File Image (gzip compressed) Data Size: 1020665 Bytes = 996.74 kB = 0.97 MB Load Address: 0x00000000 Entry Point: 0x00000000 Contents: Image 0: 782219 Bytes = 763 kB = 0 MB Image 1: 238433 Bytes = 232 kB = 0 MB
The newly created filelinux.img
is the second image we have to copy to the CF card.
We are done.
But wait - one essential part was not mentioned yet: the linuxrc
script in our initial ramdisk image which contains all the magic. This script is quite simple:
#!/bin/nash echo Mounting /proc filesystem mount -t proc /proc /proc echo Creating block devices mkdevices /dev echo Creating root device mkrootdev /dev/root echo 0x0100 > /proc/sys/kernel/real-root-dev echo Mounting flash card mount -o noatime -t vfat /dev/hda1 /mnt echo losetup for filesystem image losetup /dev/loop0 /mnt/rootfs.img echo Mounting root filesystem image mount -o defaults --ro -t ext2 /dev/loop0 /sysroot echo Running pivot_root pivot_root /sysroot /sysroot/initrd umount /initrd/proc
Let's go though it step by step:
- The first line says that it's a script file for the
/bin/nash
interpreter.
Note: even if this file looks like a shell script it is NOT interpreted by a shell, but by thenash
interpreter. For a complete list of availablenash
commands and their syntax please refer to the manual page, nash(8). - The first action is to mount the
/proc
pseudo file system which is needed to find out some required information. - Then we create block device entries for all partitions listed in
/proc/partitions
(mkdevices
command). - In the next step a block device for our new root file system is created (
mkrootdev
command). - Then we mount the CF card. We assume that there is only a single partition on it (
/dev/hda1
) which is of typeVFAT
(which also will work withFAT
file systems). These assumptions work fine with basicly all memory devices used under Windows. - We further assume that the file name of the root file system image on the CF card is
"rootfs.img"
- this file now gets mounted using a loop device (losetup
andmount
commands). - Our file system image, is now mounted on the
/sysroot
directory. In the last step we usepivot_root
to make this the new root file system. - As a final cleanup we unmount the
/proc
file system which is not needed any more.
There is one tiny flaw in this method: since we mount the CF card on a directory in the ramdisk to be able to access to root file system image. This means that we cannot unmount the CF card, which in turn prevents us from freeing the space for the inital ramdisk. The consequence is that you permanently lose approx. 450 kB of RAM for the ramdisk. [We could of course re-use this ramdisk space for temporary data, but such optimization is beyond the scope of this document.]
And how does this work on our target?
- First we copy the two images to the CF card; we do this on the target under Linux:
bash-2.05b# fdisk -l /dev/hda Disk /dev/hda: 256 MB, 256376832 bytes 16 heads, 32 sectors/track, 978 cylinders Units = cylinders of 512 * 512 = 262144 bytes Device Boot Start End Blocks Id System /dev/hda1 * 1 978 250352 6 FAT16 bash-2.05b# mkfs.vfat /dev/hda1 mkfs.vfat 2.8 (28 Feb 2001) bash-2.05b# mount -t vfat /dev/hda1 /mnt bash-2.05b# cp -v linux.img rootfs.img /mnt/ `linux.img' -> `/mnt/linux.img' `rootfs.img' -> `/mnt/rootfs.img' bash-2.05b# ls -l /mnt total 4700 -rwxr--r-- 1 root root 1020729 Jun 14 05:36 linux.img -rwxr--r-- 1 root root 3788800 Jun 14 05:36 rootfs.img bash-2.05b# umount /mnt
- We now prepare U-Boot to load the
"uMulti"
file (combined Linux kernel and initial ramdisk) from the CF card and boot it:=> setenv fat_args setenv bootargs rw => setenv fat_boot 'run fat_args addip;fatload ide 0:1 200000 linux.img;bootm' => setenv bootcmd run fat_boot
- And finally we try it out:
U-Boot 1.1.3 (Jun 13 2005 - 02:24:00) CPU: XPC86xxxZPnnD4 at 50 MHz: 4 kB I-Cache 4 kB D-Cache FEC present Board: TQM860LDB0A3-T50.202 DRAM: 16 MB FLASH: 8 MB In: serial Out: serial Err: serial Net: SCC ETHERNET, FEC ETHERNET [PRIME] PCMCIA: 3.3V card found: Transcend 256M Fixed Disk Card IDE interface [silicon] [unique] [single] [sleep] [standby] [idle] [low power] Bus 0: OK Device 0: Model: Transcend 256M Firm: 1.1 Ser#: SSSC256M04Z27A25906T Type: Removable Hard Disk Capacity: 244.5 MB = 0.2 GB (500736 x 512) Type "run flash_nfs" to mount root filesystem over NFS Hit any key to stop autoboot: 0 reading linux.img 1025657 bytes read ## Booting image at 00200000 ... Image Name: Linux with Pivot Root Helper Created: 2005-06-13 0:32:41 UTC Image Type: PowerPC Linux Multi-File Image (gzip compressed) Data Size: 1025593 Bytes = 1001.6 kB Load Address: 00000000 Entry Point: 00000000 Contents: Image 0: 787146 Bytes = 768.7 kB Image 1: 238433 Bytes = 232.8 kB Verifying Checksum ... OK Uncompressing Multi-File Image ... OK Loading Ramdisk to 00f3d000, end 00f77361 ... OK Linux version 2.4.25 (wd@xpert) (gcc version 3.3.3 (DENX ELDK 3.1.1 3.3.3-9)) #1 Mon Jun 13 02:32:10 MEST 2005 On node 0 totalpages: 4096 zone(0): 4096 pages. zone(1): 0 pages. zone(2): 0 pages. Kernel command line: rw ip=192.168.3.80:192.168.3.1::255.255.255.0:tqm860l:eth1:off panic=1 Decrementer Frequency = 187500000/60 Calibrating delay loop... 49.86 BogoMIPS ... NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. RAMDISK: Compressed image found at block 0 Freeing initrd memory: 232k freed VFS: Mounted root (ext2 filesystem). Red Hat nash version 4.1.18 starting Mounting /proc filesystem Creating block devices Creating root device Mounting flash card hda: hda1 hda: hda1 losetup for filesystem image Mounting root filesystem image Running pivot_root Freeing unused kernel memory: 60k init BusyBox v0.60.5 (2005.03.07-06:54+0000) Built-in shell (msh) Enter 'help' for a list of built-in commands. # ### Application running ...
9.7. Root File System Selection
Now we know several options for file systems we can use, and know how to create the corresponding images. But how can we decide which one to chose?
For practical purposes in embedded systems the following criteria are often essential:
- boot time (i. e. time needed from power on until application code is running)
- flash memory footprint
- RAM memory footprint
- effects on software updates
The following data was measured for the different configurations. All measurements were performed on the same TQM860L board (MPC860 CPU at 50 MHz, 16 MB RAM, 8 MB flash, 256 MB CompactFlash card):
File System Type | Boot Time | Free Mem | Updates | while running |
---|---|---|---|---|
ramdisk | 16.3 sec | 6.58 MB | whole image | yes |
JFFS2 | 21.4 sec | 10.3 MB | per file | only non-active files |
cramfs | 10.8 sec | 10.3 MB | whole image | no |
ext2 (ro) | 9.1 sec | 10.8 MB | whole image | no |
ext2 on CF (ro) | 9.3 sec | 10.9 MB | whole image | no |
File on FAT fs | 11.4 sec | 7.8 MB | whole image | yes |
As you can see, the ramdisk solution is the worst of all in terms of RAM memory footprint; also it takes a pretty long time to boot. However, it is one of the few solutions that allow an in-situ update while the system is running.
JFFS2 is easy to use as it's a writable file system but it takes a long time to boot.
A read-only ext2 file system shines when boot time and RAM memory footprint are important; you pay for this with an increased flash memory footprint.
External flash memory devices like CompactFlash cards or USB memory sticks can be cheap and efficient solutions especially when lots of data need to be stored or when easy update procedures are required. -
9.8. Overlay File Systems
Introduction
Overlay File Systems provide an interesting approach to several frequent problems in Embedded Systems. For example, mini_fo
is a virtual kernel file system that can make read-only file systems writable. This is done by redirecting modifying operations to a writeable location called "storage directory", and leaving the original data in the "base directory" untouched. When reading, the file system merges the modifed and original data so that only the newest versions will appear. This occurs transparently to the user, who can access the data like on any other read-write file system.
What it is good for?
In embedded systems the main use of mini_fo
is to overlay the root file system. This means it is mounted on top of the regular root file system, thereby allowing applications or users to transparently make modifications to it but redirecting these to a different location.
Some examples of why this is usefull are explained in the following sections.
Making a read-only root filesystem writeable
Root file systems stored in flash are often read only, such as cramfs or read only ext2. While this offers major advantages in terms of speed and flash memory footprint, it nevertheless is often desireable to be able to modify the root file system, for example to
- apply (small) software updates without having to burn a whole new root file system image to flash
- make modifications during developement when frequent changes to the root file system occur.
This can be achieved by mounting mini_fo
on top of the root file system and using a (probably small) writeable partition as the storage file system. This could be either a JFFS2 flash file system, or during development even an external hard disk. This has the following advantages:
- read-only file systems (fast, small memory footprint) can be used like persistent writable file systems (in contrast to a ramdisk)
- slow flash journalling file systems with large flash memory footprint can be avoided.
Non persistant changes
Ramdisks are often used when the root file system needs to be modified non-persistantly. This works well, but downsides are the large RAM memory footprint and the time costly operation of copying the ramdisk into RAM during startup. These can be avoided by overlaying the root file system as in the previous example but with the difference that the tmpfs file system is used as storage. Thus only modified files are stored in RAM, and can even be swapped out if neccessary. This saves boot time and RAM!
Resetable changes
Mini_fo
can be easily used to implement a "reset to factory defaults" function by overlaying the default root file system. When configuration changes are made, these are automatically directed to the storage file system and take precedence over the original files. Now, to restore the system to factory defaults, all that needs to be done is delete the contents of the storage directory. This will remove all changes made to the root file system and return it to the original state.
Note: Deleting the contents of the storage directory should only be done when the overlay file system is unmounted.
Examples
Generally, there are two different ways of overlaying the root file system, which both make sense in different scenarios.
Starting a single application in a chrooted overlayed environment
This is easy. Let's assume "/" is the read-only root file system and /dev/mtdblock5 contains a small JFFS2 flash partition that shall be used to store modifications made by application "/usr/bin/autoPilot":
# mount -t jffs2 /dev/mtdblock5 /tmp/sto # insmod mini_fo.o # mount -t mini_fo -o base=/,sto=/tmp/sto/ / /mnt/mini_fo/ # cd /mnt/mini_fo/ # chroot . /usr/bin/autoPilot
The mini_fo
file system is mounted with "/" as base directory, "/tmp/sto/" as storage directory to the mount point "/mnt/mini_fo". After that, chroot(1)
is used to start the application with the new file system root "/mnt/mini_fo". All modifications made by the application will be stored to the JFFS2 file system in /tmp/sto.
Starting the whole system system in chrooted overlayed environment
This is more interesting, and a bit trickier, as mounting needs to be done during system startup after the root file system has been mounted, but before init is started. The best way to do this is to have a script that mounts the mini_fo file system on top of root and then starts init in the chrooted overlayed environment. For example assume the following script "overlay_init", stored in /sbin/:
#!/bin/bash # # mount mini_fo overlay file system and execute init # # make sure these exist in the read-only file system STORAGE=/tmp/sto MOUNT_POINT=/mnt/mini_fo/ # mount tmpfs as storage file system with a maximum size of 32MB mount -t tmpfs -o rw,size=32M none $STORAGE /sbin/modprobe mini_fo mount -t mini_fo -o base=/,sto=$STORAGE / $MOUNT_POINT exec /usr/sbin/chroot $MOUNT_POINT /sbin/init echo "exec chroot failed, bad!" exec /bin/sh exit 1
Now its easy to choose between a mini_fo
overlayed and the regular non overlayed system just by setting the "init" kernel parameter in the boot loader to "init=/sbin/overlay_init".
Tips
pivot_root(1)
can be used with chroot if there is need to access the original non overlayed root file system from the chrooted overlayed environment.
Performance overhead
The mini_fo
file system is inserted as an additional layer between the VFS and the native file system, and thus creates some overhead that varies strongly depending of the operation performed.
- modifying a regular file for the first time
This results in a copy of the original file beeing created in the storage directory, that is then modified. Overhead depends on the size of the modified file. - Reading from files, creating new files, modifying already modified files
These operations are passed directly through to the lower native layer, and only impose an overhead of 1-2%.
Further information
This section discusses how the mini_fo
overlay file system can be used in embedded systems. More general information is available at the mini_fo
project page: http://www.denx.de/wiki/Know/MiniFOHome.
9.9. The Persistent RAM File system (PRAMFS)
The pramfs
file system supports persistent memory devices such as SRAM. Instead of having a block emulation layer over such a memory area and using a normal file system on top of that, pramfs
seeks to induce minimal overhead in this situation. Most important in this respect is that the normal block layer caching of the Linux kernel is circumvented in pramfs
.
9.9.1. Mount Parameters
The most important parameters for normal usage are
physaddr
: The physical address of the static memory.init
: When given, it will initialize the file system to that size.
9.9.2. Example
We will show a sample usage of pramfs
in this section using normal DRAM on a board with at least 256MB of memory. For pramfs
we reserve the upper 32MB by appending mem=224M
to the kernel command line.
First off we generate some testdata on a persistent file system (/tmp
) to demonstrate that pramfs
survives a reboot (of course with power always applied to keep the DRAM refreshed):
bash-3.00# dd if=/dev/urandom bs=1M count=8 of=/tmp/testdata 8+0 records in 8+0 records out bash-3.00#
Next we mount the 32MB that we reserved and initialize it to be 32MB in size and copy the testfile. A final compare shows that the copy was indeed successful so we can reboot:
bash-3.00# mount -t pramfs -o physaddr=0xe000000,init=0x2000000 none /mnt bash-3.00# cp /tmp/testdata /mnt bash-3.00# cmp /tmp/testdata /mnt/testdata bash-3.00# reboot
Having rebooted (using mem=224M
on the kernel command line again of course) we mount the file system but this time without the init
parameter because it is preinitialized. We then check the contents again:
bash-3.00# mount -t pramfs -o physaddr=0xe000000 none /mnt bash-3.00# ls /mnt testdata bash-3.00# cmp /tmp/testdata /mnt/testdata bash-3.00#
留言列表