Creating a boot ramdisk

A Linux boot ramdisk, strictly speaking, an initial RAM filesystem or initramfs, is a compressed cpio archive. cpio is an old Unix archive format, similar to TAR and ZIP but easier to decode and so requiring less code in the kernel. You need to configure your kernel with CONFIG_BLK_DEV_INITRD to support initramfs.

In fact, there are three different ways to create a boot ramdisk: as a standalone cpio archive, as a cpio archive embedded in the kernel image, and as a device table which the kernel build system processes as part of the build. The first option gives the most flexibility because we can mix and match kernels and ramdisks to our hearts content. However, it means that you have two files to deal with instead of one and not all bootloaders have the facility to load a separate ramdisk. I will show you how to build one into the kernel later.

Standalone ramdisk

The following sequence of instructions creates the archive, compresses it and adds a U-Boot header ready for loading onto the target:

$ cd ~/rootfs
$ find . | cpio -H newc -ov --owner root:root > ../initramfs.cpio
$ cd ..
$ gzip initramfs.cpio
$ mkimage -A arm -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk

Note that we ran cpio with the option --owner root:root. This is a quick fix for the file ownership problem mentioned earlier, making everything in the cpio file UID and GID 0.

The final size of the uRamdisk file is ~ 2.9 MiB, with no kernel modules. Add to that 4.4 MiB for the kernel zImage file, and 440 KiB for U-Boot and this gives a total of 7.7 MiB of storage needed to boot this board. We are a little way off the 1.44 MiB floppy that started it all off. If size was a real problem, you could use one of these options:

  • Make the kernel smaller by leaving out drivers and functions you don't need
  • Make BusyBox smaller by leaving out utilities you don't need
  • Use uClibc or musl libc in place of glibc
  • Compile BusyBox statically

Booting the ramdisk

The simplest thing we can do is to run a shell on the console so that we can interact with the device. We can do that by adding rdinit=/bin/sh to the kernel command line. Now, you can boot the device.

Booting with QEMU

QEMU has the option -initrd to load initframfs into memory, so the full command is now as follows:

$ cd ~/rootfs
$ QEMU_AUDIO_DRV=none 
qemu-system-arm -m 256M -nographic -M vexpress-a9 -kernel zImage -append "console=ttyAMA0 rdinit=/bin/sh" -dtb vexpress-v2p-ca9.dtb -initrd initramfs.cpio.gz

Booting the BeagleBone Black

To boot the BeagleBone Black, boot to the U-Boot prompt and enter these commands:

fatload mmc 0:1 0x80200000 zImage
fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb
fatload mmc 0:1 0x81000000 uRamdisk
setenv bootargs console=ttyO0,115200 rdinit=/bin/sh
bootz 0x80200000 0x81000000 0x80f00000

If all goes well, you will get a root shell prompt on the console.

Mounting proc

Note that the ps command doesn't work: that is because the proc filesystem has not been mounted yet. Try mounting it and run ps again.

A refinement to this setup is to write a shell script that contains things that need to be done at boot-up and give that as the parameter to rdinit=. The script would look like the following snippet:

#!/bin/sh
/bin/mount -t proc proc /proc
/bin/sh

Using a shell as init in this way is very handy for quick hacks, for example, when you want to rescue a system with a broken init program. However, in most cases, you would use an init program, which we will cover further down.

Building a ramdisk cpio into the kernel image

In some cases, it is preferable to build the ramdisk into the kernel image, for example, if the bootloader cannot handle a ramdisk file. To do this, change the kernel configuration and set CONFIG_INITRAMFS_SOURCE to the full path of the cpio archive you created earlier. If you are using menuconfig, it is in General setup | Initramfs source file(s). Note that it has to be the uncompressed cpio file ending in .cpio; not the gzipped version. Then, build the kernel. You should see that it is larger than before.

Booting is the same as before, except that there is no ramdisk file. For QEMU, the command is like this:

$ cd ~/rootfs
$ QEMU_AUDIO_DRV=none 
qemu-system-arm -m 256M -nographic -M vexpress-a9 -kernel zImage -append "console=ttyAMA0 rdinit=/bin/sh" -dtb vexpress-v2p-ca9.dtb

For the BeagleBone Black, enter these commands into U-Boot:

fatload mmc 0:1 0x80200000 zImage
fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb
setenv bootargs console=ttyO0,115200 rdinit=/bin/sh
bootz 0x80200000 – 0x80f00000

Of course, you must remember to rebuild the kernel each time you change the contents of the ramdisk and regenerate the .cpio file.

Another way to build a kernel with ramdisk

An interesting way to build the ramdisk into the kernel image is by using a device table to generate a cpio archive. A device table is a text file which lists the files, directories, device nodes, and links that go into the archive. The overwhelming advantage is that you can create entries in the cpio file that are owned by root, or any other UID, without having root privileges yourself. You can even create device nodes. All this is possible because the archive is just a data file. It is only when it is expanded by Linux at boot time that real files and directories get created, using the attributes you have specified.

Here is a device table for our simple rootfs, but missing most of the symbolic links to busybox to make it manageable:

dir /proc 0755 0 0
dir /sys 0755 0 0
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
nod /dev/null 0666 0 0 c 1 3
nod /dev/ttyO0 0600 0 0 c 252 0
dir /bin 0755 0 0
file /bin/busybox /home/chris/rootfs/bin/busybox 0755 0 0
slink /bin/sh /bin/busybox 0777 0 0
dir /lib 0755 0 0
file /lib/ld-2.19.so /home/chris/rootfs/lib/ld-2.19.so 0755 0 0
slink /lib/ld-linux.so.3 /lib/ld-2.19.so 0777 0 0
file /lib/libc-2.19.so /home/chris/rootfs/lib/libc-2.19.so 0755 0 0
slink /lib/libc.so.6 /lib/libc-2.19.so 0777 0 0
file /lib/libm-2.19.so /home/chris/rootfs/lib/libm-2.19.so 0755 0 0
slink /lib/libm.so.6 /lib/libm-2.19.so 0777 0 0

The syntax is fairly obvious:

  • dir <name> <mode> <uid> <gid>
  • file <name> <location> <mode> <uid> <gid>
  • nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>
  • slink <name> <target> <mode> <uid> <gid>

The kernel provides a tool that reads this file and creates a cpio archive. The source is in usr/gen_init_cpio.c. There is a handy script in scripts/gen_initramfs_list.sh that creates a device table from a given directory, which saves a lot of typing.

To complete, the task, you need to set CONFIG_INITRAMFS_SOURCE to point to the device table file and then build the kernel. Everything else is the same as before.

The old initrd format

There is an older format for a Linux ramdisk, known as initrd. It was the only format available before Linux 2.6 and is still needed if you are using the mmu-less variant of Linux, uCLinux. It is pretty obscure and I will not cover it here. There is more information in the kernel source, in Documentation/initrd.txt.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.131.38.210