We are currently working a project that contains cm-x270 module (http://bec-systems.com/web/content/view/62/9/ ) which contains 4MB of NOR flash and 128MB of NAND flash. The Linux kernel and a small root file system (rootfs) will be stored in NOR flash, and the main rootfs in the NAND flash. The rootfs in the NOR flash will be used to update the system from a USB Flash drive in the field. This article describes how to build a small boot rootfs using OpenEmbedded. We also look at various ways to store the image in flash.
Requirments
In many embedded systems, field upgrades is a requirement. There are many ways to organize this, but one of the most reliable is to have two rootfs partitions. The first partition is a “safe” rootfs that is only used to update the second partition. The second partition contains the main application and can be updated in the field. If a programming error, or power loss occurs while updating the 2nd partition, you can always boot into the first rootfs partition and restart the process.
The kernel and the bootloader in this system take up about 1.5MB (out of 4MB total) of the NOR flash. This leaves us with about 2.5MB for the update rootfs. Because we are building the rest of the system using OpenEmbedded (OE), it may make sense for us to also build this update rootfs using OE to keep everything in one build environment.
uclibc, glibc, or klibc?
There are several libc libraries that are commonly used to build Embedded Linux systems. glibc is the standard that is used in desktop and sever systems and is what is being used in the main NAND rootfs partition. uclibc is smaller than glibc is often used in space constrained systems. klibc is a very minimal libc subset that supports operations typically used in an initramfs. Because we are already using glibc, we decided to try this first as we could then use the same toolchain and build tree and just generate two images — one for NAND, and one for NOR.
OpenEmbedded support for small images
OE includes a task-base-minimal meta target that can be used as a basis for a small images, or you can create your own based on task-base. You will then need to create a custom image recipe that will use this meta task. An example is shown below:
PR = "r0" export IMAGE_BASENAME = "svs_nor" export PACKAGE_INSTALL = "task-min" # keep extra language files from being installed export IMAGE_LINGUAS = "" RDEPENDS = "task-min" IMAGE_FSTYPES = "tar jffs2 ext2 cramfs squashfs squashfs-lzma cpio.gz" IMAGE_ROOTFS_SIZE_ext2="10240" #EXTRA_IMAGECMD_jffs2="--pad=0x1000000 --eraseblock=0x40000" EXTRA_IMAGECMD_jffs2="" inherit image
In this case, I had defined a custom recipe for task-min based on task-base, which installs the following packages:
root@compulab-pxa270:~$ ipkg list_installed base-files - 3.0.14-r58 - base-passwd - 3.5.9-r2 - busybox - 1.2.1-r11 - initscripts - 1.0-r86 - ipkg - 0.99.163-r1 - ipkg-collateral - 1.0-r5 - libc6 - 2.5-r4 - libgcc1 - 4.1.1-r10 - libipkg0 - 0.99.163-r1 - makedevs - 1.0.0-r2 - sysvinit - 2.86-r32 - sysvinit-inittab - 2.86-r32 - sysvinit-pidof - 2.86-r32 - task-min - 1.0-r1 - tinylogin - 1.4-r3 - update-rc.d - 0.7-r0 -
Storing the NOR filesystem in flash
OE can generate images for many different flash filesystems and initrd mechanisms. These are specified in the IMAGE_FSTYPES variable in the above recipe. In this case, I chose to generate many different filesystems so I could see evaluate how much compression I would get with each filesystem type. The results:
Rootfs Type | Size (bytes) |
squashfs-lzma | 1785856 |
tar.bz2 | 1918243 |
cpio.gz | 2024558 |
squashfs | 2109440 |
cramfs | 2265088 |
jffs2 | 2455396 |
ext2 (uncompressed) | 5481000 |
I ended up choosing the cpio.gz format as it can be loaded directly into an initramfs filesystem by the kernel. There are several advantages to using an initramfs for this task:
- You don’t have to mess around setting up tmpfs filesystems for directories that need to be writable.
- You can update the NOR flash partition while you are running out of the initramfs filesystem.
- It is fairly small with only squashfs-lzma being smaller.
Future Optimizations
For now, the glibc based solution is good enough because it fits in the flash space we have and it is easy to build with our existing build environment. In the future, we may move to a uclibc or klibc based solution and try to reduce our flash size (OpenEmbedded supports both klibc and uclibc). The above image sizes can also be reduced by removing ipkg and other components that are not needed.
This exercise illustrates many of the advantages of Linux and OpenEmbedded. With very little work, I can produce exactly what I need. The ability of the 2.6 Linux kernel to load a cpio archive into an initramfs is a very elegant solution for small boot images.