In these series of blog posts, I’m going to be documenting my experiences learning about and contributing to the Linux Kernel.
I’m pretty familiar with Linux, having used it as my primary OS for a few years, but I’ve never messed around with the Kernel before. It always seemed like this enormous, very complicated and very intimidating black box that was more of an abstract concept for me in day-to-day usage than it was an actual project with files and code I might understand and contribute to. I’m glad that this class gave me both a reason and a starting point to learn about this rich enviroment.

This first entry will focus on how I setup my development enviroment for the Kernel and the many things that went wrong along the way, mostly due to my excellent initial idea of attempting to learn Linux Kernel development on Windows (which itself was mostly because I was worried my laptop didn’t have enough storage space for a dual-boot partition).
I followed the tutorials developed for this class, which were a great help getting started.

WSL2 for kernel development: not the best idea

Like I mentioned before, yeah, I did start out by attempting to follow the tutorials that were very much written for Linux on my Windows laptop. Not the best idea, but to be fair, it did work out in the beginning: WSL2 is much closer to an actual linux system than WSL1 and I figured that since I would be installing and testing my builds of the kernel in a virtual machine anyways, it wouldn’t matter if the host system was a “real” linux enviroment or not.

I managed to get through the first few tutorials and even setup my VM before I hit a wall with some apparently unfixable errors and eventually found out that while it’s true that WSL2 even has its own kernel and looks like a proper Linux system, it doesn’t seem to contain any kernel modules at all and instead apparently uses custom built-in drivers from Microsoft. At this point, I figured that it would be less of a hassle to clean up my laptop’s storage and get a small Manjaro partition up and running than it would be to keep working around the limitations my current setup, so I did just that. Everything documented here from here on out was done in a clean Manjaro install.

Setting up an enviroment to work on the kernel

For this class, the first tutorial was aimed at setting up an arm64 virtual machine that would be our testing enviroment for future kernel modifications. As I’d done this tutorial on my WSL2 enviroment, it wasn’t much of a hassle. I did run into two annoying issues: the first was with user permissions for libvirt and the second was with setting up SSH access inside the VM. Libvirt seemed to have some issues with access permissions to the directory I was using for my VM, so I had to create an user group that I named kerneldev, add myself and the libvirt-qemu user to it and change the ownership of the folders that causing the problem to kerneldev. With the initial emulation working, I dumped the VM’s kernel to a folder on the host and setup libvirt to use that path as the kernel for our VM. This didn’t change anything right now, but it meant I could later easily replace that kernel with one I’d built to test it in the VM.

After getting the VM registered in virsh and up and running, I wanted to have SSH access to it so I could easily move files in and out as needed. To do this, I first had to change the PermitRootLogin setting on the sshd_config file, but after I’d done so one of the commands I ran later updated openssh and asked me if I wanted to replace my config file with an updated version. I said no, wanting to keep my changes and well, turns out the updated version refused to work with the old config, so I ended up reinstalling. This time I waited to change the PermitRootLogin setting after replacing the default config with the updated version and it worked perfectly. With this, I now had an ARM64 VM that’d boot with a specific kernel image and could move on to the next tutorial: building the kernel and testing it out.

Building the Linux kernel for ARM

To build the kernel, the first step I had to do was clone its sourcecode so I had what to build in the first place. As our tutorials were aimed at the IIO (Industrial I/O) subsystem, I cloned the appropriate testing branch and attempted my first build, which failed almost immediately: I hadn’t set up the ARCH and CROSS_COMPILE enviroment variables correctly and had some dependecies missing. With that fixed, I built a new default config file and started the kernel image build again, this time successfully. Unlike the kernel image, which the VM had been setup to load from a folder outside the VM, the kernel modules had to be installed inside the VM’s file system. To do that, I mounted it’s .qcow2 file in a local folder and ran make INSTALL_MOD_PATH=$VM_DIR/mountpoint_arm64/ modules_install before unmounting the file to get the VM ready to boot. With all that done, I started up the VM and was happily surprised when it worked straight away, with no errors and a quick check with uname -a confirming that I was running the kernel I’d just built.

Learning about modules and kernel build configuration

The last tutorial of this first introductory batch was about kernel modules and build configuration. I didn’t run into any issues with this tutorial, as it was less about setting something up like the first two and more about learning how to tweak the configuration files to build specific modules. I created and built the simple example module from the tutorial and it was pretty exciting watching it load in the VM, as it was the first actual change I’d made to the kernel so far.

Next steps

With a fully functional testing and development enviroment ready, it was time to go to the next set of tutorials, this time aimed at learning the basics of the kernel’s IIO subsystem. These, unlike the previous, which were very hands on and technical, were mostly reading, so there isn’t much to write here about them. Then it was time for my project, which is the topic of the next post. Thanks for reading!