Integrating Linux kernel module inside Linux kernel source

Integrating a Linux kernel module directly into the Linux kernel source tree can streamline development and testing by keeping everything in one place. This process involves modifying the kernel source tree to include your module, allowing you to build and test the module as part of the kernel. This guide provides a detailed, step-by-step approach to integrating a Linux kernel module into the kernel source, complete with practical examples.

Why Integrate a Kernel Module?

  • Streamlined Development: Keeps your module within the kernel source tree, simplifying development and testing.
  • Consistency: Ensures that your module is built and tested with the exact kernel version you are working on.
  • Ease of Maintenance: Makes it easier to manage and maintain kernel modules alongside kernel source changes.

Follow, steps from “Cross Compilation and Booting of Linux kernel for Raspberry Pi3 – Manual Compilation” to clone kernel source code for Raspberry Pi, toolchain and setup the environment to cross compile kernel image zImage and modules from the default configuration.

 $ cd linux 

create a directory to write a kernel module as,

 $ mkdir drivers/helloworld/ 

Now, lets write our basic helloworld kernel module / device driver in drivers/helloworld/helloworld.c

 $ vim drivers/helloworld/helloworld.c 
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
 
static int __init hello_init(void) {
        printk(KERN_INFO "Hello, world\n");
        return 0;
}
 
static void __exit hello_exit(void) {
        printk(KERN_INFO "Goodbye, world\n");
}
 
module_init(hello_init);
module_exit(hello_exit);

To, get this driver module compiled as part of kernel, we need to write Makefile and Kconfig file by following Linux kernel build mechanism, the contents of those files will be as below,

$ vim drivers/helloworld/Makefile 
obj-$(CONFIG_HELLOWORLD) += helloworld.o
$ vim drivers/helloworld/Kconfig
#
# Helloworld driver as part of kernel source
#

menu "Helloworld Driver"

config HELLOWORLD
        depends on ARM
        tristate "helloworld module"
        default y
        help
          Helloworld kernel module integrated as part of kernel source.

endmenu


here, as part of Kconfig, we are telling the Kernel build framework, that this driver can have 3 states for configuration as, “not selected”, “y” i.e. selected as part of kernel image and “m” selected to build as kernel module which is indicated by “tristate” , configuration parameter “depends” tells build framework to select this kernel module if “ARM” has been selected, ( you can change as you want to set dependency ), parameter “default y” selects this Confifuration parameter HELLOWORLD as “CONFIG_HELLOWORLD=y” in the default configuration, where as “help” shows the respective information in “make menuconfig”

Now, we need to tell the kernel build framework to consider our newly created driver directory for the compilation, by appending following line into “drivers/Kconfig” as,

$  vim drivers/Kconfig 
source "drivers/helloworld/Kconfig"

and

append following line to drivers/Makefile as,

 $ vim drivers/Makefile 
obj-$(CONFIG_HELLOWORLD)       += helloworld/

Now, we are ready to start the compilation of the kernel, currently because we have mentioned “config HELLOWORLD” as “default y” , if we do

 $ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- menuconfig 

In above command, you need to use toolchain “arm-bcm2708-linux-gnueabi” as per your build system.

we will see following things in “Device Drivers —> Helloworld Driver —> <*> helloworld module” in “Help”

You can use space bar to select and deselect “helloworld module” for,
1. deselect as < > helloworld module
2. select as module, helloworld module
3. select as in built to kernel image as, <*> helloworld module

after selecting proper type as above, click “Exit” till you see following window,

Do you wish to save your new configuration?
│  (Press  to continue kernel configuration.)    
│                            ├──────────────────────────────────────────────────────────┤  
|                   < Yes >      <  No  >  

select “yes” in this as if you open updated, .config file you will see a new option added as,

#
# Helloworld Driver
#
CONFIG_HELLOWORLD=y

Now, lets build kernel image, as

$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- zImage
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  CHK     include/generated/compile.h

  CC      drivers/helloworld/helloworld.o
  LD      drivers/helloworld/built-in.o
  LD      drivers/built-in.o

  LD      vmlinux.o
  MODPOST vmlinux.o
  GEN     .version
  CHK     include/generated/compile.h
  UPD     include/generated/compile.h
  CC      init/version.o
  LD      init/built-in.o
  KSYM    .tmp_kallsyms1.o
  KSYM    .tmp_kallsyms2.o
  LD      vmlinux
  SORTEX  vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm/boot/Image
  Kernel: arch/arm/boot/Image is ready
  GZIP    arch/arm/boot/compressed/piggy_data
  AS      arch/arm/boot/compressed/piggy.o
  LD      arch/arm/boot/compressed/vmlinux
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready

Now to compile same driver as kernel module, select it as “m” in kernel menuconfig as,

 $ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- menuconfig 

we will see following things in “Device Drivers —> Helloworld Driver —> helloworld module”

and then compile as,

$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- modules
  scripts/kconfig/conf  --silentoldconfig Kconfig
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CC      kernel/bounds.s
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CC      arch/arm/kernel/asm-offsets.s
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  CC      scripts/mod/empty.o
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  GEN     scripts/mod/devicetable-offsets.h
  HOSTCC  scripts/mod/file2alias.o
  HOSTCC  scripts/mod/sumversion.o
  HOSTLD  scripts/mod/modpost
  GZIP    kernel/config_data.gz
  CHK     kernel/config_data.h
  UPD     kernel/config_data.h
  CC [M]  kernel/configs.o
  CC [M]  drivers/helloworld/helloworld.o
  Building modules, stage 2.
  MODPOST 1561 modules
  CC      drivers/helloworld/helloworld.mod.o
  LD [M]  drivers/helloworld/helloworld.ko
  CC      kernel/configs.mod.o
  LD [M]  kernel/configs.ko

Now, we have to install this modules for copying into hardware, to do that we have to create separate directory for output binaries and setting INSTALL_MOD_PATH to this directory,

 $ mkdir out 
 $ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- modules_install INSTALL_MOD_PATH=./out 
 $ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- modules_install INSTALL_MOD_PATH=./out 

Leave a Comment