This week, I decided to do a deeper dive into the Linux kernel, actually compiling and installing a new version on my PC. I’m in hacker’s paradise.
A good resource on the Linux kernel that I’ve been ignoring for quite some time is the Linux Kernel Newbies site. Clicking on the tutorial link will take you to submitting your first kernel patch, and in there you’ll see a link directing you to Developing on a Native Linux platform. Let’s take these one step at a time, starting with the latter setup instructions.
After setting up the program prerequisites with the command:
sudo apt-get install vim libncurses5-dev gcc make git exuberant-ctags libssl-dev bison flex libelf-dev bc
you set up your own kernel repository by creating a kernels directory, and then cloning Greg Kroah-Hartman's staging tree into it:
mkdir -p git/kernels
cd git/kernels
git clone -b staging-testing git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
cd staging
Now I get to compile my own kernel. But first, I get to configure the kernel to my heart’s content, using the menuconfig command, that displays the following screen:
I could spend hours researching what some of these options are. Each of these menus goes to a submenu that each contain configuration options, some of which contain submenus themselves. These layers of customization boggle my mind. For example, the Kernel hacking submenu above itself lists a rich set of options that you can change and have fun with:
I guess that is the essence of Linux: having ultimate, open-source control to do whatever you want. It’s pretty cool.
Having to wipe the drool off my chin, it was time to proceed with the compile. I’ll have to come back to menuconfig sometime in the future.
I issue the command “make -j16”, ignoring the warning of “Where X is a number like 2 or 4. If you have a dual core, 2 or 3 might be good. Quad core, 4 or 6. Do not run with really big numbers unless you want your machine to be dog-slow!” in the tutorial – after all, the tutorial seems quite dated, and for sure by now kernel compiles can take advantage of the 16 threads on my AMD Ryzen CPU! And yes, it runs pretty quickly, finished in about 10 minutes (although, to be honest, I didn’t get the exact time, because I had to go walk the dog in the interim):
Now, it’s time to install this kernel, with the command:
sudo make modules_install install
This took about ten minutes, too, to complete:
Fortunately, Linux stores both old and new kernel versions, so you can boot into your old kernel if you run into issues with your new kernel. The bootloader program called grub lets you choose which kernel you want to boot into. But, first, I had to do some editing of the /etc/default/grub file, as per the tutorial, to make sure that the grub boot menu always appear under Ubuntu. Then, it was simply a matter of rebooting, and then choosing “Advanced options for Ubuntu” to select from the list of kernels to boot:
I’ve ended up installing the 4.20.0-rc1+ kernel a couple of times, per my experimentation, as you can see. As well, I’ve got different versions of the kernel already installed, presumably as part of updates to Ubuntu 16.04 that I have on this machine.
I can boot into any of these kernels, and explore any differences. That’s for a later time. I expect that the differences are subtle – although the release candidate one at the top probably has a bunch of new capabilities that I will delve into at some point. But for now, I want a stable kernel, so I pick the 4.15.0-42-generic.
Next step is to explore the contents of the staging directory, by doing a “cd git/kernels/staging”, which you can see both from the command line and the Ubuntu GUI:
I then went about editing the e1000_main.c that is in the /git/kernels/staging/drivers/net/ethernet/intel/e1000 directory, and added the line
printk(KERN_DEBUG “I can modify the Linux kernel!\n”);
And then I launched the make again with the “make -j16” command.
Oh oh! A compile error. It doesn’t like me mixing code with declarations. I guess that makes sense. The tutorial did warn me that I might get some compile errors – little did I know that it would be the source of those errors! But it’s just a warning, and it appears that the compile was successful.
Now it’s time to install the kernel again using the “sudo make modules_install install” command, launch it, and use the dmesg command to see if my kernel code change was successful.
After the install, I launched my new kernel with grub (using the same process as above), and after doing a “dmesg | less”, I don’t see my printk! Even after searching for it using “/I can modify the Linux kernel!”, there’s no sign of it. Why not? Well, in the not-so-fine-print of the tutorial, it says that the e1000 driver will not necessarily be running on my hardware; it seems to be used more commonly in VMs. So, I need to find some other module that is executed in kernel, and hack that code instead. Either that or do my kernel hacking in a VM. That’s the topic of Part 2 of this article.
Want more of the MinnowBoard Chronicles in the meantime? You can see the first 31 episodes in our eBook.