Is there a method of servicing hardware interrupts in standard C (ANSI/ISO)? All the implementations Ive seen so far either use compiler specific language extensions or pre-processor directives.
I just came across the standard C library 'signals' feature, but wikipedia is very light on its use and I dont think it serves the purpose.
POSIX signals can allow a user program written in C to catch and handle some types of interrupts and/or exceptions. It's the most standard approach I know about.
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
int a,b,*p;
jmp_buf jump_destination;
void exception_handler (int sg)
{
printf ("Error dereferencing pointer\n");
p=&b; /* pointer quick fix. */
longjmp(jump_destination,1); /* long GOTO... */
}
void main (void)
{
int i;
signal (SIGSEGV, exception_handler);
b=0; p=NULL;
setjmp(jump_destination); /* ...to this position */
printf ("Trying to dereference pointer p with value %08.8X... ",p);
printf ("After dereferencing pointer, its value is: %d\n", *p);
}
For hardware interrupts, C has no explicit semantics, mainly because it is not needed. For example, a Linux device driver can install its own interrupt handler for a hardware device. All you need is to call request_irq() function with the address of the function that will be in charge of handling the interrupt.
For example, this will install an interrupt handler for the RTC chip (assumming it's present and activated in your hardware)
...
...
res=request_irq (8, /* que IRQ queremos */
interrupt_handler, /* address of handler */
IRQF_DISABLED, /* this is not a shared IRQ */
“mydriver", /* to be shown at /proc/interrupts */
NULL);
if (res!=0)
{
printk ("Can't request IRQ 8\n");
}
...
...
Your handler is just a regular C function:
static irqreturn_t gestor_interrupcion (int irq, void *dev_id, struct pt_regs *regs)
{
/* do stuff with your device, like read time or whatever */
...
...
...
return IRQ_HANDLED; /* notify the kernel we have handled this interrupt */
}
This is possible (to use a regular C function to handle a hardware interrupt) because the handler itself is called from another kernel function, that has taken care of preserving the current context so the interrupted process won't notice anything. If you are dealing with interrupts in a "naked" computer and you want to keep your C code from deviating from the standard, then you will need to use some assembler to call your function.
No.
Signals (from POSIX) aren't for handling hardware interrupts, though they can be connected to them. They're for handling higher level system events.
You'll have to do something like the implementations you've seen, with specific code for each hardware platform you want to support.
At some point your implementation will have to interface with some standard outside of the C spec, whether that's Linux's family of types and function signatures that define interrupt handlers, a similar body of C code built up for another operating system, or maybe the hardware spec for an embedded platform that would allow you to implement your own. If you can clarify your goal, it might be possible to give a more specific answer.
Related
How can a program signal itself when it does a write access to a configurable memory region?
This would be something similar to the data-breakpoint feature found in some debuggers. POSIX compliance is desired but not required as long as it works on Linux.
Here there is an illustrative code of what I would like:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void configure_trap(void *FROM, void *TO){
/*
Set a trap on write access to any memory location from
address FROM to address TO.
When the trap is triggered, send SIGTRAP to the process.
There is no need for an answer to have the full code, just
an indication on how to proceed.
*/
}
char *ptr;
void trap_signal_handler(int signum){
if(ptr[123] == 'x'){
printf("Invalid value in ptr[123] !!!\n");
/*
Print a backtrace using libunwind. (Not part of this question.)
*/
}
}
void some_function(){
ptr[123] = 'x';
/*
This write access could be performed directly in this function or
another function called directly or indirectly by this one and it
could reside in this program or in an external library or could even
be performed in a system call.
trap_signal_handler should be called at this point.
After the signal handler has been executed, program should resume
normal operation.
*/
}
int main(){
struct sigaction sa = { .sa_handler = trap_signal_handler };
sigaction(SIGTRAP, &sa, NULL);
ptr = malloc(1024);
configure_trap(&ptr[123], &ptr[123]);
some_function();
return(0);
}
Thanks!
First, use mprotect() to mark a page read-only. Then when it is written, SIGSEGV will be raised. You will have installed a signal handler for this, and if it is done using sigaction you can know what address was accessed by inspecting si_addr. For more on this, see: C SIGSEGV Handler & Mprotect
Note that mprotect() has a granularity of one page, meaning if you try to protect a single byte, you will actually have protected 4 KB (if that's your page size).
Use the https://github.com/vicencb/qdbp project.
It first mprotects the memory page as read-only.
When a SIGSEGV is raised it single steps your program one instruction at a time until the one that caused the write to the read-only memory.
Then it calls your callback.
I'm working on an embedded Linux ARM system that needs to react to a power failure signal by turning off some power supplies (via GPIO control) in a specific sequence. This process needs to start as soon as possible, so I've installed an interrupt handler to detect this power failure.
The problem is that we need to introduce a little bit of delay between turning each supply off. I understand that delays are not usually allowed in an interrupt handler, but it's totally okay if this handler never returns (power is failing!).
I'm trying to introduce a delay by using the method described in this post, but I can't for the life of me actually cause a measurable delay (observed on an oscilloscope).
What am I doing wrong, and how can I do it right?
What follows is the relevant code.
/* This function sets en_gpio low, then waits until pg_gpio goes low. */
static inline void powerdown(int en_gpio, int pg_gpio)
{
/* Bring the enable line low. */
gpio_set_value(en_gpio, 0);
/* Loop until power good goes low. */
while (gpio_get_value(pg_gpio) != 0);
}
/* This is my attempt at a delay function. */
#define DELAY_COUNT 1000000000
static void delay(void)
{
volatile u_int32_t random;
volatile u_int32_t accum;
volatile u_int32_t i;
get_random_bytes((void*)&random, 4);
accum = 0;
for (i = 0; i < DELAY_COUNT; i++)
accum = accum * random;
}
/* This is the interrupt handler. */
static irqreturn_t power_fail_interrupt(int irq, void *dev_id)
{
powerdown(VCC0V75_EN, VCC0V75_PG);
delay();
powerdown(DVDD15_EN, DVDD15_PG);
delay();
powerdown(DVDD18_EN, DVDD18_PG);
delay();
powerdown(CVDD1_EN, CVDD1_PG);
delay();
powerdown(CVDD_EN, CVDD_PG);
/* It doesn't matter if we get past this point. Power is failing. */
/* I'm amazed this printk() sometimes gets the message out before power drops! */
printk(KERN_ALERT "egon_power_fail driver: Power failure detected!\n");
return IRQ_HANDLED;
}
Using delay functions in hard IRQ handlers is usually bad idea, because interrupts are disabled in hard IRQ handler and system will hang until your hard IRQ function is finished. On the other hand, you can't use sleep functions in hard IRQ handler since hard IRQ is atomic context.
Taking all that into the account, you may want to use threaded IRQ. This way hard IRQ handler is only wakes bottom half IRQ handler (which executed in kernel thread). In this threaded handler you can use regular sleep functions.
To implement threaded IRQ instead of regular IRQ, just replace your request_irq() function with request_threaded_irq() function. E.g. if you have requesting IRQ like this:
ret = request_irq(irq, your_irq_handler, IRQF_SHARED,
dev_name(&dev->dev), chip);
You can replace it with something like this:
ret = request_threaded_irq(irq, NULL, your_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
dev_name(&dev->dev), chip);
Here NULL means that the standard hard IRQ handler will be used (which is only wakes threaded IRQ handler), and your_irq_handler() function will be executed in kernel thread (where you can call sleep functions). Also IRQF_ONESHOT flag should be used when requesting threaded IRQ.
It also should be mentioned that there is managed version ofrequest_threaded_irq() function, called devm_request_threaded_irq(). Using it (instead of regular request_threaded_irq()) allows you to omit free_irq() function in your driver exit function (and also in error path). I would recommend you use devm_* function (if your kernel version already has it). But don't forget to remove all the free_irq() calls in your driver if you decided to go with devm_*.
TL;DR
Replace your request_irq() with request_threaded_irq() (as it shown above) and you will be able to use sleep in your IRQ handler.
I would rearchitect this into two parts:
An interrupt handler
An application which waits for the interrupt handler, and then does the timed logic.
As you have experienced, sleeping in an IRQ handler is bad. So is any significant busy waiting as it kills the responsiveness of the rest of the system.
The specific mechanism for the interaction could be any of several means.
If a Linux device driver were used, it could accept read() operations and return something (like how long the wait was, or even single byte of zero) when an interrupt occurs. So the application would open the device, do a blocking read() and when it returns successfully (without error) do whatever logic is required all in user mode at (maybe) normal priority.
It turns out the root cause of my issue was a misconfigured pin (the one the interrupt signal was on), and my interrupt wasn't event occuring... I was looking at the rails coming down themselves uncontrolled. I'm guessing I messed this up while I was working on another part of the system...
I ended up using the following function to implement my delay in the hard interrupt. It's not sexy, but it does work and it's simple, and I believe the shift operation avoids overflow as pointed out in a comment by #specializt.
This code is very specific to a single piece of equipment, and the testing I've done today shows it to be pretty stable.
/* This is my attempt at a delay function. */
/* A count of 8 is approximately 100 microseconds */
static void delay(int delay_count)
{
volatile u_int32_t random;
volatile u_int64_t accum;
volatile u_int32_t i;
accum = 0;
for (i = 0; i < delay_count; i++)
{
get_random_bytes((void*)&random, 4);
accum = accum * random;
accum = accum >> 32;
}
}
I've written a program that uses SIGALRM and a signal handler.
I'm now trying to add this as a test module within the kernel.
I found that I had to replace a lot of the functions that libc provides with their underlying syscalls..examples being timer_create with sys_timer_create timer_settime with sys_timer_settime and so on.
However, I'm having issues with sigaction.
Compiling the kernel throws the following error
arch/arm/mach-vexpress/cpufreq_test.c:157:2: error: implicit declaration of function 'sys_sigaction' [-Werror=implicit-function-declaration]
I've attached the relevant code block below
int estimate_from_cycles() {
timer_t timer;
struct itimerspec old;
struct sigaction sig_action;
struct sigevent sig_event;
sigset_t sig_mask;
memset(&sig_action, 0, sizeof(struct sigaction));
sig_action.sa_handler = alarm_handler;
sigemptyset(&sig_action.sa_mask);
VERBOSE("Blocking signal %d\n", SIGALRM);
sigemptyset(&sig_mask);
sigaddset(&sig_mask, SIGALRM);
if(sys_sigaction(SIGALRM, &sig_action, NULL)) {
ERROR("Could not assign sigaction\n");
return -1;
}
if (sigprocmask(SIG_SETMASK, &sig_mask, NULL) == -1) {
ERROR("sigprocmask failed\n");
return -1;
}
memset (&sig_event, 0, sizeof (struct sigevent));
sig_event.sigev_notify = SIGEV_SIGNAL;
sig_event.sigev_signo = SIGALRM;
sig_event.sigev_value.sival_ptr = &timer;
if (sys_timer_create(CLOCK_PROCESS_CPUTIME_ID, &sig_event, &timer)) {
ERROR("Could not create timer\n");
return -1;
}
if (sigprocmask(SIG_UNBLOCK, &sig_mask, NULL) == -1) {
ERROR("sigprocmask unblock failed\n");
return -1;
}
cycles = 0;
VERBOSE("Entering main loop\n");
if(sys_timer_settime(timer, 0, &time_period, &old)) {
ERROR("Could not set timer\n");
return -1;
}
while(1) {
ADD(CYCLES_REGISTER, 1);
}
return 0;
}
Is such an approach of taking user-space code and changing the calls alone sufficient to run the code in kernel-space?
Is such an approach of taking user-space code and changing the calls
alone sufficient to run the code in kernel-space?
Of course not! What are you doing is to call the implementation of a system call directly from kernel space, but there is not guarantee that they SYS_function has the same function definition as the system call. The correct approach is to search for the correct kernel routine that does what you need. Unless you are writing a driver or a kernel feature you don't nee to write kernel code. System calls must be only invoked from user space. Their main purpose is to offer a safe manner to access low level mechanisms offered by an operating system such as File System, Socket and so on.
Regarding signals. You had a TERRIBLE idea to try to use signal system calls from kernel space in order to receive a signal. A process sends a signal to another process and signal are meant to be used in user space, so between user space processes. Typically, what happens when you send a signal to another process is that, if the signal is not masked, the receiving process is stopped and the signal handler is executed. Note that in order to achieve this result two switches between user space and kernel space are required.
However, the kernel has its internal tasks which have exactly the same structure of a user space with some differences ( e.g. memory mapping, parent process, etc..). Of course you cannot send a signal from a user process to a kernel thread (imagine what happen if you send a SIGKILL to a crucial component). Since kernel threads have the same structure of user space thread, they can receive signal but its default behaviour is to drop them unless differently specified.
I'd recommend to change you code to try to send a signal from kernel space to user space rather than try to receive one. ( How would you send a signal to kernel space? which pid would you specify?). This may be a good starting point : http://people.ee.ethz.ch/~arkeller/linux/kernel_user_space_howto.html#toc6
You are having problem with sys_sigaction because this is the old definition of the system call. The correct definition should be sys_rt_sigaction.
From the kernel source 3.12 :
#ifdef CONFIG_OLD_SIGACTION
asmlinkage long sys_sigaction(int, const struct old_sigaction __user *,
struct old_sigaction __user *);
#endif
#ifndef CONFIG_ODD_RT_SIGACTION
asmlinkage long sys_rt_sigaction(int,
const struct sigaction __user *,
struct sigaction __user *,
size_t);
#endif
BTW, you should not call any of them, they are meant to be called from user space.
You're working in kernel space so you should start thinking like you're working in kernel space instead of trying to port a userspace hack into the kernel. If you need to call the sys_* family of functions in kernel space, 99.95% of the time, you're already doing something very, very wrong.
Instead of while (1), have it break the loop on a volatile variable and start a thread that simply sleeps and change the value of the variable when it finishes.
I.e.
void some_function(volatile int *condition) {
sleep(x);
*condition = 0;
}
volatile int condition = 1;
start_thread(some_function, &condition);
while(condition) {
ADD(CYCLES_REGISTER, 1);
}
However, what you're doing (I'm assuming you're trying to get the number of cycles the CPU is operating at) is inherently impossible on a preemptive kernel like Linux without a lot of hacking. If you keep interrupts on, your cycle count will be inaccurate since your kernel thread may be switched out at any time. If you turn interrupts off, other threads won't run and your code will just infinite loop and hang the kernel.
Are you sure you can't simply use the BogoMIPs value from the kernel? It is essentially what you're trying to measure but the kernel does it very early in the boot process and does it right.
I'm learning interrupt handling in linux kernel and tried below code snippet to register a dummy irq handler on IRQ2. But it seems it is not getting registered as I'm seeing a negative return value and a message in kernel as below , arising from cleanup function which is trying to do free_irq():
[ 2203.989585] Trying to free already-free IRQ 2
below is the printk from kernel log which suggests that it hasn't got registered:
Here with registering IRQ handler on IRQ2 for flowTest...retval_irqreg= -22
Below is relevant part of my code which has four functions
1 the bottom half
2. handler
3. init function
4. cleanup function
I'm not scheduling the bottom half yet , though it is present below. I'm working on kernel 3.5.0-17.
//Bottom half for the irq handler
static void bh_flowTest()
{
printk(KERN_INFO "Inside bottom half for flowTest.\n");
}
// IRQ handler function
static irqreturn_t flow_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
printk(KERN_INFO "This is flowTest IRQ handler.\n");
//static struct tq_struct task = {NULL, 0, bh_flowTest, NULL};
/* Schedule bottom half to run */
//queue_task(&task, &tq_immediate);
// mark_bh(IMMEDIATE_BH);
return IRQ_HANDLED;
}
static int flow_init(void)
{
printk(KERN_ALERT "Here with flowTest module ... loading...\n");
int result=0;
dev_t dev=0;
result = alloc_chrdev_region(&dev, minor_num,
num_devices,"mod_flowtest"); // allocate major number dynamically.
i=MAJOR(dev);
printk(KERN_ALERT "Major allocated = %d",i);
cdev_init(&ms_flow_cd,&flow_fops);
cdev_add(&ms_flow_cd,dev,1);
//Registering interrupt handler on IRQ2 since IRQ2 is free as per /proc/interrupts
int retval_irqreg;
retval_irqreg=request_irq(2,(irq_handler_t)flow_irq_handler, /* our handler. It has been typecasted to remove warnings of incompatible pointer type , and enum irqreturn_t. Try removing the cast and see the warnings */
IRQF_SHARED,
"test_flow_irq_handler", NULL);
printk(KERN_ALERT "Here with registering IRQ handler on IRQ2 for flowTest...retval_irqreg= %d\n",retval_irqreg);
return 0;
}
static void flow_terminate(void)
{
dev_t devno=MKDEV(i,0); // wrap major/minor numbers in a dev_t structure , to pass for deassigning.
printk(KERN_ALERT "Going out... exiting...\n");
unregister_chrdev_region(devno,num_devices); //remove entry from the /proc/devices
free_irq(2, NULL);
}
I feel there is some basic mistake ,but if someone could point me to that please..!
It looks like IRQ 2 does NOT yet exist on your hardware. i.e. when you call request_irq() the Linux kernel does NOT yet know which interrrupt on which physical line from which peripheral hardware must be treated as the trigger for IRQ number "2" as it is just a number which is NOT yet associated with any actual physical interrupt yet).
Basically one first needs to associate a particular IRQ number with an actual physical
hardware interrupt before attempting to register an ISR for that IRQ number. thsi is commonly done in the Linux kernel using irq_domain_add_linear().
In the past, IRQ numbers could be chosen so they matched the hardware IRQ line into the root interrupt controller (i.e. the component actually firing the interrupt line to the CPU) nowadays this number is just a number.
The irq_alloc_desc*() and irq_free_desc*() APIs provide allocation of irq numbers, but they don't provide any support for reverse mapping of the controller-local IRQ (hwirq) number into the Linux IRQ number space.
The current design of the Linux kernel uses a single large number space where each separate IRQ source is assigned a different number. This is simple when there is only one interrupt controller, but in systems with multiple interrupt controllers the kernel must ensure that each one gets assigned non-overlapping allocations of Linux IRQ numbers.
More details in the Linux-kernel Documentation/IRQ-domain.txt.
Also you may be interested in registering an ISR for an existing interrupt (say the keyboard IRQ?) When multiple ISRs are registered for a single IRQ, the Linux kernel will call each of the ISRs in each drivers that register a shared irq.
I ve got some SSE procedure which zeroes memory in loop,
When pointer is unaligned it raises SIGSEGV which goes
into my handler. Can I get more info here in such handler
routine, Now I got no info where it was done, can I also
react in some predictible way from it? When I chose to
ignore it It seemd to me that it should go back and becouse
it was in loop raise SIGSEGV again, (got such behaviour with
division by zero when where I ignore it it just goes on
further) but it does not work here such way but sadly just
crashes after ignoring. Can I do some more eleborate recovery here?
EDIT - ADD
in my signal.h (some very old win32 compiler but I use it)
I have such stuff
/* _SIGCONTEXT contains exception info for WIN32 exceptions that were caught
and turned into signals. There will always be three 32-bit parameters
passed to the user's signal handler. Unused parameters will be 0. The
_PSIGCONTEXT parameter will always be the last (third) parameter.
*/
typedef struct
{
struct _EXCEPTION_RECORD * _pexecptionrecord; /* defined in WINNT.H */
struct _CONTEXT * _pcontext; /* defined in WINNT.H */
unsigned long _result; /* return value for the SEH */
} _SIGCONTEXT, *_PSIGCONTEXT;
typedef int sig_atomic_t; /* Atomic entity type (ANSI) */
typedef void (*_CatcherPTR)(int);
#define SIG_DFL ((_CatcherPTR)0) /* Default action */
#define SIG_IGN ((_CatcherPTR)1) /* Ignore action */
#define SIG_ERR ((_CatcherPTR)-1) /* Error return */
//////////////////////////////////
//skipped #define SIGABRT 22
// #define SIGFPE 8 .... constants block here
int raise(int __sig);
void (*signal(int __sig, void (*__func)(int) )) (int);
Signal Raise are understood for me but how to get to data of SIGCONTEXT or use 'catcher' ?
While it is possible on some operating systems under certain circumstances to catch and handle SIGSEGV, SIGBUS, SIGILL and SIGFPE it's a really bad idea to do anything other than crashing. They indicate a bug in your program that you need to fix, not something you just sweep under the rug.
But in case you really enjoy shooting yourself in the foot and leaving horrible undebuggable and unmaintainable messes that others will have to clean up after you while cursing you and your ancestors while wishing that voodoo worked, have a peek at the documentation for sigaction() and how siginfo_t. They contain at least some parts of what you need.