In this day and age, most embedded systems include a way for users to easily update software once the device has been deployed. This article discusses the requirements for a field update mechanism along with pointers for how to implement.
Update Mechanism Requirements
Requirements for a field update mechanism might be:
- easy for users to perform updates.
- little chance of “bricking” a unit if reset occurs during update.
- ability to update all of the software in the unit
- in this case, update from files on a USB Storage device
- ability to program NOR and NAND flash
Other systems may have requirements to update from a network, Compact Flash card, etc.
The USB wrinkle
System update is typically done by the bootloader. In this case, we are using a Compulab cm-x270 module (http://bec-systems.com/site/77/compulab-cm-x270-pxa270-module-review) that has a proprietary boot loader and does not support updates from a USB storage device. Therefore, we need to run the updates from the context of the Linux operating system which has drivers for USB and the NAND flash. We decided that the best way in this case is to have a small Linux OS image that contains enough functionality to update the main filesystem. This also provides us with the capability to recover if the main filesystem ever gets corrupted. The flash layout for the system is:
|Partition||Flash Device||Size||File System||Description|
|Update rootfs||NOR||2304KiB||Initramfs (RO)||Small rootfs that contains just enough functionality to update the system|
|Main rootfs||NAND||512MiB||JFFS2 (RW)||Main rootfs including application|
There are other schemes that could work as well. One way might be to have two identical application partitions and only update one at a time. A reset while updating the kernel or the update rootfs does have the potential of “bricking” the system, but most of the updates will be for the Main rootfs, so we think this risk is low enough to be acceptable in this system. The update rootfs is loaded into RAM before it is used, so there is little chance of it being corrupted during normal operation. Running the update rootfs from ram is also convenient in that we can easily re-program the update rootfs in flash as we are not directly mounting the filesystem in flash.
Building the Update rootfs
The base update rootfs is built with OpenEmbedded as described in http://bec-systems.com/site/77/compulab-cm-x270-pxa270-module-review. Once we had the system booting into the update rootfs, we had to add the following functionality:
- Boot the main rootfs from NAND flash
- look for update files on a USB Storage device and flash the kernel, update rootfs, and main rootfs as needed.
Booting the NAND flash image is accomplished using the busybox switch_root application. This utility allows you to easily switch to another rootfs from an initramfs. There are a few gotchas to be aware of when working with switch_root:
- switch_root must be “exec’d” from the init process with PID=”1″.
- switch_root requires a “/init” file to be present
The above functionality was accomplished by writing a shell script called /init. If an error occurs, the /init shell script then launches the standard init process in /sbin/init which provides a terminal and allows for easy debugging. The mtd-utils package provides a handy utility called nandwrite which can be used to write jffs2 images to NAND flash (handles bad blocks, etc). At some point, we may remove all the extra functionality from the update rootfs like the standard sysvinit, terminal login, etc, but for now it is handy to have for debugging. To give you an idea how easy it is to write to flash from a shell script, consider the following snippets:
# write Image into update rootfs parition
cat /usb/nor_rootfs.img > /dev/mtd2
# write Image to NAND rootfs partition
nandwrite /dev/mtd3 /usb/nand_jffs2.img
# and to launch rootfs in nand flash
exec switch_root -c /dev/console /jffs2 /sbin/init 5
Having typically used bootloaders for system update in the past, I’m very pleased with how this mechanism worked out. Thanks to the flexibility of Linux and OpenEmbedded, this only took several days to implement and debug. Writing update programs with shell scripts at the application level gives you a lot of flexibility and allows you to use standard Linux drivers for USB and Flash access which are very robust. At some point if there is interest, we may look at cleaning up our code and contributing functionality to OpenEmbedded to generate a more generic update initramfs. Please contact us or leave a comment if you are interested in collaborating on something like this.