Interrupt Service Routines: Difference between revisions

Jump to navigation Jump to search
formatting, appearance, orthography
[unchecked revision][unchecked revision]
No edit summary
(formatting, appearance, orthography)
Line 1:
The [[:Category:x86|x86]] architecture is an interrupt driven system. External events trigger an interrupt - the normal control flow is interrupted and aan '''Interrupt Service Routine''' (ISR) is called.
 
Such events can be triggered by hardware or software. An example of a hardware interrupt is the keyboard: Everyevery time you press a key, the keyboard triggers IRQ1 (Interrupt Request 1), and the corresponding interrupt handler is called. Timers, and disk request completion are other possible sources of hardware interrupts.
 
Software driven interrupts are triggered by the <tt>int</tt> opcode; e.g. the services provided by MS-DOS are called by the software triggering <tt>INT 21h</tt> and passing the applicable parameters in CPU registers.
 
For the system to know which interrupt service routine to call when a certain interrupt occurs, offsets to the ISR'sISRs are stored in the [[IDT|Interrupt Descriptor Table]] when you're in [[Protected mode]], or in the [[Interrupt Vector Table]] when you're in [[Real Mode]].
 
An ISR is called directly by the CPU, and the protocol for calling an ISR differs from calling e.g. a C function. Most importantly, an ISR has to end with the <tt>iret</tt> opcode, whereas usual C functions end with <tt>ret</tt> or <tt>retf</tt>. The obvious but nevertheless wrong solution leads to one of the most "popular" triple-fault errors among OS programmers.
 
== The Problem ==
Many people shun away from Assemblerassembly, and want to do as much as possible in their favorite high-level language. [[GCC]] (as well as other compilers) allow you to add inline Assemblerassembly, so many programmers are tempted to write an ISR like this:
 
<source lang="C">
Line 23:
</source>
 
This cannot work. The compiler doesn't understand what is going on. It doesn't understand that the registers and stack isare required to be preserved between the asm statements; the optimizer will likely corrupt the function. Additionally, the compiler adds stack handling code before and after your function, which together with the iret results in Assemblerassembly code resembling this:
 
<source lang="asm">
Line 38:
</source>
 
It should be obvious how this messes up the stack (ebp gets push'edpushed but never pop'edpopped). Don't do this. Instead, these are your options.
 
== Solutions ==
=== Plain AssemblerAssembly ===
 
Learn enough about Assemblerassembly to write your interrupt handlers in it. ;-)
 
=== Two-Stage AssemblerAssembly Wrapping ===
 
Write an Assemblerassembly wrapper calling the C function to do the real work, and then doing the iret.
 
<source lang="asm">
Line 68 ⟶ 69:
</source>
 
=== Compiler Specific Interrupt Directives ===
 
Some compilers for some processors have directives allowing you to declare a routine interrupt, offer a #pragma interrupt, or a dedicated macro. Clang 3.9, Borland C, Watcom C/C++, Microsoft C 6.0 and Free Pascal Compiler 1.9.* and up offer this, while GCC does not. Visual C++ offers an alternative shown under '''Naked Functions''':
 
==== Clang ====
 
As of version 3.9 it supports [http://clang.llvm.org/docs/AttributeReference.html#id1 interrupt attribute] for x86/x86-64 targets.
 
<source lang="C">
struct interrupt_frame
Line 89 ⟶ 93:
}
</source>
 
==== Borland C ====
 
<source lang="C">
/* Borland C */
Line 98 ⟶ 104:
</source>
 
==== Watcom C/C++ ====
 
<source lang="C">
/* Watcom C/C++ */
Line 106 ⟶ 113:
}
</source>
 
==== Naked Functions ====
Some Compilers can be used to make interrupt routines, but requires you to manually handle the stack and return operations. Doing so requires that the function be generated without an epilogue or prologue. This is called making the function ''naked'' - this is done in Visual C++ by adding the attribute ''_declspec(naked)'' to the function. You need to verify that you do include a return operation (such as ''iretd'') as that is part of the epilogue that the compiler has now been instructed to not include.
 
Some Compilerscompilers can be used to make interrupt routines, but requiresrequire you to manually handle the stack and return operations. Doing so requires that the function beis generated without an epilogue or prologue. This is called making the function ''naked'' - this is done in Visual C++ by adding the attribute ''_declspec(naked)'' to the function. You need to verify that you do include a return operation (such as ''iretd'') as that is part of the epilogue that the compiler has now been instructed to not include.
 
If you intend to use local variables, you must set up the stack frame in the manner which the compiler expects; as ISRs are non-reentrant, however, you can simply use static variables.
 
===== Visual C++ =====
 
Visual C++ also supplies the __LOCAL_SIZE assembler macro, which notifies you how much space is required by the objects on the stack for the function.
Line 130 ⟶ 139:
</source>
 
==== gcc / g++ ====
 
Neither gcc nor g++ offer any means (on x86 or x86-64) to have an interrupt service routine only in C or C++ without performing '''black magic'''.
 
===== Black Magic =====
 
Look at the faulty code [[Interrupt_Service_Routines#The_Problem|above]], where the proper C function exit code was skipped, screwing up the stack. Now, consider this code snippet, where the exit code is added manually:
 
<source lang="C">
/* BLACK MAGIC - Stronglystrongly Discourageddiscouraged! */
void interrupt_handler() {
__asm__("pushad");
Line 162 ⟶ 171:
</source>
 
This assumes that <tt>leave</tt> is the correct end-of-function handling - you are doing the function return code "by hand", and leave the compiler-generated handling as "dead code". Needless to say, such assumptions on compiler internals are dangerous. This code can break on a different compiler, or even a different version of the same compiler. It is therefore strongly discouraged, and listed only for completeness.
 
===== Asm Goto =====
:''Full article: [[ISRs_PIC_And_Multitasking|ISRs, PIC, And Multitasking]]''
 
Since GCCversion 4.5, the inline assemblerGCC supports the "asm goto" statement. It can be used to make ISRs as functions which return the correct address of the ISR entry point.
 
[[Category:Interrupts]]
Anonymous user
Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu