Does Writing Linux Kernel Module Require Compiling Own Kernel? - c

I have a simple hello world kernel module on Ubuntu x86_64:
#include <linux/module.h>
static int
mod_init(void)
{
printk(KERN_INFO "RYANhello world\n");
return 0;
}
static void
mod_exit(void)
{
printk(KERN_INFO "RYANgoodbye world\n");
}
MODULE_LICENSE("GPL");
module_init(mod_init);
module_exit(mod_exit);
Makefile:
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
CUR_DIR := $(shell pwd)
obj-m := module.o
default:
$(MAKE) -C $(KERNEL_DIR) M=$(CUR_DIR) modules
When I sudo insmod module.ko I get insmod: ERROR: could not insert module module.ko: Invalid parameters. Inspecting dmesg:
loading out-of-tree module taints kernel
module verification failed: signature and/or required key missing - tainting kernel
Repeating insmod yields module is already loaded however /var/log/syslog shows no trace of it loading (i.e printk messages not present). Also, running sudo rmmod module.ko:
rmmod: ERROR: ../libkmod/libkmod-module.c:1941 kmod_module_get_holders() could not open '/sys/module/module/holders': No such file or directory
rmmod: ERROR: Module unloading is not supported
This seems to indicate it's not loaded, even though dmesg says it is?
Addressing common issues; my host kernel and gcc version are the same as ones I compiling with.
So, this leads me to think that the module not being signed is the issue. To disable this do I have to compile and install my own kernel with appropriate .config? In other words, to write and test your own kernel modules on a modern GNU/Linux OS with enforced signing, do you have to compile and install your own kernel?
EDIT
CONFIG_MODULE_SIG_FORCE is not set in my /boot/config-5.8.0-53-generic, so it seems I should be able to load my module albeit with a tainted kernel message. So, why would I be getting Invalid parameters?

Inspecting dmesg on first insmod it was saying that the module was already loaded. As I had never loaded this before, this prompted me to think that this name was already taken.
Low and behold, renaming module.c/module.o --> example.c/example.o fixed the problem. The invalid parameters message was what threw me.

Related

i2c_register_board_info symbol is undefined

I am trying to write a kernel module for a BeagleBone Black that would communicate with my custom I2C slave device. I tried following several kernel module development tutorials, they all seem incomplete at some point, or assume that I know something I clearly don't... My current problem now is that the Makefile doesnt see the i2c_register_board_info symbol. I am writing this driver as a separate module, its not compiled during kernel compilation. Also, I have enabled the I2C tools when building using buildroot. Using the I2C tools I am able to detect and interface with my device. My Makefile looks as follows:
MODULE_NAME = PowerManagerDriver
PWD := $(shell pwd)
SRC_DIR = user_files
BUILD_DIR = build
BUILD_EXT = *.o .*.cmd *.ko *.mod.c *.order *.symvers *.dwo
SRCS = $(SRC_DIR)/main.c $(SRC_DIR)/pmd_i2c.c
OBJS = $(SRCS:.c=.o)
obj-m += $(MODULE_NAME).o
$(MODULE_NAME)-y = $(OBJS)
KERNELDIR ?= /home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92
all: default
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
mv $(SRC_DIR)/*.o $(BUILD_DIR)/
mv $(BUILD_EXT) $(BUILD_DIR)/ &> /dev/null
clean:
rm -rf $(BUILD_DIR)/*
rm -rf $(BUILD_EXT)
make output:
13:45:33 **** Incremental Build of configuration Default for project PowerManagerDriver ****
make ARCH=arm CROSS_COMPILE=arm-buildroot-linux-uclibcgnueabihf- -j6 all
make -C /home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92 M=/home/lukasz/eclipse-workspace/PowerManagerDriver modules
make[1]: Entering directory '/home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92'
CC [M] /home/lukasz/eclipse-workspace/PowerManagerDriver/user_files/main.o
CC [M] /home/lukasz/eclipse-workspace/PowerManagerDriver/user_files/pmd_i2c.o
/home/lukasz/eclipse-workspace/PowerManagerDriver/user_files/pmd_i2c.c:99:26: warning: ‘pdm_i2cClient’ defined but not used [-Wunused-variable]
static struct i2c_client pdm_i2cClient = { 0 };
^
LD [M] /home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.o
Building modules, stage 2.
MODPOST 1 modules
WARNING: "i2c_register_board_info" [/home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.ko] undefined!
CC /home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.mod.o
LD [M] /home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.ko
make[1]: Leaving directory '/home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92'
mv user_files/*.o build/
mv *.o .*.cmd *.ko *.mod.c *.order *.symvers *.dwo build/ &> /dev/null
13:45:35 Build Finished. 0 errors, 1 warnings. (took 1s.394ms)
So far I have only few functions for I2C interfacing:
static int pdm_i2cProbe(struct i2c_client* client,
const struct i2c_device_id* id)
{
PMD_ASSERT(client);
PMD_ASSERT(id);
return 0;
}
static struct i2c_driver pdm_i2cDriver =
{
.driver =
{
.name = "pdm-driver",
.owner = THIS_MODULE,
},
.probe = pdm_i2cProbe,
};
static struct i2c_board_info pdm_i2cBoardInfo[] =
{
{
I2C_BOARD_INFO("pdm-driver", 0x30),
.irq = 69,
},
};
/**
* #brief Initializes the I2C module.
* #param busNr: The I2C peripheral number on which the device is connected.
* #return \ref e_pdmStatus_OK on succesfull init.
*/
pmdStatus_t pdm_i2cInit(const unsigned int busNr)
{
if (i2c_register_board_info((int)busNr, pdm_i2cBoardInfo,
ARRAY_SIZE(pdm_i2cBoardInfo)))
return e_pmdStatus_BADPARAM;
if (i2c_add_driver(&pdm_i2cDriver))
return e_pmdStatus_EXE;
return e_pmdStatus_OK;
}
At this point even though I dont probe anything, as the probe function is empty, I was hoping to at least load the module correctly. I dont even get to the testing phase since the i2c_register_board_info is not found. My .config file for when building the kernel consists of those lines:
# I2C support
#
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
CONFIG_I2C_CHARDEV=y
# CONFIG_I2C_MUX is not set
CONFIG_I2C_HELPER_AUTO=y
I have found this topic previously i2c registering macro not found? but I can't get anything out of it.
When trying to load the module with insmod as requested:
# uname -a
Linux buildroot 4.9.59 #1 SMP Fri Oct 5 11:55:54 CEST 2018 armv7l GNU/Linux
# insmod PowerManagerDriver.ko
[ 39.438108] PowerManagerDriver: loading out-of-tree module taints kernel.
[ 39.445800] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
[ 39.455743] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
insmod: can't insert 'PowerManagerDriver.ko': unknown symbol in module, or unknown parameter
#
If your hardware is 'guaranteed' to be there, you do not need to define the device imperatively. You can use device tree (dtb) to declare the existence of your slave at a particular address on a particular bus, provide params to your driver etc. So your module never needs to do the register board info thing.
This is explained in the docs: https://www.kernel.org/doc/Documentation/i2c/instantiating-devices
In other words you should be able to write a driver for your slave device as a kernel module using only open firmware (of) hooks and a populated device tree (dtb).
OK, it sounds like this is a duplicate of Build Linux Kernel module with warning i2c_register_board_info undefined.
PROBLEM:
# insmod PowerManagerDriver.ko
[ 39.438108] PowerManagerDriver: loading out-of-tree module taints kernel.
[ 39.445800] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
[ 39.455743] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
insmod: can't insert 'PowerManagerDriver.ko': unknown symbol in module, or unknown parameter
CAUSE:
Build Linux Kernel module with warning i2c_register_board_info undefined
Function i2c_register_board_info isn't exported (with EXPORT_SYMBOL)
for kernel modules. Only code compiled into the kernel may use such
functions. As far as I understand from its description, the function
is intended to use by boards developers
Your problem is you are mixing two things in one driver, i.e. driver
itself which can be a module and platform (legacy!) code, which can't.
You have to drop platform code for ACPI or device tree resource
provider, or as a last resort to split it out to another compile unit.
WORKAROUND:
I removed i2c_register_board_info then I can build driver to module
(.ko) without warning and can insmod by adding these function in init
function called by module_init:
...
adapter = i2c_get_adapter(CONFIG_I2C_BUS);
...
client = i2c_new_device(adapter, &i2c_pn535_sample_devs);
...
not sure if it is good or bad. because when i remove driver (rmmod),
driver does not release device and can not insmod again, I have to
reboot device to insmod again
Please feel free to add (and accept!) your own response if you find a different - or better - solution.
Sorry I couldn't be of more help :(

"Segmentation fault" when `rmmod` or `modprobe -r`

Trying the simplest kernel module from LDD3 without any modification on custom built kernel v4.1.0-rc6 for Beagle Bone board with BusyBox v1.23.0. The code for the module is as follows:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
The Makefile is:
ARCH := arm
CROSS_COMPILE := arm-cortex_a8-linux-gnueabi-
obj-m := hello.o
all:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C /path/to/linux/source/tree M=$(PWD) modules
clean:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C /path/to/linux/source/tree M=$(PWD) clean
The module is compiling and installing on the rootfs just fine. also it is loading:
$ insmod hello.ko
[ 30.692404] Hello, world
But when trying to remove it, I am getting:
$ rmmod hello.ko
Segmentation fault
$modprobe -r hello.ko
Segmentation fault
$ lsmod
hello 813 0 - Live 0xbf000000 (O)
The kernel is compiled with module unloading (both regular and forced) support enabled.
What can be the possible cause of this issue? What is the way to approach the investigation of it?
Update:
As suggested in the comments I have tried including linux/kernel.h, defining the MODULE,LINUX and __KERNEL__ symbols. Added the__init and __exit prefixes to the functions. Removed the static modifiers. Removed the printk lines. The result the same. dmesg shows only the initial greeting. Loading and unloading of the kernel's modules such a gpio_keys or crypto/ccm is working, surprisingly. So the only thing remaining to suspect is the way the module is compiled..
Update 2
Updating the kernel to the freshest snapshot didn't help. Compiled the module with different optimization settings didn't help. Next step, I guess, I am going to modify the BusyBox's rmmod to have some indication of the problem location..
Have a look at these tutorials:
http://www.tldp.org/HOWTO/Module-HOWTO/x839.html
http://www.tldp.org/LDP/lkmpg/2.4/html/x281.htm
Try adding:
#define MODULE
#define LINUX
#define __KERNEL__
#include <linux/kernel.h> /* Needed for KERN_ALERT */
I have managed to work around this problem. Using strace I've found out that the segfault is occurring somewhere when reading the BusyBox specific modules.dep.bb file. This file is used by BusyBox when it is compiled with the "Simplified modutils" option (CONFIG_MODPROBE_SMALL). By disabling the option, selecting the utils to be installed and rebuilding BusybBox I've got the module unloading work. I believe the problem root is near the fact the test module is compiled and stored outside the /lib/..../modules directory, so busybox with the simplified modutils is just getting confused.

insmod error in kernel module programming

I am just starting with modular programming.
Above are my two files:
hello.c
#include <linux/init.h>
#include <linux/module.h>
static int hello_init(void)
{
printk(KERN_ALERT "TEST: Hello world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "TEST: Good Bye");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile
obj-m += hello.o
KDIR = /usr/src/linux-headers-3.13.0-46-generic
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.* *.symvers *.order
And here's my terminal output showing error in insmod command, kindly help.
anubhav#anubhav-Inspiron-3421:~/Desktop/os$ make
make -C /usr/src/linux-headers-3.13.0-46-generic SUBDIRS=/home/anubhav/Desktop/os modules
make[1]: Entering directory `/usr/src/linux-headers-3.13.0-46-generic'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory `/usr/src/linux-headers-3.13.0-46-generic'
anubhav#anubhav-Inspiron-3421:~/Desktop/os$ insmod hello.ko
insmod: ERROR: could not insert module hello.ko: Operation not permitted
If you have secure boot enabled, the newer kernels won't allow inserting arbitrary kernel modules. So, either you can disable secure boot in your BIOS or you need to sign the kernel modules you want to install.
Steps to securely sign your kernel modules:
Create a X509 certificate that can be imported in firmware
Enrolling the public key just created
Sign the module you want to install
Install the module
You need to be root to do steps 2 & 4. The detailed process is described in a nice Ubuntu blog.
As isowen mentioned only root can load or unload the module.
You see the print in hello_init() when you do insmod hello and you see the print in hello_exit() when you do rmmod hello.
execute cat /proc/sys/kernel/modules_disabled and if you see the result
1 then execute echo 'kernel.modules_disabled=1' >> /etc/sysctl.d/99-custom.conf then reboot and try again. ;) BR nu11secur1ty

Loadable kernel module not compiles correctly on different computers

I'm trying to make loadable kernel module for ARM achitecture. Just for example I'm made simple hello.c
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
static int __init hello_start(void)
{
printk(KERN_INFO "Loading hello module...\n");
return 0;
}
static void __exit hello_end(void)
{
printk(KERN_INFO "Goodbye Mr.\n");
}
module_init(hello_start);
module_exit(hello_end);
And created Makefile
obj-m := hello.o
PWD := $(shell pwd)
ARCH=arm
CROSS_COMPILE_LINARO=/home/cooperok/mk908/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/bin/arm-linux-gnueabihf-
KERNEL_ROCKCHIP=/home/cooperok/mk908/kernel/
default:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE_LINARO) -C $(KERNEL_ROCKCHIP) M=$(PWD) modules
clean:
make CROSS_COMPILE=$(CROSS_COMPILE_LINARO) -C $(KERNEL_ROCKCHIP) M=$(PWD) clean
Cross compiler and kernel sources are the same on both computers. When I'm running make on first machine compiled hello.ko file is successfully installed with insmod command, but when I'm compiling module on second machine and trying to install that module I'm getting error "insmod: error inserting 'hello.ko': -1 Invalid module format"
As I understand module is compiling with crosscompiler which I'm specified in CROSS_COMPILE (arm-linux-gnueabihf-gcc and others) and only it will make module without any specific libraries installed on computers, is it?
First computer have 64bit OS (Ubuntu 12.04), and second 32bit OS (Linux Mint 12), this is main difference, so make util is different. But is it realy main reason why module correctly compiling only on 64bit OS? If so is it problem in make, or it's really deals with different libraries?

Linux makefile example

Hi I am a newbie to linux and am working my way through an example
http://www.linuxforu.com/2010/12/writing-your-first-linux-driver/
I am using Ubuntu 12.04 and have created the c code file called ofd.c (see below) and is is saved in a directory I created at ~/Development/MyProgs/myHelloWorldLinuxModule/v2. I have also created a Makefile (see below) which is in the same directory.
I was hoping to see a .ko file generated in the same directory when I type make, but all I get is a message saying "Nothing to be done for default"
I don't really understand the makefile
should I define KERNELRELEASE somewhere,
what is the line at default actually doing,does this mean carry out make on the kernel directory and the working directory or am I supposed to put my code somewhere in particular.
there is no usr/src/linux but a /usr/src/linux-headers-3.8.0-29, so I changed this, is that correct. (Didn't seem to make any difference though).
Any help would be greatly appreciated.
Code:
/* ofd.c – Our First Driver code */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
static int __init ofd_init(void) /* Constructor */
{
printk(KERN_INFO "Namaskar: ofd registered");
return 0;
}
static void __exit ofd_exit(void) /* Destructor */
{
printk(KERN_INFO "Alvida: ofd unregistered");
}
module_init(ofd_init);
module_exit(ofd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Our First Driver");`
Makefile......
# Makefile – makefile of our first driver
# if KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq (${KERNELRELEASE},)
obj-m := ofd.o
# Otherwise we were called directly from the command line.
# Invoke the kernel build system.
else
KERNEL_SOURCE := /usr/src/linux
PWD := $(shell pwd)
default:
${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} modules
clean:
${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} clean
endif
The error is due to naming convention your makefile is not able to find source code
First check which kernel is running by typing uname -a
Then go to cd /usr/src/
then check your linux source-code name
for e.g
uname -a
Linux vinay-VirtualBox 3.2.0-50-generic-pae #76-Ubuntu SMP Tue Jul 9 19:24:55 UTC 2013 i686 i686 i386 GNU/Linux
here its source-code name is linux-headers-3.2.0-50-generic-pae
So in your Makefile u need to give correct name like this
KERNEL_SOURCE := /usr/src/linux-headers-3.2.0-50-generic-pae
To avoid above problem try this Makefile
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := ofd.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
here uname -a resolves the problem
You have an error in your Makefile.
You must use a tab character on the lines defining the actions to take against the target, e.g. the lines:
default:
${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} modules
The line must have a tab from the start of the line to the ${MAKE}. You need to force a tab character there, otherwise the error you've witnessed will occur.
The Make syntax is very particular about this requirement, and it's an easy error to make. If you used an editor like vim, it would typically display the space characters to the user highlighted in red so that they can understand that an error is present there.
You didn't show the make command you ran to get this error. However, an error like that usually means that you have a file or subdirectory named default in your directory. Make wants to build targets and the first target in the makefile is default. It has no defined prerequisites which means that if it exists, make assumes it's up-to-date and nothing needs to be done to build it (it exists, and it doesn't depend on anything else).
You should probably use this:
.PHONY: default clean
to declare the default and clean targets to be "phony"; this tells make that even if those targets exist already it should still always run the recipe associated with them.
The error is coming due to the reason, the path specified in Makefile for headers or kernel source code, for the label KERNEL_SOURCE is not able to find the headers, which you already mentioned you suspected. Instead of changing the path, /usr/src/linux-headers-3.8.0-29 to /usr/src/linux.
Change the path given in the Makefile as the actual version of kernel whose source code you have or if the same kernel you are running you can get the version by uname command.
The better approach could be :
default:
$(MAKE) -C /usr/src/$(shell uname -r)/ M=$(shell pwd) modules

Resources