Shivering in this cold winter one would certainly stumble upon the question that the Linux Kernel, which operates most of your operating system, is just a huge C program.
Shouldn’t it, then, be possible to compile and run it as you do to all your C programs ?

Here, I’d be using the famous qemu emulator to run a pre compiled linux kernel. So, let’s begin.

Theory

We all hate this part but it deserves its position.
Here’s how a typical linux boot works after the kernel is loaded.
The kernel initializes and looks for an initramfs. This is a temporary root filesystem. Now, the kernel doesn’t know if your real root is residing on a USB stick or a hard disk or RAID or god-knows-what. It cannot support everything, so it’s the job of initramfs to store the loadable kernel modules and assist the kernel. In short, it complements the kernel.
After the Kernel has initialized keyboard, mouse etc, now it’s the time to mount your hard disk (or say, the root filesystem) and then present you the fancy login screen where you can carry on with your journey.

Get your tools

  • QEMU
  • The Kernel
  • fakeroot

The Kernel

You can find a pre-compiled kernel at /boot/vmlinuz-linux

Initramfs

We’d be creating this one. The tool at our disposal is mkinitcpio

Step zero would be to enter into the fakeroot environment. It makes you appear as root but you’re not. This is required to make a proper initramfs.

$ fakeroot

then create a config file named mkinitcpio.conf with the following contents:

MODULES=(ext4)
HOOKS=(base systemd modconf sd-vconsole filesystems block keyboard)

and the last step is to generate the initramfs

$ mkinitcpio -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -g initramfs.img

Now, you have the initramfs in a file named initramfs.img.

You can now safely exit the fakeroot environent by typing exit.

Boot the kernel using QEMU

After you have your hands on a compiled kernel and initramfs, you’d be eager to boot it in a live environment. We can simulate the same using QEMU.

Looking at man qemu, here are some interesting options

	-kernel bzImage
	Use bzImage as kernel image. The kernel can be either a Linux kernel or in multiboot for‐
	mat.  

   -append cmdline
		  Use cmdline as kernel command line

   -initrd file
		  Use file as initial ram disk.  

Cool, we’ve a kernel image, an initramfs, so why wait ? Let’s boot right in.

$ qemu-system-x86_64 -kernel vmlinuz-linux -initrd initramfs.img

To your surprise, it yields:

no-ram

The Kernel panicked. It typically happens when you try to boot without a RAM.
So, let’s try adding a 1G of RAM.

$ qemu-system-x86_64 -kernel vmlinuz-linux -initrd initramfs.img -m 1G

Another roadblock!

no-hdd

Now, the kernel and the initramfs were done but you’d need a hard disk where everything should carry on i.e. the Operating System.

Add a hard disk

First, we’d be creating a sparse file of 2GB

$ dd if=/dev/zero of=kernel-hd bs=1M count=2048
2048+0 records in
2048+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 9.87468 s, 217 MB/s

Now, a hard disk in itself isn’t enough. You’d need a filesystem (typically called formatting the hard disk). Let’s create one.

$ mkfs.ext4 kernel-hd
mke2fs 1.45.6 (20-Mar-2020)
Discarding device blocks: done
Creating filesystem with 524288 4k blocks and 131072 inodes
Filesystem UUID: 3654d72f-3f6d-4d90-8848-2900d7c271d0
Superblock backups stored on blocks:
	32768, 98304, 163840, 229376, 294912

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

We’ve successfully created a filesystem in the virtual hard disk. Yay!

Boot the kernel with the hard disk

Now, we’ve everything set up. The only task at hand is to plug in the newly created hard disk to a QEMU VM and instruct the kernel to treat that hard disk as the new_root.

$ qemu-system-x86_64 -kernel vmlinuz-linux -initrd initramfs.img -m 1G -hda kernel-hd -append "root=/dev/sda"

no-hdd2 Wait, what !?

Hmm, something is not quite right.
It turns out the kernel cannot continue to boot without some basic files (the OS) in the root filesystem (the hard disk)

Populating the Hard Disk

There are plently of ways you can find or generate the required files (say, a kernel-less Operating System). Here we’ll use the packages provided by Arch.
Feel free to experiment about this.

Install pacstrap by

$ sudo pacman -S arch-install-scripts

Now mount the kernel-hd at /mnt

$ sudo mount kernel-hd /mnt

And populate the hard disk with Arch’s base packages.

$ sudo pacstrap /mnt base

This will start a download of around 115MB. Note, that after this step, you should update your host Arch system soon. (pacman -Syu)

The OS is ready but you’re gonna need some login credentials to log in.
Set a root password in your little OS that you’ve just created.

$ sudo passwd --root /mnt root

And finally unmount kernel-hd

$ sudo umount kernel-hd

Boot our handcrafted system

Rerun the following command

$ qemu-system-x86_64 -kernel vmlinuz-linux -initrd initramfs.img -m 1G -hda kernel-hd -append "root=/dev/sda"

and finally you’ll be greeted with

login-screen

Login with the credentials you set before and enjoy!

WTH ?

Root account is locked

If something goes wrong in the initramfs phase and you see root account is locked, you can unlock the root account by creating a mkinitcpio.conf file as follows:

# Required
MODULES=(ext4)
HOOKS=(base systemd modconf sd-vconsole filesystems block keyboard)

# Unlocking the root account
echo 'root:x:0:0:root:/root:/bin/sh' > /tmp/passwd
echo 'root:$6$drlHj0v2/B7liRyL$YH0ZHsG4d05mS6moBPkdzI5dlt9RjrPbWTCwDk7r5ZCWIGHFEx9A/atj/hPImYPq7qGzi8zGHOKqRgfHSfq7b/:18611:0:99999:7:::' > /tmp/shadow
add_file /tmp/passwd /etc/passwd 0644
add_file /tmp/shadow /etc/shadow 0600

and then regenerating the initramfs.img as shown before.
Remember to enter the fakeroot environment before generating the initramfs.img. This will enable you to login with the password 12345678

Why not use the default /boot/initramfs-linux.img

We have been using the same kernel that your host is using, located at /boot/vmlinuz-linux. So, it might sound better to use the host initramfs as well.
Theoretically, you should be able to just copy and use that but for reasons I’ve faced and bashed my head over multiple times, I’d recommend generating your own initramfs.

Waiting for /dev/sda… or /dev/sda not detected

It might be a qemu issue. You can try emulating SATA as mentioned here.
Basically, you’d need to drop the -hda kernel-hd flag and use the one mentioned in the answer.
Thanks to sheep from freenode IRC for the suggestion.

Why Arch ?

Because it’s the best. Feel free to write your own OS files. Maybe another time.

Still don’t want Arch

Sure, you should head into Linux From Scratch. Also, you may unlock the root account and use journalctl to look into what the kernel is error-ing for.

Don’t want an OS at all

First, unlock the root account and don’t create a Hard Disk at all. You’d be able to log into the initramfs after boot. Explore, enjoy.

Afterwords

Now you know that Linux itself is not an operating system. It’s just a kernel. A part of the operating system. The entire operating system (or distro) consists of the kernel, accompanying initramfs, the root file system and the community.