Linux kernel module development buildroot - c

I have build a Linux kernel for the beaglebone black using buildroot. Now I would like to develop a hello world Linux kernel module application:
#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 problem is I still keep missing some header files. After finally gathering them all, I get an error that the code is not compilable (many errors, I don't want to paste them all). What I was wondering is either I am really including the right files?
At the moment I have:
/home/lukasz/brl/Machine/beaglebone/build/linux-headers-a75d8e93056181d512f6c818e8627bd4554aaf92/include
/home/lukasz/brl/Machine/beaglebone/build/uboot-2018.01/arch/x86/include
/home/lukasz/brl/Machine/beaglebone/build/linux-headers-a75d8e93056181d512f6c818e8627bd4554aaf92/arch/arm/include/generated
/home/lukasz/brl/Machine/beaglebone/build/linux-headers-a75d8e93056181d512f6c818e8627bd4554aaf92/arch/arm/include
/home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92/include
Its a bit odd to me that the C include files and asm files are so scattered around within the directory. Are there some mistakes in my understanding of the topic here?
My Linux version:
# uname -a
Linux buildroot 4.9.59 #1 SMP Fri Oct 5 11:55:54 CEST 2018 armv7l GNU/Linux

To compile a kernel module, you need the real kernel sources, not just the kernel header files. You have to build from the kernel source directory with M= pointing to the source of your modules. And together with your module source, you of course also need a working Makefile. These steps are explained in any of the dozens of how-to-write-a-kernel-module guides, e.g. this one.
For cross-compilation, you also need to pass the appropriate arguments so that the kernel knows for which architecture to build and which cross-compiler to use. At the very least, this means you have to give the ARCH= and CROSS_COMPILE= options when building. Sometimes you need additional options (e.g. to point to the appropriate depmod tool).
To simplify this, Buildroot offers kernel module infrastructure. In the simplest case, you can just create a Config.in file containing
config BR2_PACKAGE_HELLOMOD
bool "hellomod"
depends on BR2_LINUX_KERNEL
and a hellomod.mk file containing
HELLOMOD_SITE = /path/to/hellomod/source
$(eval $(kernel-module))
$(eval $(generic-package))
You also have to source the Config.in from package/Config.in in the Buildroot tree. Or better yet, use an external tree so you don't have to modify Buildroot itself.

Related

init.h file not found

I was trying to do a hello world module from the book Linux Device Drivers.
but I got a fatal error from gcc,
fatal error: linux/init.h: No such file or directory
1 | #include<linux/init.h>
I've installed the linux headers with
sudo apt install linux-headers-$(uname -r)
it says:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
linux-headers-5.10.0-9-amd64 is already the newest version (5.10.70-1).
The following package was automatically installed and is no longer required:
libc-devtools
Use 'sudo apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded.
My code:
#include<linux/init.h>
#include<linux/module.h>
static int hello_init(void)
{
printk(KERN_ALERT,"Hello world\n");
}
static void hello_exit(void)
{
printk(KERN_ALERT,"Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
``
Like you, I try understanding linux device drivers, and I tried the first example (hello.c) :
All this happens because we're not in a proper environment, and I think we should use another linux system (a "mainline" one, like advised in the previous chapter if I'm not mistaken, this is important if you want to avoid any problem, or worse making your system unstable/losing important data with the examples shown in the book).
But I finally got the "hello world" working :
go in the examples/misc-modules directory (where "hello.c" resides)
edit Makefile and put all other modules in comment so you should only see "obj-m := hello.o" (put all others in lines below and starting with a "#")
execute command "make" and you should have your "hello.ko"
To see the messages of your module hello.ko, run dmesg -w in another terminal (these are kernel messages, they won't be seen on the standard console !)
rem : "hellop.ko" can also be build but it fails with seq.c (another error, implicit definition of a function, header missing or something like that...)

How to compile Hello World program for PowerPC

I have a Dreambox 500 which on Wikipedia says has a PCP processor which is PowerPC:
$ cat /proc/cpuinfo
processor: 0
cpu: STBx25xx
clock: 252MHz
Review: 9.80 (pvr 5151 0950)
bogomips: 250.36
Machine: Dream Multimedia Dreambox TV
plb bus clock: 63MHz
I would normally install GCC but it has low storage on it and I need to compile a program for it.
I've heard GCC can compile powerpc but I had no luck doing so.
Example this code
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
And I use this to compile
gcc example.c -mtune=powerpc
But it give this error
example.c:1:0 error: bad value (powerpc) for -mtune- switch
#include <stdio.h>
^
Thank you!
You should use cross-compiler, because your target architecture differs from host one. Host is the architecture of your system (usually amd64 (x86_64) or i386 (x86_32)). And target arch is the arch on which your compiled program will run (powerpc in your case).
Many GNU/Linux distors provide crosscompilers as a separate packages. For example, for Ubuntu these packages are available:
sudo apt-get install gcc-4.8-powerpc-linux-gnu g++-4.8-powerpc-linux-gnu binutils-4.8-powerpc-linux-gnu
Packages above are for trusty. In later releases different GCC versions are available.
Then you can compile your program using powerpc-linux-gnu-gcc-4.8. Or you can set your environment variables CC and CXX to powerpc-linux-gnu-gcc-4.8 and powerpc-linux-gnu-g++-4.8 accordingly.
upd:
I found crosscompiler toolchain for Dreambox 500 here, but it contains relatively old GCC (3.4).
In order to use it extract downloaded file to /opt/cross/dm500, add /opt/cross/dm500/cdk/bin to path via export PATH=$PATH:/opt/cross/dm500/cdk/bin and use gcc from here with appropriate prefix.
After being on a programming forum for a while, found a guy with the same problem, and after a while he found a way to fix it and I tried it and it works.
The thing I have to do is
powerpc-gcc someprog.c -static
I have no idea what the -static does but it increases the executable file size and at the end it works!

Compiling linux kernel module show_mem routine

I am trying to invoke show_mem() from mm.h in a user defined kernel module. When I compile it shows show_mem undefined. I am running Ubuntu 14.04 and have a compiled linux kernel 3.19.
/*
* Author - [Deepak]
*/
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/mm.h> /* Needed for show_mem */
#include <asm/cacheflush.h>
#include <linux/mm.h>
static char *user_data1 __initdata = "Hello World";
static int *user_data2 __initdata = 2;
static int __init starter(void)
{
printk(KERN_INFO "[ds494] Loading Hello2 module - %s %d \n",user_data1,user_data2);
show_mem(1);
return 0;
}
static void __exit ending(void)
{
printk(KERN_INFO "[ds494] Exiting Hello2 module - Goodbye World 2\n");
}
module_init(starter);
module_exit(ending);
And below is the make file -
obj-m += memmod.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
And I get the below error -
**MODPOST 1 modules
WARNING: "show_mem" [/home/deepak/cs/hw/homework_4/memmod.ko] undefined!
LD [M] /home/deepak/cs/hw/homework_4/memmod.ko
make[1]: Leaving directory '/home/deepak/Downloads/linux-3.19'**
Any suggestions, please.
Root cause.
You can't use show_mem() function in loadable modules, because it's not exported by EXPORT_SYMBOL.
Possible solutions.
Basically you have 3 options how to workaround this problem.
Modifying kernel sources
download kernel source (see this)
add code for exporting this symbol (like this, but this patch is intended for ARM architecture, for x86 see lib/show_mem.c)
build your module using those modified sources
If you also need to run your module -- you will need to build your customized kernel first and run it (instead of Ubuntu stock kernel).
It's not upstreamable solution though and quite frankly nobody will be able to use your module (you will need to provide modified kernel as well).
Compiling your module as built-in module.
It can be done inside of kernel tree, using obj-y instead of obj-m). In that case you will be able to use show_mem() function. Just like the first option, this option implies modifying of kernel sources.
Write your own show_mem() implementation.
I'm not sure about this one, though, because it may turn out that you can't use API needed for this task in loadable module at all. It's also can be quite difficult to implement this.
Conclusion.
If it's only educational task (which I guess it is), I'd say go with first option.
If you really need to implement that as a loadable module, and you can't modify kernel sources, I afraid you only have 3rd option, which is hardest one.

Writing a built in object for Linux Kernel?

Everywhere I search for Linux Kernel Development, I get answers for creating Linux Kernel modules.
Example
/*
* hello−1.c − The simplest kernel module.
*/
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
int init_module(void)
{
printk(KERN_INFO "Hello world 1.\n");
/*
* A non 0 return means init_module failed; module can't be loaded.
*/
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world 1.\n");
}
Here, there is init_module and cleanup_module functions which i understand contains things to be executed when the kernel is initialized and cleaned up.
There are made by adding
obj-m += hello-1.c
to the makefile.
But I dont want this. I want to add a built in program, not a driver, basically a service to facilitate cloud uploading of some data from the kernel level. I dont event want the module option for the program when compiling the kernel.
I understand for just programs I should use obj-y not obj-m. But there is no manual to write these kind of programs. Why? Am I missing something? Does these programs also have the init_module and cleanup_module functions even though they are not modules?
For example consider that your source is under driver/new in the linux kernel source tree.
You need to modify Makefile's under drivers and new to build your module statically into linux kernel.
Under drivers/Makefile add the below line at the end.
obj-y += new/
Under drivers/new/Makefile add the below line at the end.
obj-y += hello.o
After build the linux kernel. And load to see that your module has printed the printk messages using dmesg command.
Note: When building module statically into linux, change
int init_module(void)
to
int __init init_module(void)
and change
void cleanup_module(void)
to
void __exit cleanup_module(void)
Look into kernel doc Makefiles
Refer:
"
--- 3.2 Built-in object goals - obj-y
The kbuild Makefile specifies object files for vmlinux
in the $(obj-y) lists. These lists depend on the kernel
configuration.
Kbuild compiles all the $(obj-y) files. It then calls
"$(LD) -r" to merge these files into one built-in.o file.
built-in.o is later linked into vmlinux by the parent Makefile.
The order of files in $(obj-y) is significant. Duplicates in
the lists are allowed: the first instance will be linked into
built-in.o and succeeding instances will be ignored.
Link order is significant, because certain functions
(module_init() / __initcall) will be called during boot in the
order they appear. So keep in mind that changing the link
order may e.g. change the order in which your SCSI
controllers are detected, and thus your disks are renumbered.
Example:
#drivers/isdn/i4l/Makefile
# Makefile for the kernel ISDN subsystem and device drivers.
# Each configuration option enables a list of files.
obj-$(CONFIG_ISDN_I4L) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
"

How to call exported kernel module functions from another module?

I'm writing an API as a kernel module that provides device drivers with various functions. I wrote three functions in mycode.c. I then built and loaded the module, then copied mycode.h into < kernel >/include/linux. In a device driver, I have a #include < linux/mycode.h > and call those three functions. But when I build the driver module, I get three linker warnings saying that those functions are undefined.
Notes:
The functions are declared extern in mycode.h
The functions are exported using EXPORT_SYMBOL(func_name) in mycode.c
Running the command nm mycode.ko shows all three functions as being available in the symbol table (capital T next to them, meaning the symbols are found in the text (code) section)
After loading the module, the command grep func_name /proc/kallsyms shows all three functions as being loaded
So clearly the functions are being exported correctly and the kernel knows what and where they are. So why can't the driver see their definitions? Any idea what am I missing?
EDIT: I found some information about this here: http://www.kernel.org/doc/Documentation/kbuild/modules.txt
Sometimes, an external module uses exported symbols from another
external module. kbuild needs to have full knowledge of all symbols
to avoid spitting out warnings about undefined symbols. Three
solutions exist for this situation.
NOTE: The method with a top-level kbuild file is recommended but may
be impractical in certain situations.
Use a top-level kbuild file If you have two modules, foo.ko and
bar.ko, where foo.ko needs symbols from bar.ko, you can use a
common top-level kbuild file so both modules are compiled in the
same build. Consider the following directory layout:
./foo/ <= contains foo.ko
./bar/ <= contains bar.ko
The top-level kbuild file would then look like:
#./Kbuild (or ./Makefile):
obj-y := foo/ bar/
And executing
$ make -C $KDIR M=$PWD
will then do the expected and compile both modules with full
knowledge of symbols from either module.
Use an extra Module.symvers file When an external module is built,
a Module.symvers file is generated containing all exported symbols
which are not defined in the kernel. To get access to symbols from
bar.ko, copy the Module.symvers file from the compilation of bar.ko
to the directory where foo.ko is built. During the module build,
kbuild will read the Module.symvers file in the directory of the
external module, and when the build is finished, a new
Module.symvers file is created containing the sum of all symbols
defined and not part of the kernel.
Use "make" variable KBUILD_EXTRA_SYMBOLS If it is impractical to
copy Module.symvers from another module, you can assign a space
separated list of files to KBUILD_EXTRA_SYMBOLS in your build file.
These files will be loaded by modpost during the initialization of
its symbol tables.
But with all three of these solutions, in order for any driver to use my API, it would have to either create a new Makefile or have direct access to my Module.symvers file? That seems a bit inconvenient. I was hoping they'd just be able to #include my header file and be good to go. Do no other alternatives exist?
From my research, it seems that those are the only three ways to handle this situation, and I've gotten each of them to work, so I think I'll just pick my favorite out of those.
Minimal QEMU + Buildroot example
I have tested the following in a fully reproducible QEMU + Buildroot environment, so maybe having this working version version will help you find out what is wong with your code.
GitHub upstream is centered on the files:
dep.c
dep2.c
Makefile
dep.c
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
int lkmc_dep = 0;
EXPORT_SYMBOL(lkmc_dep);
static struct task_struct *kthread;
static int work_func(void *data)
{
while (!kthread_should_stop()) {
printk(KERN_INFO "%d\n", lkmc_dep);
usleep_range(1000000, 1000001);
}
return 0;
}
static int myinit(void)
{
kthread = kthread_create(work_func, NULL, "mykthread");
wake_up_process(kthread);
return 0;
}
static void myexit(void)
{
kthread_stop(kthread);
}
module_init(myinit)
module_exit(myexit)
dep2.c
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
extern int lkmc_dep;
static struct task_struct *kthread;
static int work_func(void *data)
{
while (!kthread_should_stop()) {
usleep_range(1000000, 1000001);
lkmc_dep++;
}
return 0;
}
static int myinit(void)
{
kthread = kthread_create(work_func, NULL, "mykthread");
wake_up_process(kthread);
return 0;
}
static void myexit(void)
{
kthread_stop(kthread);
}
module_init(myinit)
module_exit(myexit)
And now you can do:
insmod dep.ko
insmod dep2.ko
With that Buildroot setup, things are already configuring depmod /lib/module/*/depmod with the dependency, so just this is enough to load both:
modprobe dep
Also, if you built your kernel with CONFIG_KALLSYMS_ALL=y, then the exported symbol can be seen with:
grep lkmc_dep /proc/kallsyms
see also: Does kallsyms have all the symbol of kernel functions?
OK: You have one module where the function is and one place what wants to import it right?
You must use "EXPORT_SYMBOL("name of the function") such as foo in the place where the function is. So in the "c" file have the function "foo" defined and put in:
EXPORT_SYMBOL(foo)
Make sure you have the prototype for "foo" in a common header file (you can have it in separate places for each module and it will work but you are asking for trouble if the signatures change). So say: void foo(void *arg);
Then the other module that wants it just invoke "foo" and you are good.
Also: Make sure that you load the module with foo first. If you have cross dependencies like module2 needs foo from module1 and module1 needs bar from module2 you need to have one register functions with another. If you want to know please ask a separate Q.

Resources