Page 1 of 1

GCC and interrupt handlers: More black magic.

Posted: Thu Jun 07, 2012 2:13 am
by linguofreak
I was looking at the black magic section on the Wiki page on interrupt handlers, and I thought up another way to kludge together interrupt handler code in gcc:

Code: Select all

void * random_interrupt_handler(int dummy)
{
    if(dummy)
        return &&handler;

    handler:
    asm(//ISR prologue goes here);

    //C code

    asm(//ISR epilogue goes here);
}
Calling random_interrupt_handler() will return the address of the actual handler (this is useful as the unary operator && can only be used to get the address of a label in the same function), which we can use to construct our IDT entry.

The good: The handler's prologue and epilogue consist entirely of our handwritten inline assembly, unlike the Wiki's black magic code, where part of the prologue is generated by the compiler and part is written by us, and we have to write an epilogue that matches the part of the prologue the compiler constructs.

The bad: The compiler will give a warning about returning the address of a local variable because we return &&handler. We also have to keep track of the number and size of local variables we use and make sure our prologue and epilogue create an appropriate stack for them (this is the flip side of "the good").

The ugly: Ideally, the first line of the function would simply be "return &&handler;". Unfortunately, that results in the handler code being "optimized" away by the compiler, even if options like -fno-dce are used. Therefore, we need to test a dummy variable with an if statement to convince the compiler that the handler code is actually needed. The wrapper function must always be called with "dummy" being non-zero, otherwise it will fall through to the interrupt handler, which is bad for obvious reasons.

I'd like to get people's opinions on how this compares to the "black magic" method listed on the Wiki. My impression is that it's less dependent on compiler internals (in that it doesn't depend on how the compiler generates stack handling code). Would you agree?

Re: GCC and interrupt handlers: More black magic.

Posted: Thu Jun 07, 2012 3:29 am
by bluemoon
While interrupt are not called *very frequently*, I think assembly stub is more maintainable and there is no need to optimize that call instruction away - you might just lost a nanosecond once every millisecond (0.0001%).

Re: GCC and interrupt handlers: More black magic.

Posted: Sat Jun 09, 2012 11:09 pm
by jbemmel
Here's an alternative solution:

void * someISR( )
{
__asm__ __volatile__ goto( "jmp %l[endOfISR]" : : : "memory" : endOfISR );
__asm__ __volatile__( ".align 16\t\n" : : : "memory" );
startOfISR:
__asm__ __volatile__( "nop\t\n" : : : "memory" );

printf( "Hello, world of ISRs!" );
__asm__ __volatile__( "iret\t\n" : : : "memory" );
endOfISR:
__asm__ __volatile__ goto( "mov %l[startOfISR], %%rax" : : : "memory" : startOfISR );
}


This does not require the dummy variable & associated test, and does not give a compiler warning either. It uses "asm goto", a relatively new feature of GCC (see http://wiki.osdev.org/Inline_Assembly#asm_goto). Now GCC does not optimize the ISR code away, because it cannot know what's happening in the __asm__ block

Just be careful when you need to interpret the DWARF2 information that the compiler generates for this function - it probably gets it wrong...

Re: GCC and interrupt handlers: More black magic.

Posted: Sun Jun 10, 2012 12:30 am
by jbemmel
bluemoon wrote:While interrupt are not called *very frequently*, I think assembly stub is more maintainable and there is no need to optimize that call instruction away - you might just lost a nanosecond once every millisecond (0.0001%).
While this may have some merit for device triggered interrupts, trap handlers can benefit from removing the extra call/ret instructions - not only for timing, but also for the additional stack usage. Some handlers like the stack fault handler (for example) cannot afford to use the stack without fixing it first

Re: GCC and interrupt handlers: More black magic.

Posted: Sun Jun 10, 2012 12:35 am
by Nessphoro
jbemmel wrote:
bluemoon wrote:While interrupt are not called *very frequently*, I think assembly stub is more maintainable and there is no need to optimize that call instruction away - you might just lost a nanosecond once every millisecond (0.0001%).
While this may have some merit for device triggered interrupts, trap handlers can benefit from removing the extra call/ret instructions - not only for timing, but also for the additional stack usage. Some handlers like the stack fault handler (for example) cannot afford to use the stack without fixing it first
If the stack is broken, how did the CPU push data onto it?

Re: GCC and interrupt handlers: More black magic.

Posted: Sun Jun 10, 2012 1:11 am
by bluemoon
There is two kind of stack fault: one occur in user mode and one in kernel mode.

For user mode stack fault the CPU perform stack switch when enter kernel so it's all good.
For kernel mode stack fault, there are three possibility:
Without IST or task gate: you are dead. The CPU cannot push return address before invoke stack fault handler, result in double fault (see below)
With task gate: CPU switch to good stack and no problem or significant stress to use stack for an extra call.
With IST: CPU switch to good stack and no problem or significant stress to use stack for an extra call.

For Double Fault, the above logic repeat for with or without IST / task.

Note: IST has it's own issue, especially on nested NMI, but it's beyond the scope of this thread.