Description
This how-to covers the process of creating and flashing a custom kernel initramfs.
Creating a custom initramfs allows a developer to implement additional functionality early in the boot process.
Compatibility
This how-to should be applicable to Kindle devices that allow the end user to flash and boot an unsigned, self built kernel (i.e anything earlier than KT4/PW4).
Process
Context
All Kindle kernels contain a small initramfs that contains a utility known as
recovery-util. The main function implemented by
recovery-util that most users will be familiar with is the additional updater that is capable of installing a firmware update, even if the main update mechanism within the rootfs has been disabled. However, when accessing the device using the serial connection, there are a number of other features which, as the name suggests, are useful for recovering bricked devices:
Code:
Menu
====
3. Load MMC over USB storage
4. Erase MMC
I. Initialize Partition Table (fdisk) and format FAT
O. Format and overwrite FAT partition
E. Export FAT partition
U. Update using update*.bin file on FAT partition
M. Update using update*.bin file on FAT partition of second MMC port
D. dmesg / kernel printk ring buffer.
Q. quit
Choose: 9 -U
Most of the features listed in the menu above are always present and functional within
recovery-util - generally, this includes the ability to erase the MMC storage, initialise the partition table, format the FAT partition, update using a firmware update on the FAT partition and printing the kernel ring buffer using dmesg.
However, some options (such as loading the entire MMC storage over USB) are either missing entirely or do not work when selected.
Probing deeper
We can understand how this is implemented in more detail by examining the contents of a stock initramfs.
Extracting a stock initramfs
First, we need to download and extract the contents of an update image:
Code:
kindletool extract update_kindle_voyage_5.13.6.bin /tmp/kv
We now need to extract the contents of the initramfs from the kernel contained within the update image:
Code:
user@ubuntu:/tmp/kv$ binwalk -e imx60_wario/uImage
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, header CRC: 0x3F3219, created: 2021-04-08 09:40:01, image size: 3143456 bytes, Data Address: 0x80008000, Entry Point: 0x80008000, data CRC: 0xA02A9C91, OS: Linux, CPU: ARM, image type: OS Kernel Image, compression type: none, image name: "Linux-3.0.35-lab126"
64 0x40 Linux kernel ARM boot executable zImage (little-endian)
18028 0x466C gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
This will leave us with a mangled copy of the zImage contained within the uImage:
Code:
user@ubuntu:/tmp/kv$ file imx60_wario/_uImage.extracted/466C
imx60_wario/_uImage.ex
tracted/466C: data
Use binwalk to extract the initramfs contents from the mangled zImage:
Code:
user@ubuntu:/tmp/kv$ binwalk -e imx60_wario/_uImage.extracted/466C
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
6773 0x1A75 End of Zip archive, footer length: -24474
129080 0x1F838 ASCII cpio archive (SVR4 with no CRC), file name: ".", file name length: "0x00000002", file size: "0x00000000"
129192 0x1F8A8 ASCII cpio archive (SVR4 with no CRC), file name: "lib", file name length: "0x00000004", file size: "0x00000000"
129308 0x1F91C ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules", file name length: "0x0000000C", file size: "0x00000000"
129432 0x1F998 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/arcotg_udc.ko", file name length: "0x0000001A", file size: "0x00000035"
129624 0x1FA58 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/ehci-hcd.ko", file name length: "0x00000018", file size: "0x00000031"
129812 0x1FB14 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/g_file_storage.ko", file name length: "0x0000001E", file size: "0x00000039"
130012 0x1FBDC ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/fsl_otg_arc.ko", file name length: "0x0000001B", file size: "0x00000033"
130204 0x1FC9C ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126", file name length: "0x0000001A", file size: "0x00000000"
130340 0x1FD24 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.alias", file name length: "0x00000028", file size: "0x0000004E"
130572 0x1FE0C ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.dep", file name length: "0x00000026", file size: "0x00000148"
131048 0x1FFE8 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.builtin.bin", file name length: "0x0000002E", file size: "0x00000000"
131204 0x20084 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.softdep", file name length: "0x0000002A", file size: "0x00000037"
131412 0x20154 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.symbols", file name length: "0x0000002A", file size: "0x00000532"
132896 0x20720 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.dep.bin", file name length: "0x0000002A", file size: "0x000001FE"
133560 0x209B8 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.alias.bin", file name length: "0x0000002C", file size: "0x00000034"
133768 0x20A88 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.symbols.bin", file name length: "0x0000002E", file size: "0x000006BA"
135648 0x211E0 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel", file name length: "0x00000021", file size: "0x00000000"
135792 0x21270 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers", file name length: "0x00000029", file size: "0x00000000"
135944 0x21308 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb", file name length: "0x0000002D", file size: "0x00000000"
136100 0x213A4 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/host", file name length: "0x00000032", file size: "0x00000000"
136260 0x21444 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/host/ehci-hcd.ko", file name length: "0x0000003E", file size: "0x00013418"
215304 0x34908 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget", file name length: "0x00000034", file size: "0x00000000"
215468 0x349AC ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/arcotg_udc.ko", file name length: "0x00000042", file size: "0x0000A4C8"
257828 0x3EF24 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/g_file_storage.ko", file name length: "0x00000046", file size: "0x0000EC48"
318496 0x4DC20 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/otg", file name length: "0x00000031", file size: "0x00000000"
318656 0x4DCC0 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/usb/otg/fsl_otg_arc.ko", file name length: "0x00000040", file size: "0x000067E4"
345428 0x54554 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/video", file name length: "0x0000002F", file size: "0x00000000"
345588 0x545F4 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/video/mxc", file name length: "0x00000033", file size: "0x00000000"
345752 0x54698 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/kernel/drivers/video/mxc/mxc_epdc_fb.ko", file name length: "0x00000042", file size: "0x00022FA8"
489200 0x776F0 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/modules.devname", file name length: "0x0000002A", file size: "0x00000034"
489404 0x777BC ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/extra", file name length: "0x00000020", file size: "0x00000000"
489548 0x7784C ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/3.0.35-lab126/extra/mxc_epdc_eink.ko", file name length: "0x00000031", file size: "0x00002E94"
501632 0x7A780 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/mxc_epdc_eink.ko", file name length: "0x0000001D", file size: "0x00000024"
501808 0x7A830 ASCII cpio archive (SVR4 with no CRC), file name: "lib/modules/mxc_epdc_fb.ko", file name length: "0x0000001B", file size: "0x00000035"
502004 0x7A8F4 ASCII cpio archive (SVR4 with no CRC), file name: "bin", file name length: "0x00000004", file size: "0x00000000"
502120 0x7A968 ASCII cpio archive (SVR4 with no CRC), file name: "bin/cat", file name length: "0x00000008", file size: "0x00000AF0"
505040 0x7B4D0 ASCII cpio archive (SVR4 with no CRC), file name: "bin/nfsmount", file name length: "0x0000000D", file size: "0x00001D20"
512620 0x7D26C ASCII cpio archive (SVR4 with no CRC), file name: "bin/sh", file name length: "0x00000007", file size: "0x00000009"
512752 0x7D2F0 ASCII cpio archive (SVR4 with no CRC), file name: "bin/sleep", file name length: "0x0000000A", file size: "0x000002D8"
513600 0x7D640 ASCII cpio archive (SVR4 with no CRC), file name: "bin/hotplug", file name length: "0x0000000C", file size: "0x00001DBC"
521336 0x7F478 ASCII cpio archive (SVR4 with no CRC), file name: "bin/load_waveform.sh", file name length: "0x00000015", file size: "0x0000027C"
522104 0x7F778 ASCII cpio archive (SVR4 with no CRC), file name: "bin/ipconfig", file name length: "0x0000000D", file size: "0x0000368C"
536192 0x82E80 ASCII cpio archive (SVR4 with no CRC), file name: "bin/mkdosfs", file name length: "0x0000000C", file size: "0x00007438"
566068 0x8A334 ASCII cpio archive (SVR4 with no CRC), file name: "bin/run-init", file name length: "0x0000000D", file size: "0x000010DC"
570508 0x8B48C ASCII cpio archive (SVR4 with no CRC), file name: "bin/kinit", file name length: "0x0000000A", file size: "0x0001320C"
648976 0x9E710 ASCII cpio archive (SVR4 with no CRC), file name: "bin/recovery-util", file name length: "0x00000012", file size: "0x00080968"
1175800 0x11F0F8 ASCII cpio archive (SVR4 with no CRC), file name: "bin/sh.shared", file name length: "0x0000000E", file size: "0x0000F900"
1239668 0x12EA74 ASCII cpio archive (SVR4 with no CRC), file name: "etc", file name length: "0x00000004", file size: "0x00000000"
......
binwalk output truncated for brevity
......
The contents of the initramfs can now be examined:
Code:
user@ubuntu:/tmp/kv$ ls imx60_wario/_uImage.extracted/_466C.extracted/cpio-root/
bin dev etc init lib mnt proc root sys usr var
Examining a stock initramfs
Now that we have a copy of the contents of the initramfs, we can begin to understand how its functionality is implemented.
Init process
When the kernel loads the initramfs, it checks a number of predefined paths for a script that it should attempt to run. In this case, the file is a symlink located at the initramfs root directory that points to
/bin/init:
Code:
lrwxrwxrwx 1 user user 17 Nov 28 11:21 init -> bin/recovery-util
If we look within
/bin/, we can find a bunch of additional scripts and binaries:
Code:
user@ubuntu:/tmp/kv/cpio-root$ ls -l bin/
total 744
-rwxr-xr-x 1 user user 2800 Nov 28 11:21 cat
-rwxr-xr-x 1 user user 7612 Nov 28 11:21 hotplug
-rwxr-xr-x 1 user user 13964 Nov 28 11:21 ipconfig
-rwxr-xr-x 1 user user 78348 Nov 28 11:21 kinit
-rwxr-xr-x 1 user user 636 Nov 28 11:21 load_waveform.sh
-rwxr-xr-x 1 user user 29752 Nov 28 11:21 mkdosfs
-rwxr-xr-x 1 user user 7456 Nov 28 11:21 nfsmount
-rwxr-xr-x 1 user user 526696 Nov 28 11:21 recovery-util
-rwxr-xr-x 1 user user 4316 Nov 28 11:21 run-init
lrwxrwxrwx 1 user user 9 Nov 28 11:21 sh -> sh.shared
-rwxr-xr-x 1 user user 63744 Nov 28 11:21 sh.shared
-rwxr-xr-x 1 user user 728 Nov 28 11:21 sleep
NFS Boot
The inclusion of the
nfsmount binary is particularly interesting. Despite the lack of any menu option allowing the user to boot from an NFS share, there are a number of strings within the
recovery-util binary that imply that this is possible using the
g_ether kernel module:
Code:
nfs_boot_default
ipconfig -d nfsaddrs=%s:%s:%s:%s:%s:%s
nfsmount -o v3,tcp %s:%s /root
nfsmount: retval = %d
Attempt #%d to NFS mount...
%s: Failed to mount NFS on /root: retval=%d
gadget_load_ethernet
/lib/modules/g_ether.ko
%s: Could not load Ethernet gadget module: %s (%d)
This kernel module is not included within the stock initramfs, meaning that it is likely an internal feature used by Lab126 during development. It
is possible to create an initramfs that can mount and boot from an NFS share, which we will cover separately in a later post.
Diags firmware update
Another interesting feature of
recovery-util relates to a type of firmware update that I was not previously aware of and have not encountered in the wild. The
recovery-util binary contains strings that imply that it is capable of installing a diags kernel and rootfs:
Code:
diagsfs_gz_file_data
imx60_wario/diag-uImage.sig
diagrootfs.img.gz.sig
imx60_wario/diag-uImage
diagrootfs.img.gz
ERROR:%s: error getting diagsfs size: %d. Skipping diagsfs size check.
Public key location
As with the main updater within the rootfs, the
recovery-util updater will only accept a signed firmware image that contains files signed with the same public key. These keys are stored within the binary itself; their existence can be verified by searching for the first 8 bytes of each of the production public keys:
Code:
user@ubuntu:/tmp/kv/cpio-root$ xxd -ps bin/recovery-util | grep 00b17e98
00000000000000000000000000000000650000000000000000b17e9899d5
user@ubuntu:/tmp/kv/cpio-root$ xxd -ps bin/recovery-util | grep 00b0f74b
8afc3100000000000000010001000000000000b0f74b8e062739f169108a
It is possible to replace these keys and repack the initramfs to create an update mechanism that will accept both Lab126 and user-signed firmware updates. This will be covered separately in a later post.
Summing it up
By examining the both the behaviour of
recovery-util over the serial port and the contents of the initramfs, we have been able to broadly understand how this utility fits into the boot process:
- On boot, the kernel will search for a file to execute
- Upon finding the /init symlink, the kernel will follow it and execute recovery-util
- recovery-util will present an option that allows the user to halt the boot process and access the various menu options over the serial port
- If this option is not selected, recovery-util will search for an update file to install, handle that update, then reboot into the updated rootfs
- recovery-util achieves this using a combination of native code within the binary itself and exec calls to the included klibc utilities.
Context
With this understanding, we can begin to plan out how to create an initramfs of our own. This raises a couple of important questions:
- How do we structure the files that we want to include within the initramfs?
- How do we implement our desired functionality within the initramfs?
In this how-to, we will structure our initramfs by creating a directory containing scripts and kernel modules that we wish to include and create an initramfs list to specify the required device nodes. To provide a simple starting point for further experimentation, we will create a simple
ash script that will mount and switch to the root partition, rather than creating a more complex executable using C.
Structuring initramfs files
Although it is not particularly obvious without reading the help for the kernel configuration menu item, it is possible to include a directory or list of directories containing the files that we want to include within the initramfs.
This might seem like a good strategy, but presents us with a problem: when creating an initramfs, we need to include not only the binaries that we want to run, but also the device nodes that are required for these tasks. If we use a directory-based approach, these would need to be created manually using
mknod - a tedious, error prone task that involves the need to escalate to root.
We can avoid this by defining the files and device nodes that we want to include within a list file.
This is the same approach used by Lab126 and an example of the format that this list should take can be found within the
linux-2.6.31-lab126 kernel sources:
Code:
dir /dev 0755 0 0
nod /dev/tty 0660 0 0 c 5 0
nod /dev/console 0600 0 0 c 5 1
nod /dev/null 0666 0 0 c 1 3
nod /dev/zero 0660 0 0 c 1 5
nod /dev/random 0660 0 0 c 1 8
nod /dev/mem 0660 0 0 c 1 1
nod /dev/pmic 0660 0 0 c 250 0
nod /dev/usb1 0660 0 0 c 189 0
nod /dev/watchdog 0660 0 0 c 10 130
nod /dev/ttymxc0 0660 0 0 c 207 16
nod /dev/ttymxc1 0660 0 0 c 207 17
nod /dev/ttymxc2 0660 0 0 c 207 18
nod /dev/ttymxc3 0660 0 0 c 207 19
nod /dev/ttymxc4 0660 0 0 c 207 20
dir /dev/fb 0755 0 0
nod /dev/fb/0 0660 0 0 c 29 0
slink /dev/fb0 /dev/fb/0 0660 0 0
nod /dev/mmcblk0 0660 0 0 b 179 0
nod /dev/mmcblk0p1 0660 0 0 b 179 1
nod /dev/mmcblk0p2 0660 0 0 b 179 2
nod /dev/mmcblk0p3 0660 0 0 b 179 3
nod /dev/mmcblk0p4 0660 0 0 b 179 4
nod /dev/mmcblk1 0660 0 0 b 179 8
nod /dev/mmcblk1p1 0660 0 0 b 179 9
nod /dev/mmcblk1p2 0660 0 0 b 179 10
nod /dev/mmcblk1p3 0660 0 0 b 179 11
nod /dev/mmcblk1p4 0660 0 0 b 179 12
dir /dev/mtd 0755 0 0
nod /dev/mtd/0 0660 0 0 c 90 0
nod /dev/mtd/1 0660 0 0 c 90 2
nod /dev/mtd/2 0660 0 0 c 90 4
nod /dev/mtd/3 0660 0 0 c 90 6
nod /dev/mtd/4 0660 0 0 c 90 8
nod /dev/mtd/5 0660 0 0 c 90 10
dir /dev/mtdblock 0755 0 0
nod /dev/mtdblock/0 0660 0 0 b 31 0
nod /dev/mtdblock/1 0660 0 0 b 31 1
nod /dev/mtdblock/2 0660 0 0 b 31 2
nod /dev/mtdblock/3 0660 0 0 b 31 3
nod /dev/mtdblock/4 0660 0 0 b 31 4
nod /dev/mtdblock/5 0660 0 0 b 31 5
nod /dev/loop0 0660 0 0 b 7 0
nod /dev/loop1 0660 0 0 b 7 1
nod /dev/loop2 0660 0 0 b 7 2
nod /dev/loop3 0660 0 0 b 7 3
dir /dev/i2c 0755 0 0
nod /dev/i2c/0 0660 0 0 c 89 0
nod /dev/i2c/1 0660 0 0 c 89 1
nod /dev/i2c/2 0660 0 0 c 89 2
dir /dev/input 0755 0 0
nod /dev/input/event0 0660 0 0 c 13 64
nod /dev/input/event1 0660 0 0 c 13 65
nod /dev/input/event2 0660 0 0 c 13 66
dir /proc 0755 0 0
dir /sys 0755 0 0
dir /root 0700 0 0
dir /lib 0755 0 0
# klibc lib
file /lib/klibc-XXXXXXXXXXXXXXXXXXXXXXXXXXX.so /initrd/lib/klibc/lib/klibc-XXXXXXXXXXXXXXXXXXXXXXXXXXX.so 0755 0 0
dir /lib/modules 0755 0 0
dir /mnt 0755 0 0
dir /mnt/wfm 0755 0 0
# usb gadget drivers
file /lib/modules/arcotg_udc.ko /initrd/initramfs/lib/arcotg_udc.ko 0755 0 0
file /lib/modules/g_file_storage.ko /initrd/initramfs/lib/g_file_storage.ko 0755 0 0
# eink drivers
file /lib/modules/eink_fb_waveform.ko /initrd/initramfs/lib/eink_fb_waveform.ko 0755 0 0
file /lib/modules/mxc_epdc_fb.ko /initrd/initramfs/lib/mxc_epdc_fb.ko 0755 0 0
# keypad driver
file /lib/modules/mxc_keyb.ko /initrd/initramfs/lib/mxc_keyb.ko 0755 0 0
file /lib/modules/tequila_keypad.ko /initrd/initramfs/lib/tequila_keypad.ko 0755 0 0
file /lib/modules/whitney_button.ko /initrd/initramfs/lib/whitney_button.ko 0755 0 0
# klibc utilities
dir /bin 0755 0 0
file /bin/ipconfig /initrd/lib/klibc/bin/ipconfig 0755 0 0
file /bin/nfsmount /initrd/lib/klibc/bin/nfsmount 0755 0 0
file /bin/run-init /initrd/lib/klibc/bin/run-init 0755 0 0
file /bin/recovery-util /initrd/sbin/recovery-util 0755 0 0
file /bin/hotplug /initrd/sbin/hotplug 0755 0 0
file /bin/kinit /initrd/lib/klibc/bin/kinit 0755 0 0
file /bin/sh /initrd/lib/klibc/bin/sh.shared 0755 0 0
file /bin/mkdosfs /initrd/initramfs/bin/mkdosfs 0755 0 0
# link init
slink /init /bin/recovery-util 0755 0 0
Although initially confusing, we can break down what each of the item types and parameters refer to like so:
Code:
dir <PATH_TO_CREATE> <PERMISSIONS> <UID> <GID>
slink <SYMLINK_TO_CREATE> <PERMISSIONS> <UID> <GID>
file <INITRAMFS_PATH> <HOST_PATH> <PERMISSIONS> <UID> <GID>
nod <NODE_TO_CREATE> <PERMISSIONS> <UID> <GID> <DEVICE_TYPE> <MAJOR> <MINOR>
We will follow the way that Lab126 have structured their initramfs list and create a directory at the root of our build host to contain the files that we want to include:
Code:
sudo mkdir -p /initrd/bin
sudo chown -R user:user /initrd
Implementing initramfs functionality
Building Busybox
In this how-to, we will forgo the traditional approach of including the small set of utilities included with klibc and use a statically linked version of Busybox instead. Doing so allows us to implement more complex functionality than is otherwise possible by utilising the various applets that Busybox provides.
Download and extract a copy of the Busybox source code:
Code:
wget https://www.busybox.net/downloads/busybox-1.34.1.tar.bz2 && tar xf busybox-1.34.1.tar.bz2
Enter the directory containing the Busybox source code and start the configuration menu:
Code:
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make menuconfig
Navigate to the
Settings submenu and select the
Build static binary (no shared libs) option under
Build Options
Depending on your toolchain, you may encounter build errors. When using NiLuJe's PW2 toolchain, I needed to make these changes using the configuration menu:
- Disable nsenter
- Disable Enable -d and -f flags (requires syncfs(2) in libc) for sync
Copy the statically linked Busybox binary to our staging directory:
Code:
cp busybox /initrd/bin/busybox
Creating an init script
Save the contents of the file below to
/initrd/bin/custom_boot.sh:
Code:
#!/bin/busybox ash
/bin/busybox --install -s
mount /dev/mmcblk0p1 /mnt/rootfs
exec switch_root -c /dev/console /mnt/rootfs /sbin/init
There's a few things going on here:
- Busybox detects which applet needs to be run by checking the first parameter that is is called with. We want to use the stripped down version of the ash shell included with Busybox, so we specify this within the shebang
- To avoid having to specify the full path to Busybox each time we call an applet, we can install symlinks to each applet within the initramfs by running /bin/busybox --install -s. Note that this command expects a number of directories to exist and will fail if they are not specified within our initramfs list
- We're mounting the rootfs partition to /mnt/rootfs. Note that this directory also needs to be specified within our initramfs list
- Finally, we use switch_root to switch to /mnt/rootfs and start the main Upstart init process by calling /sbin/init. We use exec to run switch_root as we want to replace the shell that is running this script with this process (the default behaviour would be to run switch_root as a new process, which causes problems as Upstart expects /sbin/init to be PID 1)
Creating an initramfs list
Save the contents of the file below to
/initrd/initramfs-custom.list:
Code:
# Root directories
dir /bin 755 0 0
dir /dev 755 0 0
dir /lib 755 0 0
dir /root 700 0 0
dir /mnt 755 0 0
dir /proc 755 0 0
dir /sys 755 0 0
# Busybox applet directories
dir /sbin 755 0 0
dir /usr 755 0 0
dir /usr/bin 755 0 0
dir /usr/sbin 755 0 0
# Second level
dir /mnt/wfm 777 0 0
dir /mnt/rootfs 777 0 0
dir /lib/modules 777 0 0
# Kernel module (example, we're not using this)
file /lib/modules/arcotg_udc.ko /initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/arcotg_udc.ko
# Binaries + scripts
file /bin/busybox /initrd/bin/busybox 755 0 0
file /bin/custom_boot.sh /initrd/bin/custom_boot.sh 755 0 0
# Devices
nod /dev/console 600 0 0 c 5 1
nod /dev/frontlight 660 0 0 c 10 162
nod /dev/kmsg 660 0 0 c 1 11
nod /dev/loop0 660 0 0 b 7 0
nod /dev/loop1 660 0 0 b 7 1
nod /dev/loop2 660 0 0 b 7 2
nod /dev/loop3 660 0 0 b 7 3
nod /dev/mem 660 0 0 c 1 1
nod /dev/mmcblk0 660 0 0 b 179 0
nod /dev/mmcblk0boot1 660 0 0 b 179 16
nod /dev/mmcblk0boot0 660 0 0 b 179 8
nod /dev/mmcblk0p1 660 0 0 b 179 1
nod /dev/mmcblk0p2 660 0 0 b 179 2
nod /dev/mmcblk0p3 660 0 0 b 179 3
nod /dev/mmcblk0p4 660 0 0 b 179 4
nod /dev/null 666 0 0 c 1 3
nod /dev/pmic 660 0 0 c 250 0
nod /dev/random 660 0 0 c 1 8
nod /dev/tty 660 0 0 c 5 0
nod /dev/tty1 660 0 0 c 5 1
nod /dev/tty2 660 0 0 c 5 2
nod /dev/tty3 660 0 0 c 5 3
nod /dev/tty4 660 0 0 c 5 4
nod /dev/tty5 660 0 0 c 5 5
nod /dev/tty6 660 0 0 c 5 6
nod /dev/ttyGS0 660 0 0 c 253 0
nod /dev/ttymxc0 660 0 0 c 207 16
nod /dev/ttymxc1 660 0 0 c 207 17
nod /dev/ttymxc2 660 0 0 c 207 18
nod /dev/ttymxc4 660 0 0 c 207 20
nod /dev/ttymxc3 660 0 0 c 207 19
nod /dev/usb1 660 0 0 c 189 0
nod /dev/watchdog 660 0 0 c 10 130
nod /dev/zero 660 0 0 c 1 5
dir /dev/fb 755 0 0
nod /dev/fb/0 660 0 0 c 29 0
dir /dev/input 755 0 0
nod /dev/input/event0 660 0 0 c 13 64
nod /dev/input/event1 660 0 0 c 13 65
dir /dev/i2c 755 0 0
nod /dev/i2c/0 660 0 0 c 89 0
nod /dev/i2c/1 660 0 0 c 89 1
nod /dev/i2c/2 660 0 0 c 89 2
slink /dev/fb0 /dev/fb/0 777 0 0
slink /init /bin/custom_boot.sh 777 0 0
There are a few things to note with this simple example:
- We are including the custom_boot.sh script that we created at /bin/custom_boot.sh
- We then create a symlink to this file using a path relative to the file structure of the initramfs
Creating an initramfs list
Before we can build the kernel, we need to build any kernel modules that we might need - note that the example kernel module in the initramfs list that we just created points to a file that does not exist yet.
Load the device configuration for your target device:
Code:
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make imx60_wario_defconfig
Prepare and build the kernel modules:
Code:
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make modules_prepare
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make modules -j8
Install the kernel modules to the staging directory:
Code:
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make modules_install INSTALL_MOD_PATH=/initrd
The full set of standard kernel modules can now be found within the staging directory:
Code:
user@ubuntu:~/Git/linux-3.0.35-lab126$ find /initrd/lib/ -name *.ko
/initrd/lib/modules/3.0.35-lab126/kernel/compat-wireless/net/wireless/cfg80211.ko
/initrd/lib/modules/3.0.35-lab126/kernel/compat-wireless/drivers/net/wireless/ath/ath.ko
/initrd/lib/modules/3.0.35-lab126/kernel/compat-wireless/drivers/net/wireless/ath/ath6kl/ath6kl_sdio.ko
/initrd/lib/modules/3.0.35-lab126/kernel/crypto/tcrypt.ko
/initrd/lib/modules/3.0.35-lab126/kernel/fs/fuse/fuse.ko
/initrd/lib/modules/3.0.35-lab126/kernel/fs/nls/nls_ascii.ko
/initrd/lib/modules/3.0.35-lab126/kernel/fs/nls/nls_utf8.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/cyttsp4_core.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/cyttsp4_i2c.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/zforce2.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/cyttsp4_loader.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/cyttsp4_device_access.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/cyttsp4_debug.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/touchscreen/cyttsp4_mt_b.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/misc/als_max44009.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/misc/prox_pic12lf1822.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/keyboard/fsr_keypad.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/input/keyboard/drv26xx_haptics.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/net/ppp_async.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/net/slhc.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/net/wan/mwan.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/net/wan/pkt_monitor.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/net/ppp_generic.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/host/ehci-hcd.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/otg/fsl_otg_arc.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/g_ether.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/arcotg_udc.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/g_file_storage.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/gadget/g_serial.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/serial/usb_wwan.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/serial/option.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/usb/serial/usbserial.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/misc/mxs-perfmon.ko
/initrd/lib/modules/3.0.35-lab126/kernel/drivers/video/mxc/mxc_epdc_fb.ko
/initrd/lib/modules/3.0.35-lab126/kernel/lib/crc-ccitt.ko
If you need a kernel module that isn't available by default, select it and any kernel modules that it depends upon using the configuration menu and rebuild the kernel modules.
Tying it all together
We now have all of the components that we need to create an initramfs.
Using the kernel configuration menu, navigate to
General Setup and set the parameter
Initramfs source file(s) to
/initrd/initramfs-custom.list, then save and exit:
Code:
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make menuconfig
Build the uImage variant of the kernel:
Code:
ARCH=arm CROSS_COMPILE=arm-kindlepw2-linux-gnueabi- make uImage -j8
The finished kernel can be found under
arch/arm/boot/uImage:
Code:
user@ubuntu:~/Git/linux-3.0.35-lab126$ ls -l arch/arm/boot/uImage
-rw-rw-r-- 1 user user 3435104 Nov 28 14:29 arch/arm/boot/uImage
Flashing the kernel
At this point, we have a kernel containing a initramfs containing the files that we specified within our initramfs list. Before flashing this kernel to the device, you should test that it works as expected; a how-to on loading custom kernels can be
found here.
Once you are confident that the kernel works as expected, we can transfer it to the device and flash it using this command:
Code:
dd if=/mnt/us/uImage of=/dev/mmcblk0 bs=4096 seek=65