972
edits
[unchecked revision] | [unchecked revision] |
(Fix "memory" constraint.) |
m (Bot: Replace deprecated source tag with syntaxhighlight) |
||
(9 intermediate revisions by 5 users not shown) | |||
Line 14:
This is the structure of a single (32-bit) IDT entry:
<
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>
and this is the structure of a single (64-bit) IDT entry:
<
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>
===Table===
To create an IDT, simply create a 256-entry array of descriptors:
<
__attribute__((aligned(0x10)))
static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance
</syntaxhighlight>
You will also need a special IDTR structure, which looks like:
<
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
</syntaxhighlight>
for a 32-bit IDT, or like:
<
typedef struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) idtr_t;
</syntaxhighlight>
for a 64-bit IDT.
Don't forget to define an IDTR:
<
static idtr_t idtr;
</syntaxhighlight>
===ISRs===
In a C source file, define a general exception handler:
<
__attribute__((noreturn))
void exception_handler(void);
Line 70:
__asm__ volatile ("cli; hlt"); // Completely hangs the computer
}
</syntaxhighlight>
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:
<
%macro isr_err_stub 1
isr_stub_%+%1:
Line 86:
iret
%endmacro
</syntaxhighlight>
Then, use these macros to define your 32 exception handlers:
<
extern exception_handler
isr_no_err_stub 0
Line 122:
isr_err_stub 30
isr_no_err_stub 31
</syntaxhighlight>
Finally, in assembly, define a "stub table". (This is used to prevent excessive code reuse, and not related to actual function.)
Using NASM macros:
<
global isr_stub_table
isr_stub_table:
Line 134:
%assign i i+1
%endrep
</syntaxhighlight>
===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:
<
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>
for a 32-bit IDT, or like:
<
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags);
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags) {
descriptor->
descriptor->
descriptor->ist = 0;
descriptor->attributes = flags;
descriptor->
descriptor->
descriptor->
}
</syntaxhighlight>
for a 64-bit IDT.
Finally, to set the entries at last, this is what the function would look like:
<
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(
for (uint8_t vector = 0; vector < 32; vector++) {
idt_set_descriptor(vector, isr_stub_table[vector], 0x8E
vectors[vector] = true;
}
Line 189 ⟶ 191:
__asm__ volatile ("sti"); // set the interrupt flag
}
</syntaxhighlight>
(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/
[[Category:Interrupts]]
|