Kernel Image for Emulation
We will create a tiny Linux system with absolute minimum additional software. We reuse host kernel image vmlinuz to simply the process.
cp /vmlinuz vmlinuz
vmlinux is a ELF format based Linux kernel executable image and vmlinuz (Virtual Memory Linux gZip) is a compressed version of vmlinux. At the beginning of vmlinuz is a routine that does some minimal amount of hardware setup and then decompresses the kernel contained within the kernel image and places it into high memory. The routine then calls the kernel and the kernel boot begins.
Initramfs
We need a temporary root file system, an initramfs when the kernel boots up. An initramfs is a cpio compressed archive. Unlike the old initrd, initramfs reuse disk cache mechanism of main memory so initramfs doesn’t require creating a synthetic block device nor having a intermediate file-system driver inside the kernel to interpret the data which may have license problem and avoid unnecessarily copying memory from the fake block device into the page cache. Bootloader places initramfs image(s) in a memory location and then pass its location and its size as parameter to kernel. The kernel checks for the presence of the initramfs and if found it is unpacked into a RAM disk at kernel initialization, mounts it as /. Once initramfs is extracted, into rootfs the kernel checks if it contains a file /init. If it does, the kernel executes this /init file.
mkdir simple
cd simple
Shell Environment
sudo apt install busybox-static
We have to install busybox-static, busybox does not install symbolic links for any of the supported utilities.
mkdir -p bin sbin usr/bin usr/sbin
cp /bin/busybox bin/busybox
ln -s busybox bin/sh
Configure Keyboard Layout for Spanish
We have to install the list of keymaps
sudo apt install kbd console-data
Convert to binary format and copy to our initramfs
loadkeys -b /usr/share/keymaps/i386/qwerty/es.kmap.gz > es.kmap
Support USB Devices
We repeatedly use this command sudo lsmod | grep usb_storage to recursively find all modules that are required for USB hot-plug. They are the following modules in total
- ohci-hcd
- ohci-pci
- uas
- usb_storage
- usbcore
- scsi_mod
- usb_common
- sd_mod
- fat
- vfat
- nls_* e.g. nls_cp437, nls_ascii
We then use sudo modinfo [module name] to find each module path and copy to our initramfs.
mkdir -p lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/host/ohci-hcd.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/host/ohci-pci.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/storage/uas.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/storage/usb-storage.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/core/usbcore.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/core/usbcore.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/scsi/scsi_mod.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/usb/common/usb-common.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/drivers/scsi/sd_mod.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/fs/fat/vfat.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/fs/fat/fat.ko lib/modules/$(uname -r)
cp /lib/modules/$(uname -r)/kernel/fs/nls/*.ko lib/modules/$(uname -r)
Init Script
We will require a /init program, it is typically a shell script. It configures some basic device nodes and directories, mounts the special /sys and /proc file systems, and starts the processing of hotplug events using mdev and then execute the more-typical /sbin/init program.
nano init
and put following content
#!/bin/sh
# install symlinks for all of the suppoted utilities
# install busybox symbolic links
/bin/busybox --install -s
#
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir --mode=0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock
#
mount -t sysfs none /sys -onodev,noexec,nosuid
mount -t proc none /proc -onodev,noexec,nosuid
#
mknod /dev/zero c 1 5
mknod /dev/null c 1 3
mknod /dev/tty c 5 0
mknod /dev/console c 5 1
mknod /dev/ptmx c 5 2
mknod /dev/tty0 c 4 0
mknod /dev/tty1 c 4 1
# load USB drivers
depmod -a # https://stackoverflow.com/q/45658297/9980245
modprobe ohci-pci
modprobe uas
modprobe sd_mod
# initial population and dynamic updates
echo "/sbin/mdev" > /proc/sys/kernel/hotplug
/sbin/mdev -s
# change keymaps http://ilikelinux69.github.io/How-to-change-the-keyboard-layout-in-Busybox
loadkmap < es.kmap
# traditional init program
exec /sbin/init
add the execution permission
chmod a+x init
find . | cpio --quiet -o -H newc | gzip >../simple.igz
The result simple.igz is our initramfs.
Install Busybox Symbolic Links
The busybox provide a –install options to create symbolic links (-s) for all the supported commands in /bin, /sbin, /usr/sbin. Here are the list of command that busybox install.
-
ls -al /bin | grep “->” | less
- arch
- ash
- cat
- chgrp
- chmod
- chown
- cp
- cpio
- cttyhack
- date
- dd
- df
- dmesg
- dnsdomainname
- dumkmap
- echo
- ed
- egrep
- false
- fatattr
- fgrep
- getopt
- grep
- hunzip
- gzip
- hostname
- ionice
- ipcalc
- kill
- link
- linux32
- linux64
- ln
- login
- ls
- lzop
- mkdir
- mknod
- mktemp
- more
- mount
- mt
- mv
- netstat
- nuke
- pidof
- ping
- ping6
- ps
- pwd
- readlink
- resume
- rev
- rm
- rmdir
- rpm
- run-parts
- sed
- setpriv
- sh
- sleep
- slat
- stty
- su
- sync
- tar
- touch
- true
- umount
- uname
- uncompress
- usleep
- vi
- watch
- zcat
-
ls -al /sbin | grep “->” | less
- acpid
- adjtimex
- arp
- blockdev
- depmod
- devmem
- fdisk
- freeramdisk
- fstrim
- getty
- halt
- hwclock
- ifconfig
- ifdown
- ifup
- init
- insmod
- ip
- ipneigh
- klogd
- loadkmap
- logread
- losetup
- lsmod
- mdev
- mkdosfs
- mke2fs
- mkswap
- modinfo
- modprobe
- nameif
- pivot_root
- poweroff
- reboot
- rmmod
- route
- run-init
- start-stop-daemon
- sulogin
- swapon
- switch_root
- sysctl
- syslogd
- tc
- tunctl
- udhcpc
- uevent
- vconfig
- watchdog
-
ls -al /usr/bin | grep “->” | less
- [
- [[
- ar
- awk
- basename
- bc
- blkdiscard
- bunzip2
- bzcat
- bzip2
- cal
- chvt
- clear
- cmp
- crontab
- cut
- dc
- deallocvt
- diff
- dirname
- dos2unix
- dkpg
- dkpg-deb
- du
- dumpleases
- env
- expand
- expr
- factor
- fallocate
- find
- fold
- free
- ftpget
- ftpput
- groups
- head
- hexdump
- hostid
- id
- killall
- last
- less
- loggger
- logname
- lsscsi
- lzcat
- lzma
- md5sum
- microcom
- mkfifo
- mkpasswd
- nc
- nl
- nproc
- nsenter
- nslookup
- od
- openvt
- passwd
- paste
- patch
- printf
- realpath
- renice
- reset
- rpm2cpio
- seq
- setkeycodes
- setsid
- sha1sum
- sha256sum
- sha512sum
- shred
- shuf
- sort
- ssl_client
- strings
- svc
- svok
- tac
- tail
- taskset
- tee
- telnet
- test
- tftp
- time
- timeout
- top
- tr
- traceroute
- traceroute6
- truncate
- tty
- unexpand
- uniq
- unix2dos
- unlink
- unlzma
- unshare
- unxz
- unzip
- uptime
- uudecode
- uuencode
- w
- wc
- wget
- which
- who
- whoami
- xargs
- xxd
- xz
- xzcat
- yes
Initial Population and Dynamic Updates
We instruct the kernel to execute /sbin/mdev whenever a device is added or removed so that the device node can be created or destroyed. Then we seed /dev with all the device nodes that were created while the system was booting.
Traditional Init Program
At the end of /init we call /sbin/init program which coordinates the rest of the boot process and configures the enviroment for the user. It becomes the parent or grandparent of all the processes that start up automatically on the system. In our case /sbin/init is our busybox console. At this point we have two busybox running one executing the /init and second one - our console /sbin/init that is executed by /init.
Test the Constructed Image
cd ..
Run with QEMU Emulator
Install the QEMU
sudo apt install qemu-system
Execute the kernel with host architecture e.g. x86_64.
qemu-system-x86_64 -vnc :0 -kernel vmlinuz -initrd simple.igz -append "root=/dev/ram" /dev/zero
Booting a real machine
The syslinux package allows us to construct bootable systems for standard PCs on DOS-formatted storage.
sudo apt install syslinux
A suitable medium should be chosen to boot from, e.g., a DOS-formatted USB flash drive. The DOS partition of the USB flash drive must be marked bootable. Some USB flash drives might need repartitioning and reformatting with the Linux tools in order to work correctly.
Syslinux Boot Process Overview
- Stage 1 - Part 1 - Load MBR: At boot, the BIOS loads the 440 byte MBR boot code at the start of the disk (/usr/lib/syslinux/bios/mbr.bin).
- Stage 1 - Part 2 - Search active partition: The MBR boot code looks for the active partition that is marked with boot flag. e.g. /boot partition.
- Stage 2 - Part 1 - Execute volume boot record: The MBR boot code executes the Volume Boot Record (VBR) of the /boot partition. In the case of Syslinux, the VBR boot code is the starting sector of /boot/syslinux/ldlinux.sys which is created by the extlinux –install command.
- Stage 2 - Part 2 - Execute /boot/syslinux/ldlinux.sys: - The VBR will load the rest of /boot/syslinux/ldlinux.sys. The sector location of /boot/syslinux/ldlinux.sys should not change, otherwise syslinux will not boot.
- Stage 3 - Load /boot/syslinux/ldlinux.c32: The /boot/syslinux/ldlinux.sys will load the /boot/syslinux/ldlinux.c32 (core module) that contains the rest of the core part of syslinux that could not be fit into ldlinux.sys (due to file-size constraints). The ldlinux.c32 file should be present in every Syslinux installation and should match the version of ldlinux.sys installed in the partition.
- Stage 4 - Search and Load configuration file: - Once Syslinux is fully loaded, it looks for /boot/syslinux/syslinux.cfg and loads.
Config for syslinux
The syslinux program should be run on the device something similar
to /dev/sdx1 for a USB flash drive. You should be
careful, as selecting the wrong devide name might overwrite your
host system’s hard drive. The syslinux loader can be
configured
using a file called syslinux.cfg
nano syslinux.cfg
which would look something like:
Default simple
timeout 100
prompt 1
label simple
kernel vmlinuz
append initrd=simple.igz root=/dev/ram
Burning the Bootloader
We give our user permission that allow syslinux to write code to MBR. We add our username to disk group which have raw access to disks with usermod.
sudo usermod -G disk -a $(whoami)
We execute this command only once, the change is permanent. We now mount the USB devices and copy the remaining archives to normal file system, we can use dmesg to find sdX1.
sudo syslinux /dev/sdX1
# Mount hand if not magicaly mounted when connected.
sudo mkdir /mnt/test
sudo mount -t vfat /dev/sdX1 /mnt/test
# And then work with /mnt/test
sudo cp vmlinuz simple.igz syslinux.cfg /mnt/test
sudo umount /mnt/test
The device may now be removed and booted on an appropriate PC.
Test with VirtualBox
- Download Plop Boot Manager
- Extract plpbt.iso
- From Settings -> Sorage -> Controller: IDE -> Add -> Select plpbt.iso
- From Settings -> System -> Boot Order -> Ensure Optical is before any other devices
- From Plop boot menu select USB device
When we finish booting from USB, we can unplug USB. All content, kernel and initramfs is available in the main memory, we don’t use USB anymore. We could not access the content of our USB since there is no corresponding driver for USB and file system.
Mount USB
Since there is no udev running, we have to create block device node in /dev manually. First check with dmesg if drivers detect USB and which identifier it has e.g. sda1 and then create our block device manually which following parameters for sda1, if identifier is diferrent you have to check here.
mknod /dev/sda b 8 0
mknod /dev/sda1 b 8 1
mkdir -p /mnt/test
mount -t vfat /dev/sda1 /mnt/test
Reference
- Un Sistema Linux Empotrado Sencillo
- An introduction to the Linux boot and startup processes
- A DETAILED LOOK AT THE BOOT PROCESS
- Disk Group
- Anatomy of the initrd and vmlinuz
- Ramfs, rootfs and initramfs
- Package: busybox
- QEMU Example: Deploying initramfs