Building kernel module as part of kernel compilation procedure

If you have already followed “Writing first Linux kernel Module and understanding Kernel module compilation, loading and unloading”, you might have already understood how to build simple kernel module for x86 which we compiled from outside kernel source code for a x86 system which was already running with Linux kernel,

Now, Lets try to integrate same kernel module as part of Linux kernel source code, so we can also cross compile the same for ARM and also set an option to make it as part of kernel binary ( zImage/uImage) or build it as module using kernel build framework.

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