I would like to write some code (for example as a small kernel module) to instrument local interrupts on Linux running on the x86-64 architecture, i.e. I would like to write some kind of handler that is called by the kernel every time a local interrupt is triggered by the APIC.
The handler would then check whether a certain process is currently running and inspect said process' memory.
I realize that what I am trying to do may not be good engineering practice, but my aim is to create a hacky one-off solution for exploration/research purposes.
In the ideal case, there would be some kind of function similar to request_irq [1] (which as far as I can tell is used for handling interrupts from devices like keyboards, network cards, ...) allowing me to tell the kernel to run my code every time a local timer interrupt occurs.
Does anybody have any pointers on how to accomplish this?
Does the kernel provide an API for registering a handler for local interrupts?
If not, I could directly modify the kernel's source code for handling interrupts. Where in the kernel would I find this code?
Edit: Here is what my research so far has found.
TLDR:request_irq is not the way to hook local timer interrupts.
According to Understanding the Linux Kernel, Table 4.2 [2], the interrupt vector 0xef is allocated to Local APIC timer interrupts. The kernel source confirms this [3].
Since request_irq takes an interrupt vector as its first argument, let's try registering a handler for this vector inside a kernel module:
#include <linux/module.h>
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/interrupt.h> // included for IRQF_ and request_irq
#include <linux/irqreturn.h> // included for IRQ_NONE
#include <asm/irq_vectors.h> // included for LOCAL_TIMER_VECTOR
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Instrument local timer interrupts");
static int DEVICE_COOKIE = 1;
static int count = 0;
static irqreturn_t handler(int irq, void *dev) {
count++;
return IRQ_NONE;
}
static int __init test_init(void) {
int status;
printk(KERN_INFO "Running request_irq\n");
status = request_irq(
LOCAL_TIMER_VECTOR,
&handler,
IRQF_TIMER,
"foobar",
&DEVICE_COOKIE);
if (status == 0) {
printk(KERN_INFO "Successfully installed handler\n");
return 0;
} else {
printk(
KERN_INFO "Failed to install handler. error code: %d\n",
status);
return -1;
}
}
static void __exit test_cleanup(void) {
free_irq(LOCAL_TIMER_VECTOR, &DEVICE_COOKIE);
printk(KERN_INFO "Goodbye kernel. I saw %d interrupts.\n", count);
}
module_init(test_init);
module_exit(test_cleanup);
When we try to insert the module with insmod, request_irq returns -EINVAL:
[74890.287173] Running request_irq
[74890.287174] Failed to install handler. error code: -22
So where does the -EINVAL come from?
Reading through the kernel source, we find that request_irq is just a wrapper around request_threaded_irq [4]. request_threaded_irq calls irq_to_desc and returns -EINVAL if the call fails. We can easily check whether this is the case with another small kernel module:
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/interrupt.h> // included for IRQF_ and request_irq
#include <linux/irqreturn.h> // included for IRQ_NONE
#include <asm/irq_vectors.h> // included for LOCAL_TIMER_VECTOR
#include <linux/irqnr.h> // included for irq_to-desc
#include <linux/irqdesc.h> // included for irq_desc
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Call irq_to_desc(LOCAL_TIMER_VECTOR)");
static int __init test_init(void) {
struct irq_desc *desc;
desc = irq_to_desc(LOCAL_TIMER_VECTOR);
if (!desc) {
printk(KERN_INFO "irq_to_desc(LOCAL_TIMER_VECTOR) failed");
return -1;
}
return 0;
}
static void __exit test_cleanup(void) {
}
module_init(test_init);
module_exit(test_cleanup);
And indeed it fails:
[75787.142533] irq_to_desc(LOCAL_TIMER_VECTOR) failed
[1] www.makelinux.net/books/lkd2/ch06lev1sec3
[2] www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s06.html
[3] lxr.free-electrons.com/source/arch/x86/include/asm/irq_vectors.h?v=4.8#L108
[4] lxr.free-electrons.com/source/kernel/irq/manage.c?v=4.8#L1634
Related
I'm trying to get an interrupt whenever the UART line gets data on the BeagleBone Black. However, I'm stuck on figuring out how to register the handler. I can use request_irq() to register GPIO interrupts for example, but trying to get the irq for the UART doesn't seem to work. I believe 73 is the right number, as after registering it, reading from /dev/ttyO1 (the UART dev file) throws an error, but the handler never triggers.
Am I approaching this right? What I'm currently trying is below. Thanks.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#define KERNEL_NAME "TestKernel"
#define IRQ_NUMBER 73
int id = 8273643;
irq_handler_t handler(int irq, void* dev_id, struct pt_regs* regs)
{
printk(KERN_ALERT KERNEL_NAME ": Handler called, irq: %d\n", irq);
return (irq_handler_t)IRQ_HANDLED;
}
static int __init driver_entry(void)
{
int result = request_irq(IRQ_NUMBER, (irq_handler_t)handler, 0, "test", &id);
printk(KERN_ALERT KERNEL_NAME ": Insert successful, result: %d\n", result);
return result;
}
static void __exit driver_exit(void)
{
free_irq(IRQ_NUMBER, NULL);
printk(KERN_ALERT KERNEL_NAME ": Cleanup\n");
}
module_init(driver_entry);
module_exit(driver_exit);
MODULE_LICENSE("GPL");
I have a simple module, written as follows:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
static int __init hellomod_init(void)
{
printk(KERN_DEBUG, "Hello, world!\n");
return 0;
}
static void __exit hellomod_exit(void)
{
printk(KERN_DEBUG, "Goodbye, world!");
}
module_init(hellomod_init);
module_exit(hellomod_exit);
and
$ cat /proc/sys/kernel/printk
7 7 7 7
so that any level messages should output.
The module loads and this can be verified with lsmod, however no output is produced when loading or unloading the module and checking dmesg.
I have tried replacing KERN_DEBUG with lower levels and still no output, so I don't think the log level is the issue.
How else can I verify the hellomod_init() function is called? If it is not being called, what is my error?
I am running and compiling against kernel version 4.6.1-2 on Arch Linux.
There shouldn't be a comma after KERN_DEBUG. So it should look something like this:
static int __init hellomod_init(void)
{
printk(KERN_DEBUG "Hello, world!\n");
return 0;
}
I am writing a simple kernel module, which could register an interrupt and handle it.
However, when I try to register interrupt by calling the request_irq function,
it returns error code -22 :
ERROR: Cannot request IRQ 30 - code -22 , EIO 5 , EINVAL 22
I believe, this error code is equal to EINVAL (invalid argument)
Please tell me, what I am doing wrong. Here is a module:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
void int068_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
printk("Interrupt should be handled there\n");
}
static int __init
clcdint_init(void)
{
unsigned int irq;
unsigned int irqflags;
int ret;
irq=68;
irqflags=IRQF_SHARED | IRQF_NO_SUSPEND;
ret = request_irq(irq, int068_interrupt,
irqflags, "clcdint-int068", NULL);
if (ret!=0) {
printk("ERROR: Cannot request IRQ %d", irq);
printk(" - code %d , EIO %d , EINVAL %d\n", ret, EIO, EINVAL);
}
printk("CLCDINT_INIT\n");
return 0;
}
module_init(clcdint_init);
static void __exit
clcdint_exit(void)
{
unsigned int irq;
irq=68;
free_irq(irq, NULL);
printk("CLCDINT_EXIT\n");
}
module_exit(clcdint_exit);
You can't pass a NULL context (last parameters of the request_irq() call) when dealing with a shared interrupt line (IRQF_SHARED flag is on).
To understand why consider the following scenario: you have two identical network cards sharing the same IRQ. The same driver will pass the same interrupt handler function, the same irq number and the same description. There is no way to distinguish the two instances of the registration except via the context parameter.
Therefore, as a precaution, you can't pass a NULL context parameter if you pass the IRQF_SHARED flag.
irqflags has a type of unsigned int, but originally it had type long.
Try the following statement, it will definitely work:
request_irq(irq, int068_interrupt,IRQF_SHARED | IRQF_NO_SUSPEND, "clcdint-int068", NULL);
I want to implement a counter in Linux device drivers which increments after every fixed interval of time. I want to do this with the help of timers. A sample code snippet would be very useful.
Have a look at following article IBM Developerworks: Timers and Lists
There is a small example of how to use Linux kernel timers (included it here for convenience, comments are from myself, removed printk messages)
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
static struct timer_list my_timer;
void my_timer_callback( unsigned long data )
{
/* do your timer stuff here */
}
int init_module(void)
{
/* setup your timer to call my_timer_callback */
setup_timer(&my_timer, my_timer_callback, 0);
/* setup timer interval to 200 msecs */
mod_timer(&my_timer, jiffies + msecs_to_jiffies(200));
return 0;
}
void cleanup_module(void)
{
/* remove kernel timer when unloading module */
del_timer(&my_timer);
return;
}
Around Linux kernel 4.15 release, void setup_timer(timer, function, data); became obsolete with an intent to remove it completely.
Instead, now we have to use
void timer_setup(
struct timer_list *timer,
void (*callback)(struct timer_list *),
unsigned int flags
);
This can be found in linux/timer.h file.
Here's a full example of module_with_timer.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
static struct timer_list my_timer;
void my_timer_callback(struct timer_list *timer) {
printk(KERN_ALERT "This line is printed after 5 seconds.\n");
}
static int init_module_with_timer(void) {
printk(KERN_ALERT "Initializing a module with timer.\n");
/* Setup the timer for initial use. Look in linux/timer.h for this function */
timer_setup(&my_timer, my_timer_callback, 0);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(5000));
return 0;
}
static void exit_module_with_timer(void) {
printk(KERN_ALERT "Goodbye, cruel world!\n");
del_timer(&my_timer);
}
module_init(init_module_with_timer);
module_exit(exit_module_with_timer);
And the Makefile is
obj-m = module_with_timer.o
# Get the current kernel version number
KVERSION = $(shell uname -r)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
Note: In real life programming, it is better to check the version of the kernel we are compiling for and then use an then appropriately start the timer.
References:
https://lwn.net/Articles/735887/
Depending on what you exactly want to do, you can directly use jiffies to measure time, as it has been suggested in the comments. You can also use kernel timers, and given the information in your question, they seem to be a better fit.
The kernel timers API is quite intuitive:
#include <linux/timer.h>
struct timer_list {
/* ... */
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
};
void init_timer(struct timer_list *timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
void add_timer(struct timer_list * timer);
int del_timer(struct timer_list * timer);
So you would just need to define a timer function and then initialize and start the timer.
You have several sources to further learn about this topic:
Understanding the Linux Kernel. This book is a sort of bible for the kernel. It is somehow outdated in some areas, but still a really good source of information.
Linux Device Drivers. This is a very useful book when developing device drivers. There is an online version too here. The chapter dealing with time, timers, etc. is chapter 7. This book may be also a bit outdated since it is from 2005 too.
Linux Kernel Development. I have not checked this book, but the good point is that it is much newer (from 2010), so you may find some updated information compared to the previous two books.
What would perror() be in the kernel? I can't tell what the error codes are from the error values ,i.e -22 alone.
The Linux kernel just uses negated errno codes as a convention. So look in asm-generic/errno-base.h or asm-generic/errno.h and find...
#define EINVAL 22 /* Invalid argument */
This can happen if you pass NULL to a function that doesn't accept NULL, for example. It depends on the function.
I know the question is old but I came across it and I found a workaround which is a little more satisfying than the accepted answer.
It's true there's no actual perror but errors can be printed in a nicer way (https://www.kernel.org/doc/html/latest/core-api/printk-formats.html#error-pointers). All we need to do is to supply the error pointer or convert it with the ERR_PTR function if we have a numeric value (basically just a cast) and we are settled.
One could then print the error directly or create their own perror as in the example:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#define MYPERROR(str, err) printk(str ": %pe\n", ERR_PTR(error))
static int __init mymodule_init(void) {
int error = -22;
printk("The error is %pe\n", ERR_PTR(error));
MYPERROR("Failure", error);
return 0;
}
static void __exit mymodule_exit(void) {
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");