Interrupts Tutorial: Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (moved Interrupts tutorial to Interrupts Tutorial: more appropriate)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(10 intermediate revisions by 6 users not shown)
Line 14:
 
This is the structure of a single (32-bit) IDT entry:
<sourcesyntaxhighlight lang="c">
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
Line 22:
uint16_t isr_high; // The higher 16 bits of the ISR's address
} __attribute__((packed)) idt_entry_t;
</syntaxhighlight>
</source>
and this is the structure of a single (64-bit) IDT entry:
<sourcesyntaxhighlight lang="c">
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
Line 34:
uint32_t reserved; // Set to zero
} __attribute__((packed)) idt_entry_t;
</syntaxhighlight>
</source>
===Table===
To create an IDT, simply create a 256-entry array of descriptors:
<sourcesyntaxhighlight lang="c">
__attribute__((aligned(0x10)))
static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance
</syntaxhighlight>
</source>
You will also need a special IDTR structure, which looks like:
<sourcesyntaxhighlight lang="c">
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
</syntaxhighlight>
</source>
for a 32-bit IDT, or like:
<sourcesyntaxhighlight lang="c">
typedef struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) idtr_t;
</syntaxhighlight>
</source>
for a 64-bit IDT.
 
Don't forget to define an IDTR:
<sourcesyntaxhighlight lang="c">
static idtr_t idtr;
</syntaxhighlight>
</source>
 
===ISRs===
In a C source file, define a general exception handler:
<sourcesyntaxhighlight lang="c">
__attribute__((noreturn))
void exception_handler(void);
Line 70:
__asm__ volatile ("cli; hlt"); // Completely hangs the computer
}
</syntaxhighlight>
</source>
This will act as your main exception handler. When you receive a CPU exception, this is the handler you will call.
 
Now, in an assembly file ('''nasm''' '''assembler''' specifically), define these two macros:
<sourcesyntaxhighlight lang="asm">
%macro isr_err_stub 1
isr_stub_%+%1:
Line 86:
iret
%endmacro
</syntaxhighlight>
</source>
Then, use these macros to define your 32 exception handlers:
<sourcesyntaxhighlight lang="asm">
extern exception_handler
isr_no_err_stub 0
Line 122:
isr_err_stub 30
isr_no_err_stub 31
</syntaxhighlight>
</source>
Finally, in assembly, define a "stub table". (This is used to prevent excessive code reuse, and not related to actual function.)
 
Using NASM macros:
<sourcesyntaxhighlight lang="asm">
global isr_stub_table
isr_stub_table:
Line 134:
%assign i i+1
%endrep
</syntaxhighlight>
</source>
===Assembling===
Finally, we can assemble the IDT:
Line 143:
 
To define the entries, it is appropriate to make use of a helper function. That helper function would look like:
<sourcesyntaxhighlight lang="c">
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags);
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags) {
Line 154:
descriptor->reserved = 0;
}
</syntaxhighlight>
</source>
for a 32-bit IDT, or like:
<sourcesyntaxhighlight lang="c">
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags);
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags) {
idt_desc_tidt_entry_t* descriptor = &idt[vector];
 
descriptor->base_lowisr_low = (uint64_t)isr & 0xFFFF;
descriptor->cs kernel_cs = GDT_OFFSET_KERNEL_CODE;
descriptor->ist = 0;
descriptor->attributes = flags;
descriptor->base_midisr_mid = ((uint64_t)isr >> 16) & 0xFFFF;
descriptor->base_highisr_high = ((uint64_t)isr >> 32) & 0xFFFFFFFF;
descriptor->rsv0 reserved = 0;
}
</syntaxhighlight>
</source>
for a 64-bit IDT.
 
Finally, to set the entries at last, this is what the function would look like:
<sourcesyntaxhighlight lang="c">
static bool vectors[IDT_MAX_DESCRIPTORS];
 
extern void* isr_stub_table[];
 
Line 179 ⟶ 181:
void idt_init() {
idtr.base = (uintptr_t)&idt[0];
idtr.limit = (uint16_t)sizeof(idt_desc_tidt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
 
for (uint8_t vector = 0; vector < 32; vector++) {
idt_set_descriptor(vector, isr_stub_table[vector], 0x8E, 1);
vectors[vector] = true;
}
 
__asm__ volatile ("lidt %0" : : "memorym"(idtr)); // load the new IDT
__asm__ volatile ("sti"); // set the interrupt flag
}
</syntaxhighlight>
</source>
(IDT_MAX_DESCRIPTORS being the number of entries in your IDT - or the last entry's index + 1)
 
Congratulations! You have successfully defined your IDT, loaded it, and enabled interrupts.
 
Line 206 ⟶ 210:
* [https://forum.osdev.org/viewtopic.php?f=1&t=33160&p=285871#p285871 Interrupts don't work in GRUB]
===References===
* [https://github.com/rizetaustanss/skylight/blob/trunk/glasskernel/src/cpu/interrupts/idt.c The code this tutorial is based off of]
 
[[Category:Interrupts]]