Interrupt Descriptor Table
Ähnlich wie die Global Descriptor Table müssen wir auch noch die Interrupt Descriptor Table anlegen. Aufmerksame Programmierer erinnern sich, dass wir zuvor die Interrupts deaktiviert haben mittels cli
. Bevor wir diese reaktivieren können, müssen wir zwei Dinge erledigen:
- Interrupt Descriptor Table anlegen und aktivieren
- PIC ummappen und aktivieren
In diesem Tutorial werden wir den ersten Teil hiervon erledigen und die IDT anlegen.
Die IDT ist ähnlich wie die GDT und zeigt der CPU, an welchen Stellen im Speicher der Code steht, welcher ausgeführt werden soll, wenn Interrupts auftreten. Interrupts werden entweder von der CPU im Fehlerfall ausgelöst, von Hardware aufgerufen oder können auch von Software aufgerufen werden.
Bei x86 gibt es einige Interrupts, die von der CPU-Architektur spezifiziert und reserviert sind. Die übrigen können von Betriebssystem beliebig verwendet werden. Die genaue Spezifikation kann man sich im Handbuch von Intel ansehen (zum Erscheinen des Artikels Vol. 3A 6-1), hier nur eine kurze Liste:
- 0: Divide Error
- 1: Debug Exception
- 2: NMI Interrupt
- 3: Breakpoint
- 4: Overflow
- 5: Bound Range Exceeded
- 6: Invalid Opcode
- 7: Device not available (no math coprocessor)
- 8: Double Fault
- 9: Coprocessor Segment Overrun
- 10: Invalid TSS
- 11: Segment not present
- 12: Stack-Segment Fault
- 13: General Protection
- 14: Page Fault
- 15: Intel reserved
- 16: x87 FPU Floating-Point Error
- 17: Alignment Check
- 18: Machine Check
- 19: SIMD Floating Point Exception
- 20: Virtualization Exception
- 21 - 31: Intel reserved
- 32 - 255: user defined (können vom OS benutzt werden)
Wie bereits gesagt ist die IDT recht ähnlich zur GDT, ein einzelner Eintrag in der IDT sieht so aus:
Der Selektor verweist auf einen Eintrag in der GDT, die Basis (bestehend aus base_higher
und base_lower
) auf den Offset in diesem Speicherbereich. type_attributes
besteht aus vier Teilen:
- Bit 47: Present Bit
- Bit 46, 45: Descriptor Privilege Level
- Bit 44: Storage Segment
- Bit 43 - 40: Gate Type
Wie bei der GDT gibt es erneut eine Struktur, die auf die Tabelle selbst verweist und die wir der CPU bekanntmachen müssen:
Die gesamte Datei idt.h
sieht ziemlich analog zur gdt.h
aus:
Als nächstes müssen wir einen Interrupt-Handler programmieren, auf den wir unsere IDT zeigen lassen können. Für Testzwecke wollen wir erstmal nur einen Handler, der uns in allen Fällen eine Meldung ausgibt und den Kernel anhält.
section .text
global int_handler
int_handler:
mov ax, 0x10
mov gs, ax
mov dword [gs:0xB8000],') : '
hlt
Ähnlich wie bei der GDT den Verweis auf den Assembler-Code uzm Installieren der GDT, verweisen wir auch diesmal wieder vom C-Code aus auf Assembler. Bei der GDT hätte man auch Inline-Assembler verwenden können, aber bei da es mehrere Zeilen waren und ein Sprung ins neue Segment nötig war, bot sich externes Assembler an. Bei Interrupts ist externes Assembler (außer man will Sonderfunktionen nutzen) notwendig, da der C-Compiler sonst Stack-Logik miteinbaut, die nicht zum Interrupt-Handler gehören.
Wir legen also wie bei der GDT zunächst wieder eine Funktion an, die die Struktur richtig befüllt (und die Adresse in higher und lower-Teil aufspaltet):
Uns reicht zunächst mal ein Interrupt-Handler für alle Fälle, deshalb iterieren wir einfach und tragen an jeder Stelle in der IDT denselben Interrupt-Handler ein.
type_attributes = 0x8e
setzt das Present-Flag in unserem Interrupt, verlangt vom aufrufenden Code Privilege Level = 0 (unser aktueller Kernel-Code ist in Level 0, Userspace-Code landet üblicherweise auf Level 3) und legt den Eintrag als 32-Bit-Interrupt-Gate fest.
Zu Testzwecken wollen wir nun einen Interrupt auslösen. Hierzu bietet sich der Divide Error (Division by zero) an. Wenn wir keine Compilerflags ändern wollen, bewerkstelligen wir dies am einfachsten durch Embedded Assembler direkt nach dem Setzen der Interrupt-Table.
Und wir sehen:
Wer wirklich sichergehen will, dass wir den Interrupt 0 (Divide Error) erhalten, kann sich als Übung für jeden Interrupt einen eigenen Handler mit jeweils unterschiedlicher Ausgabe schreiben.
I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.