Run the Linux kernel on QEMU

When developing the Linux kernel, it is quite troublesome to build, install, reboot and test it every time you rewrite it. [QEMU]( saves me some trouble. In this article, I will share my kernel development environment. ## Environment I usually do all my development on EC2. I'm not particular about Linux distributions, so I choose Amazon Linux 2 somehow. OS: Amazon Linux 2 ## Steps I will work in my home directory (~/). Please rewrite each command as necessary. ``` $ WORKDIR=~ ``` ### 1. Get the Linux kernel source code First, let's clone the Linux kernel repository with git. You can download the latest source code of each subsystem from []( I chose the [net-next]( tree of netdev for developing the networking subsystem. Unless you have a specific one, you should select the [linux-next]( tree. ``` $ cd ${WORKDIR} $ sudo yum install -y git $ git clone git:// ``` ### 2. Build the Linux kernel I will emulate the x86_64 architecture with QEMU, so I generated the default configuration for x86_64 in `.config` and built the Linux kernel. You can change the configuration with `make menuconfig`, but in that case, you need to install `ncurses-devel`. ``` $ sudo yum install -y gcc flex bison elfutils-libelf-devel openssl-devel $ cd net-next $ make x86_64_defconfig $ make -j $(nproc) ``` ### 3. Get the Buildroot source code The root file system is required to boot the Linux kernel. I use the [Buildroot]( to create a root file system easily that contains a set of essential commands. ``` $ cd ${WORKDIR} $ git clone git:// ``` ### 4. Build the Buildroot You can build a root file system as follows. At that time, configure the architecture and necessary commands with `make menuconfig`. It will take much time to build, so you may want to spin the washing machine and wash the dishes while streaming your [favourite artist's live]( on YouTube. ``` $ sudo yum install -y ncurses-devel gcc-c++ patch perl-Data-Dumper perl-ExtUtils-MakeMaker perl-Thread-Queue $ cd buildroot $ make menuconfig $ make -j $(nproc) ``` I chose x86_64 architecture to run the Linux kernel on the x86_64 virtualized system. Also, I chose ext4 for a file system because I have been using Ubuntu for a long time, but any file system is OK if it is a read-write file system. If you want to develop Btrfs, choose btrfs. - Target options - Target Architecture - select "x86_64" - Filesystem images - check "ext2/3/4 root filesystem" - ext2/3/4 variant - select "ext4" You may want to run commands in multiple sessions, so I recommend openssh. I also selected curl, iptables, nginx, tcpdump, and python3. - Toolchain - C library - select "glibc" - check "Install glibc utilities" - Target packages - Interpreter languages and scripting - check "python3" - core python3 modules - check "ssl" - Libraries - Crypto - openssl support - ssl library - select "openssl" - openssl - check "openssl binary" - check "openssl additional engines" - Networking - check "libcurl" - checl "curl binary" - SSL/TLS library to use - select "OpenSSL" - Networking applications - check "iptables" - check "nginx" - check "ngx_http_ssl_module" - check "openssh" - check "tcpdump" ### 5. Configure DHCP You must assign an IP address to a virtual machine to connect to the external network. If you do not specify the `-netdev` argument, you can use a built-in DHCP server. (See also: [QEMU - Networking - User-mode networking]( The DHCP configurations are in `/etc/network/interfaces` in the file system (`${WORKDIR}/buildroot/output/images/rootfs.ext4`) created by Buildroot. ``` # interface file auto-generated by buildroot auto lo iface lo inet loopback ``` By default, only is the loopback network interface (lo) assigned an IP address, so you need to put the following in the file to assign an IP address to eth0. ``` auto eth0 iface eth0 inet dhcp ``` Type these commands to mount the root file system and add settings. ``` $ sudo mkdir /mnt/buildroot $ sudo mount -o loop ${WORKDIR}/buildroot/output/images/rootfs.ext4 /mnt/buildroot $ echo -e '\nauto eth0\niface eth0 inet dhcp' | sudo tee -a /mnt/buildroot/etc/network/interfaces ``` ### 6. Configure SSH The default user is the root and no password is set. This is a test environment in QEMU, so you need not set up public-key authentication for another user. I modified `/etc/ssh/sshd_config` as follows to perform SSH login as root without a password. ``` PermitRootLogin yes PermitEmptyPasswords yes ``` Configure `/etc/ssh/sshd_config` in the root file system and umount it. ``` $ sudo sed -i -e 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' \ -e 's/#PermitEmptyPasswords no/PermitEmptyPasswords yes/' \ /mnt/buildroot/etc/ssh/sshd_config $ sudo umount /mnt/buildroot ``` ### 7. Run the Linux kernel on QEMU Once you install QEMU, you can run the Linux kernel with a bit long command. The command changes according to git trees and file systems. This command binds port 10022 on the host with port 22 (SSH) on the guest. ``` $ sudo yum install -y qemu $ qemu-system-x86_64 -boot c -m 2048M \ -kernel ${WORKDIR}/net-next/arch/x86/boot/bzImage \ -hda ${WORKDIR}/buildroot/output/images/rootfs.ext4 \ -append "root=/dev/sda rw console=ttyS0,115200 acpi=off nokaslr" \ -serial stdio -display none \ -nic user,hostfwd=tcp::10022-:22 ``` You can log in as root without a password. ``` Welcome to Buildroot buildroot login: root # uname -r 5.8.0-rc1 ``` From another terminal, you can perform SSH login. ``` $ ssh root@localhost -p 10022 ... # curl -kI HTTP/1.1 200 OK ... ``` To terminate QEMU, press Ctrl+C in the terminal where you launched QEMU. ``` # (press Ctrl+C) qemu-system-x86_64: terminating on signal 2 ``` ### 8. Add an alias I cannot remember the command of course, and it is also troublesome to copy and paste it every time to launch QEMU. So, I created an alias to run QEMU with the `net-next` command. ``` $ echo "alias net-next='qemu-system-x86_64 -boot c -m 2048M -kernel ${WORKDIR}/net-next/arch/x86/boot/bzImage -hda ${WORKDIR}/buildroot/output/images/rootfs.ext4 -append \"root=/dev/sda rw console=ttyS0,115200 acpi=off nokaslr\" -serial stdio -display none -nic user,hostfwd=tcp::10022-:22'" >> ~/.bashrc $ source ~/.bashrc ``` ## Postface When I started kernel development half a year ago, I was rewriting the code and building it again and again. I often misconfigured GRUB or wrote wrong code, resulting in that EC2 has gone away... I hope newbies like me will not have such a bitter experience.