Skip to content

Instantly share code, notes, and snippets.

@RealYukiSan
Last active April 21, 2024 12:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RealYukiSan/c69d9cc9120c1e5d7b5afcf371e3f79d to your computer and use it in GitHub Desktop.
Save RealYukiSan/c69d9cc9120c1e5d7b5afcf371e3f79d to your computer and use it in GitHub Desktop.
Play with linux system on QEMU

The idea came from Nir Lichtman video with the title: 'Making Simple Linux Distro from Scratch'

this gist just re-document it in the form of text

Requirement

  • compiled linux kernel
  • compiled static-linked busybox
  • installed qemu

Creating initramfs

install busybox to specific directory, I suggest to use /tmp/distro as working directory

and on the busybox source code directory:

make CONFIG_PREFIX=/tmp/distro/initramfs install

delete linuxrc file and replace it with init file

cat > ./initramfs/init << "EOF"
#!/bin/sh
/bin/sh
EOF

compress initramfs with cpio:

cd initramfs
find . | cpio -o -H newc > ../init.cpio

you can cross-check the content of init.cpio with cpio -v -t -F init.cpio

Creating boot image

on linux, you'll need to install dosfstools package to be able create FAT file system

dd if=/dev/zero of=boot.img bs=1M count=50
mkfs -t fat boot.img
mount boot.img /mnt
syslinux -i /mnt

Use a loopback device, which allows a disk file to be treated as a device. Using a loopback device you can create a three megabyte file on your hard disk and build the filesystem on it.

TIL, such file is called loop-back device.

Try to mount the image to /mnt/whatever and execute command findmnt -A to see the loop-back device, reference.

Run it on QEMU

qemu-system-x86_64 -drive format=raw,file=./boot.img,index=0,media=disk \
-initrd ./init.cpio -kernel ./bzImage \
-nographic -enable-kvm  -append "root=/ console=ttyS0"

tested on arch linux.

Alternative

  • You can use TigerVNC to connect to your guest OS
  • You can place bzImage and init.cpio to the boot image instead of pass it as parameter to qemu command

just mount the boot image, copy the file, and unmount

but you'll need to specify the path to bzImage later when running the guest, the prompt looks like this:

boot: <path-to-kernel-image>
boot: /bzImage -initrd=/init.cpio

see other kernel parameter option (initrd, console, root):

curl -L -s https://raw.githubusercontent.com/torvalds/linux/master/Documentation/admin-guide/kernel-parameters.txt | less

Note

instead of provide your custom init shell script, it is possible to work with default init by busybox. You just need to provide the /dev directory and other default standard linux directory for Virtual File System + /etc/fstab, see LFS chapter 7.3.

you can use extlinux instead of syslinux for bootloader on ext file system.

you can use truncate command instead of dd.

if you are not providing bootloader configuration, you'll need to specify the path of kernel image.

also, it's possible to build linux system without initramfs, so no need to create archived cpio file and instead, just install the busybox to the disk image or other drive.

when emulate the linux system on QEMU make sure the disk image have read and write permission and it is recommended to pass rw to kernel parameter.

Unanswered Question

Does the following statement from tldp:

When the kernel is completely loaded, it initializes device drivers and its internal data structures. Once it is completely initialized, it consults a special location in its image called the ramdisk word. This word tells it how and where to find its root filesystem. A root filesystem is simply a filesystem that will be mounted as ``/''. The kernel has to be told where to look for the root filesystem; if it cannot find a loadable image there, it halts.

have something to do with the Creating Boot Image and the kernel parameter root?

what's relation between firmware of BIOS/UEFI and the partition layout table such as MBR or GPT?

Link and References

@RealYukiSan
Copy link
Author

RealYukiSan commented Apr 15, 2024

I'm curious what kernel parameters or command line options are passed when the kernel is booted on my current linux distro, and did a little searching.

in the linux distro that I use, it turns out that it is in the grub configuration in the /boot/grub/grub.cfg file. specifically located in the menuentry 'linux' section

apart from that, I also tried to find out how initramfs works in Linux, and here's the explanation from official documentation

it states that after initramfs is extracted, the kernel will look for the init file, so I wanted to make sure it was in the linux distro that was being used.

to be able to confirm it, I need to extract the initramfs image in the /boot directory:

sudo zstd -d /boot/initramfs-linux.img -o /tmp/decompressed.img
sudo cpio --verbose -t -F /tmp/decompressed.img | less

and indeed it exists!

@RealYukiSan
Copy link
Author

RealYukiSan commented Apr 15, 2024

I notice that previously the init was using simple shell script instead of linuxrc from the busybox, you can take a look the init.c on the mirror git repo

the arch distro using shell script for init, take a look at decompressed cpio:

sudo cpio --verbose -i -F ./decompressed.img
less ./init

related link:

@RealYukiSan
Copy link
Author

RealYukiSan commented Apr 15, 2024

it's kinda unique that busybox using soft-link and applet mechanism to refer to a specific program

@RealYukiSan
Copy link
Author

RealYukiSan commented Apr 15, 2024

I think the reason Nir Lichtman replace the default init from busybox is for the sake of simplicity, the busybox itself mention about it on their FAQ about building full bootable system

@RealYukiSan
Copy link
Author

also see other gist

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment