I'm trying to compile a kernel module for Linux. I have the following files: testuio.c
and Makefile
. When I type make all
I get the following errors:
$ make all
make -C /lib/modules/`uname -r`/build M=/srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
CC [M] /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o
In file included from /usr/include/unistd.h:25,
from /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.c:13:
/usr/include/features.h:424:12: fatal error: sys/cdefs.h: No such file or directory
# include <sys/cdefs.h>
^~~~~~~~~~~~~
compilation terminated.
make[3]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/scripts/Makefile.build:271: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:1665: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory] Error 2
make[1]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:179: sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
make: *** [Makefile:19: all] Error 2
This is correct, there is no such file under /usr/include/sys/. What I don't understand is why it would not find it under /usr/include/x86_64-linux-gnu/sys where there is such a file.
The following is part of gcc -xc -E -v -
output:
#include <...> search starts here:
.
/usr/lib/gcc/x86_64-linux-gnu/8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
My Makefile contains:
# (1) consult https://www.kernel.org/doc/Documentation/kbuild/modules.txt to build a kbuild-compatible Makefile
# sections of special interest: 3.1 (shared makefile)
# (2) specifics about the Makefile under https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
# sections of special interest 3.7 (compilation flags)
ifneq ($(KERNELRELEASE),)
# kbuild part of the makefile (could pull this out into a file named Kbuild, this variant is more robust though)
obj-m += testuio.o
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
#ccflags-y = -I/usr/include/aarch64-linux-gnu -I/usr/include # see (2) section 3.7
else
# "normal" makefile
KDIR ?= /lib/modules/`uname -r`/build # ?= sets KDIR only if it has no value already
all:
make -C $(KDIR) M=$(PWD) modules
install:
make -C $(KDIR) M=$(PWD) modules_install
clean:
make -C $(KDIR) M=$(PWD) clean
endif
For reference my testuio.c contains:
// In order for a device to be compatible with this UIO platform device driver
// it needs to use "generic-uio" in its compatible property
// This is a kernel driver.
#include <linux/module.h> // included for module_* macros
#include <linux/device.h> // included for devm_kzalloc
#include <linux/mman.h> // included for mmap
// #include <linux/stat.h> // included for fstat (for being able to read out of files)
#include <linux/platform_device.h> // included for struct platform_device
#include <linux/uio_driver.h> // included for struct uio_info
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
#define EOPENUIOFD 1
#define EMEMMAP 2
#define UIO_SIZE_FILE "/sys/class/uio/uio0/maps/map0/size"
MODULE_LICENSE("");
MODULE_AUTHOR("Alexander Pastor");
MODULE_VERSION("0.0.1");
// MODULE_DEVICE_TABLE(???);
typedef struct testuio_dev {
struct uio_info* info;
} testuio_dev;
// TODO: change s.t. interupts can be handled correctly
// HINT: might not be necessary tho
static int testuio_irq_handler(int irq, struct uio_info* info)
{
// if (IRQ is not caused by my hardware)
if (true)
return IRQ_NONE;
/* Disable interrupt */
// Perform some register access to silence the IRQ line
return IRQ_HANDLED;
}
static int testuio_probe(struct platform_device* pdev)
{
struct testuio_dev* dev;
struct resource* res;
int irq;
// collect handles and information from platform device
// devm_kzalloc => managed (free upon detaching device from system) kernel zero-initialized memory allocation
// gfp flags => get free page flags
dev = devm_kzalloc(&pdev->dev, (sizeof(struct testuio_dev)), GFP_KERNEL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
// basic uio info struct initialization (required!)
dev->info->name = "testuio";
dev->info->version = "0.0.1";
// memory region initialization
dev->info->mem[0].name = "dummy_mem";
dev->info->mem[0].addr = res->start;
dev->info->mem[0].size = resource_size(res);
dev->info->mem[0].memtype = UIO_MEM_PHYS;
// other memory types include:
// - UIO_MEM_LOGICAL allocated by kmalloc
// - UIO_MEM_VIRTUAL allocated by vmalloc
// interrupt initialization
dev->info->irq = irq;
dev->info->irq_flags = 0;
dev->info->handler = &testuio_irq_handler;
if(uio_register_device(&pdev->dev, info))
{
iounmap(dev->info->mem[0].internal_addr);
return -ENODEV;
} else {
return 0;
}
}
int main(int argc, char** argv)
{
int uio_fd; // UIO file descriptor
unsigned int uio_size; // memory size of UIO
FILE* size_fp; // pointer to the file containing memory size
void* base_address; // start address of mapped memory
uio_fd = open(/dev/uio, O_RDWR);
if(uio_fd == -1)
return EOPENUIOFD;
size_fp = fopen(UIO_SIZE_FILE, O_RDONLY);
fscanf(size_fp, "0x%08x", &uio_size); // 0x%08x expects unsigned int; %p expects void*
// Whenever the user space program reads or writes in the virtual address range
// it is accessing the device w/o the need for a system call. This improves performance.
base_address = mmap(NULL, // NULL => kernel chooses page-aligned address at which to create mapping
uio_size, // length of memory mapping
PROT_READ | PROT_WRITE, // flags: grant read + write access
MAP_SHARED_VALIDATE, // updates to mapping are visible to other processes
// (+validates given flags, available since Linux 4.15)
uio_fd,
0);
if (uio_fd == MAP_FAILED)
return EMEMMAP;
// --- BEGIN APPLICATION CODE
// ??? Is this even the right spot for the application code
// see also
// https://docplayer.net/37414164-Linux-user-space-device-drivers-john-linn-based-on-3-14-linux-kernel.html
// helpful: https://stackoverflow.com/questions/26259421/use-mmap-in-c-to-write-into-memory
// TODO: read in content from file as done in link above using fstat
int testvals[8] = {0xDEADBEEF, 7, 12, 13, 31, 42, -63, -65535}
memcpy(base_address, testvals, sizeof(testvals));
for(int i=0; i<8; i++)
{
printk(KERN_INFO "The value at address %p is %d",
base_address+i*sizeof(int),
(int)*(base_address+i*sizeof(int)));
}
//! interrupt stuff
// read() returns the number of interrupt events.
// It allows blocking and non-blocking modes with this being blocking mode
int pending = 0;
int reennable = 1;
read(uio_fd, (void*)&pending, sizeof(int));
//! device specific processing
// acking the interrupt in the device
write(uio_fd, (void*)&reenable, sizeof(int));
// --- END APPLICATON CODE
//! undo virtual address mapping
munmap(base_address, uio_size);
return 0;
}
You seem to be mixing up kernel and userspace stuff in your code. However all this is wrong:
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
As you're stitching some user-space include paths to your kernel flags.
And also all these:
#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf
don't belong in a kernel module.The main
function also has to go away.
My assumption is you need a kernel module and a userspace application to test it. Don't mix the two of them together. Keep these things separate.
User contributions licensed under CC BY-SA 3.0