Sunday, May 10, 2015

Preparing a test VM - Kernel

In previous post I talked about preparing a filesystem with Gentoo. Today I will talk about compiling the Linux kernel.

In normal Gentoo setup you emerge (install like "apt-get install" in Ubuntu) the kernel from Gentoo repo and compile it on the machine you are installing Gentoo.

We will be doing something different. Using virtualization, we will feed the kernel binary files from the host to the VM. The main reason is we need the compiled kernel files in our development environment to compile modules for that kernel. Kernel source and the module files should be located in the same machine.

To compile our kernel and modules we need a Linux machine with gcc and decent CPU power. Then we need to obtain the Linux kernel source.

Obtain Linux source code


There are two ways to do that:

One is to download a tar.xz file from kernel.org. These files are typically around a hundred megs.

Second way is to clone the Linux kernel git repository (git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git). Cloning takes much longer (and more than 1 GB of disk space) however it brings all the tags and commits. You can switch between different versions (tags). And you can obtain extra information from commits using git blame (like why a certain piece of code is introduced or changed). I recommend checking out stable Linux branches if you want to work on an older but stable code base like me (git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git)

I don't think you will need the whole git repo in the beginning so I advise downloading and extracting one of the tar.xz files (by issuing tar -xJf linux-abc.tar.xz). You can use the mainline or one of the stable releases. My code will be based on 3.14 branch.

We are going to learn how to compile a kernel in this article but first let's consider what we will get at the end of the compiling process.

Kernel image and modules


One thing you will hear about a lot is that the Linux kernel is monolithic. What does that mean? Does it mean that Linux kernel is not modular? But what about the so-called "kernel modules" you have been hearing about?

What is meant by monolithic is that the whole operating system kernel is compiled into a unified binary. Yes, there is one kernel image and many little module files but they don't work like a user-space executable and dynamic libraries. When you compile the Linux kernel all the binary files produced work together as one big executable file.

Modules are compiled "against" a kernel image. You can't expect a module compiled for a kernel version to work when plugged into another version. You can't even expect a module compiled against a kernel image to work with another kernel image with same version if the two kernel images have different configs.

Basically a kernel is in fact a huge binary which consists of a kernel image which can run by itself plus smaller module files. All those files combined forms the kernel, the only difference is the modules need to be uploaded to the memory separately before you run the code inside them.

Another way of saying this; if you don't need to run some part of the kernel code immediately, you mark it to be compiled as a module in menuconfig. When you need to run that code, you first load that part onto the memory by using insmod.

In our case however we want all the stuff we are going to run inside the binary. Otherwise we will need to copy the modules we need from development host to the VM's rootfs every time we recompile the kernel image. So be sure to mark the options you want to have as <*>, NOT as <M>.

Configure and compile


We need to check a few things before we try to run our kernel inside a VM. So type make menuconfig to enter the enormous world of kernel options. Just browsing the menus is a big step towards learning an operating system kernel; you can see how many things Linux can do.

The choices in the menu are already marked for the development PC's configuration. That will be fine, we won't need to change too many options.

  • Make sure you are compiling e1000 driver which we are going to use. It is in Device Drivers -> Network device support -> Ethernet driver support ->  Intel(R) PRO/1000 Gigabit Ethernet support.
  • Unmark any options related to your graphics card if any. You only need Cirrus QEMU driver. Go to Device Drivers -> Graphics Support and choose Direct Rendering Manager->Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) option. Then choose DRM Support for bochs dispi vga interface (qemu stdvga) and from Console driver support -> VGA text console. This is the new graphics driver for QEMU, do not use "Cirrus driver for QEMU emulated device". You don't need to set anything in "Support for framebuffer devices section", like you would for a physical card. You can also disable all other hardware specific options like GPU switching, AGP or SoC/LCD specific modules (they are for embbedded systems).
  • You will need to enable keyboard from Device Drivers -> Input device support -> Keyboards -> AT keyboard if you are going to use a VGA monitor instead of serial console.
  • For serial console access you need Device Drivers -> Character devices -> Serial drivers -> 8250/16550 support -> Console on 8250/16550 support. Other options for PCI or DMA are not necessary.
  • Make sure Virtualization is marked in main menu and KVM support is enabled for your processor. You can disable any options regarding the host. It would be good to enable all the guest options you might need. You need some experience and knowledge on virtualization to understand what each option changes. I can't write every possible option here so you are on your own. But you should be okay as long as test environment boots.
  • Go to Kernel Hacking -> printk and dmesg options at top. Make sure show timing info on printk is set and set the default log level to 7 to see every output of the kernel. (like setting it to the maximum verbose mode). Note that this only changes the log level until userspace is initialized. Make sure early printk is enabled.

Then issue make -jN command where N is the number of threads you want to use while compiling the kernel. It is usually advised to use the number of your host's cores + 1 (like make -j9 for an eight core computer). Using a larger number won't make a considerable difference most likely.

Well, to be honest, compiling a kernel from scratch can become quite a complicated job. For now if you can just boot the machine it is okay. I hope to write another short article about vital points for new developers in kernel configuration.

Exercise:


Until that article, a good exercise is to remove every option you don't need from the kernel. You will need to decide for each module whether to remove it or compile it into your binary.

It may not be possible to understand every option in the menuconfig even after you are comfortable with writing kernel level code. But you can try to remove every electronic device and every approach/algorithm whose name you don't know.

If your kernel works perfectly afterwards that means you didn't need to know those names already. And if it fails to run than you just found something integral to making your computer work and therefore you should have known before.

It will take a lot of time taking out stuff and compiling and testing your kernel configuration. Sometimes finding out what is missing may be quite painful.  But as I said it is a good and educational exercise.

It will also reduce the compilation time as a bonus :)

See you until next article...

No comments:

Post a Comment