Building & Modifying Linux Kernel

For a normal operation, we don’t really need to customize our own kernel. But sometimes we might need to do as I will mention later here. Beside it gives a very good understanding of system and usually lots of fun involved with that.

  • To trim down your kernel to only the necessary & essential services, useful for particular and dedicated servers – security reasons, faster boot and less RAM usage
  • Patching bugs to a production system, where you can not really risk upgrading the whole kernel for a single fix or some.
  • To try out a particular device driver that kernel currently not supporting
  • Testing some of the “Alpha” modules or features which has not been yet released within the distribution

Here I will go first through modifying/customizing the Kernel configuration without adding any new modules and afterwards I will focus on adding new Modules dynamically & statically to the kernel.

***************************************************************************************

Here I have a Ubuntu 14.04 with following kernel:

hossein@ubuntu:~$ uname -a
Linux ubuntu 4.4.0-31-generic #50~14.04.1-Ubuntu SMP Wed Jul 13 01:07:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

The idea here is to download the latest Linux kernel, do some changes and build our own one and load it to the system. I download the latest Linux kernel at the time of writing this article which is 4.13.10 from http://www.kernel.org.

hossein@ubuntu:~$ mkdir NewKernel
hossein@ubuntu:~$ cd NewKernel/
hossein@ubuntu:~/NewKernel$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.13.10.tar.xz
hossein@ubuntu:~/NewKernel$ tar xf linux-4.13.10.tar.xz

So now we have the kernel source and we can modify it based on our needs and then compile it again. There is in general 3 different ways of configuring and building the kernel:

  • Make old configuration — asks you questions on what the kernel should support one by one, time consuming.
  • Make menuconfig — creates a menu where you can browse options on what the kernel supports.
  • Make qconfig/xconfig/gconfig — same as menuconfig, except that now the configuration menu is graphics based.”qconfig” Requires the QT library.

The second option – using menuconfig – is very good way to go. So I use this method here for our purpose. In order to run “make menuconfig” we need to install some perquisite. One of them is “ncurses-devel” packages which in my case that using Ubuntu is libncurses5-dev as can be seen here:

hossein@ubuntu:~$ sudo apt install kernel-package libncurses5-dev ncurses-doc libssl-dev build-essential

Description: there are some other methods which is usually made for a special Linux distribution. As an example for Ubuntu we can use following guide:

https://wiki.ubuntu.com/Kernel/BuildYourOwnKernel

However I prefer/recommend the method which I describe here.

So to start we run following command which will launch a text-based user interface with default configuration options as shown in the figure.

hossein@ubuntu:~/kernelCompile/linux-4.13.9$ make menuconfig

 

new

In this graphical menu, we can go through customizing our own kernel by selecting or removing different options. At the end, it will write everything in a file called .config (in the same directory). The .config file is a Key-Value file included of all configurations that is needed for kernel compilation such as drivers/modules, file-system and etc.

It is very important to know that we can also copy the current kernel configuration file of our running system and then modify (do not forget to change the Kernel version number):

cp /boot/config-$(uname -r)  .config

We can also use a command like: “make localmodconfig” which only use the modules that already in the system has been loaded. Keep it in mind that the disadvantage of this way is that any new hardware added to the system in the future might not be supported by the kernel that we build this way. In contrast to “make menuconfig” that we have a page and can change the configuration based on our needs, here we will go through several questions for configuring the kernel.

In the last step we need to compile the main kernel and all kernel modules that we need for our modules.

# make -j 4
So now many programs usually in C will be compiled and its binary will be created.

# make modules -j4

And at the end we need to transfer the binary files to appropriate directories to be accessed. First we must transfer the modules with following command and then will transfer the main kernel files.

# sudo make modules_install

It will copy all modules and drivers to the appropriate directories.

  • hossein@ubuntu:~/NewKernel/linux-4.13.10$ sudo make install
    sh ./arch/x86/boot/install.sh 4.13.10 arch/x86/boot/bzImage \
    System.map “/boot”
    run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.13.10 /boot/vmlinuz-4.13.10
    run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.13.10 /boot/vmlinuz-4.13.10
    update-initramfs: Generating /boot/initrd.img-4.13.10
    run-parts: executing /etc/kernel/postinst.d/pm-utils 4.13.10 /boot/vmlinuz-4.13.10
    run-parts: executing /etc/kernel/postinst.d/update-notifier 4.13.10 /boot/vmlinuz-4.13.10
    run-parts: executing /etc/kernel/postinst.d/zz-update-grub 4.13.10 /boot/vmlinuz-4.13.10
    Generating grub configuration file …
    Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
    Found linux image: /boot/vmlinuz-4.13.10
    Found initrd image: /boot/initrd.img-4.13.10
    Found linux image: /boot/vmlinuz-4.4.0-31-generic
    Found initrd image: /boot/initrd.img-4.4.0-31-generic
    Found memtest86+ image: /boot/memtest86+.elf
    Found memtest86+ image: /boot/memtest86+.bin
    done

In my case which I use ubuntu, it will create the following files in the /boot directory.

  • vmlinuz-4.13.10 – The actual kernel
  • System.map-4.13.10 – The symbols exported by the kernel
  • initrd.img-4.13.10 – initrd image is temporary root file system used during boot process
  • config-4.13.10 – The kernel configuration file

The “make install” command also will update the grub.cfg file (/boot/grub/grub.cfg), so usually we don’t need to manually update it. So in the next boot, system will boot from new kernel. But to be sure, we can change the grub file in a way that still we have time to choose the old kernel in case of a problem. I commented following 2 lines in my case in /boot/default/grub
#GRUB_HIDDEN_TIMEOUT=0
#GRUB_HIDDEN_TIMEOUT_QUIET=true

so after the reboot, we can go to “advanced options for Ubuntu” and choose the Old kernel whenever we want.

In some distro (not here) it might not be possible to create initrd automatically. So we need to use a command to do so:
# mkinitrd -o initrd.img-<kernelversion> <kernelversion>”

Adding New Modules to the Kernel

As a reminder, kernel modules are piece of codes to extend functionality of the kernel that can be loaded or unloaded from kernel any time we need. As an example, most of the drivers are implemented as modules. I would say the main benefit of the module is to reduce the kernel image size and load only what we need. All kernel modules have a .ko extension.

There are 2 ways of adding new modules to the kernel:

a. Without rebuilding and compiling the kernel source code

Here the idea is to write our module (mainly in c) and compile in a way that produce the .ko file extension and then load it with some command as I show later. So let’s create a special directory for this case “NewModule” and write a simple hello word program in c and convert it to the Linux module.

hossein@ubuntu:~/NewModules$ cat hello.c
#include <linux/module.h> // included for all kernel modules
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“HRZ”);
MODULE_DESCRIPTION(“A Simple Hello World module”);
static int __init hello_init(void)
{
printk(KERN_INFO “Hello world!\n”);
return 0; // Non-zero return means that the module couldn’t be loaded.
}
static void __exit hello_cleanup(void)
{
printk(KERN_INFO “Cleaning up module.\n”);
}
module_init(hello_init);
module_exit(hello_cleanup);

And then we can compile it and create a Linux Module which has a .ko extension with following command: (we can also create a Makefile and put the command inside).

  • hossein@ubuntu:~/NewModules$ make -C /lib/modules/4.13.10/build M=/home/hossein/NewModules modules
    make: Entering directory `/home/hossein/NewKernel/linux-4.13.10′
    CC [M] /home/hossein/NewModules/hello.o
    Building modules, stage 2.
    MODPOST 1 modules
    CC /home/hossein/NewModules/hello.mod.o
    LD [M] /home/hossein/NewModules/hello.ko
    make: Leaving directory `/home/hossein/NewKernel/linux-4.13.10′

We can check the info of the created module with following command:

  • hossein@ubuntu:~/NewModules$ modinfo hello.ko
    filename: /home/hossein/NewModules/hello.ko
    description: A Simple Hello World module
    author: HRZ
    license: GPL
    srcversion: 43ECC69D96CE63B17E6E75A
    depends:
    name: hello
    vermagic: 4.13.10 SMP mod_unload modversions

Now its the time to load the module into kernel. We will use the “insmod” for this purpose:

  • hossein@ubuntu:~/NewModules$ lsmod | grep -i hello
  • hossein@ubuntu:~/NewModules$ sudo insmod hello.ko
  • hossein@ubuntu:~/NewModules$ lsmod | grep -i hello
    hello 16384 0

Description: There are 2 commands for loading the modules into Linux kernel which are – modprobe & insmode. However there are several differences between them:

  • “modprobe” will read the modules from /lib/modules/$(uname -r) directory which is usually at modules.dep.bin. But insmod will accept the path to the file.
  • “modproble” will solve the dependencies automatically which basically means all needed modules also will be loaded. But this is not the case in “insmode”.

Description: It is also useful to know that we can see the list of all modules which currently loaded by following 2 ways:

  • lsmod
  • cat /proc/modules

and also we can remove a module (unload a module from kernel) by following 2 commands:

  • rmmode $moduleName
  • modprobe -r $moduleName

and also we can configure it in a way that always certain modules being loaded at boot time of the system. For this we need to write the name of the modules in /etc/modules file.

 

b. Building and compiling into the kernel source code

Here the idea is to add the new modules into the kernel source and then compile the whole kernel with new added modules. Here I choose the same hello.c program which I had in the above and will write it into drivers/gpio directory of the source code. So Here I still use the same kernel source code which I downloaded and built previously – 4.13.10. Take into consideration that there are many different directories in drivers, which here I chose gpio for my case.

So I copied the same hello.c into ~/NewKernel/linux-4.13.10/drivers/gpio and changed the name to hello-world.c here (under-score is not accepted).

Now in order to be able to add/remove this module at the time of kernel configuration (in my case make menuconfig), we can modify the Kconfig file in the same directory (~/NewKernel/linux-4.13.10/drivers/gpio) as can be seen here:

  • hossein@ubuntu:~$ cat NewKernel/linux-4.13.10/drivers/gpio/Kconfig
    menu “Hossein is Testing the Modules”
    config HELLO_WORLD          #this should be the same name as our C file (hello_world.c)
    tristate “Hello world simple module”
    help
    Choose this module for Hello Word module
    endmenu

As can be seen I created completely a new menu here which is called “Hossein is Testing the Modules”, however we can add it to other menu as well. It is quite simple and straightforward. The “tristate” is the description of the module. If the module is dependent on another module, we can load it here by writing “depends on XXX”.

And the end we need to modify the Makefile in the same directory and add following line at the end of the file:

  • hossein@ubuntu:~$ cat NewKernel/linux-4.13.10/drivers/gpio/Makefile


    obj-$(CONFIG_HELLO_WORLD) +=hello-world.o

It is important to know that the first part should be written in capital and the name of it should be same as we have written in Kconfig file.
Now same as before we can configure the kernel for new configuration by using the “ make menuconfig” and choose our new module in:
Device Drivers—>GPIO Support —>Hossein is Testing the Modules —>

 

newone

 

hossein@ubuntu:~/NewKernel/linux-4.13.10$ cat .config | grep -i hello
CONFIG_HELLO_WORLD=y

Now we need to run one more time:

#make -j4
#make modules -j4
#make modules_install
#make install

 

 

 

 

 

 

 

 

 

 

 

 

 

%d bloggers like this: