Interrupts Tutorial
Posted rtoax
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Interrupts Tutorial相关的知识,希望对你有一定的参考价值。
目录
Interrupts Tutorial - OSDev Wiki
Every operating system that needs to work with the hardware (efficiently) must make use of interrupts. For example, you could use the entirety of an AP to poll the mouse, or you could use the mouse IRQs instead and save much more CPU time, and a lot of electrical load. Therefore, every reasonable operating system makes use of interrupts.
Pre-requisites
Before you create an IDT, you need to create a GDT, load it properly, and configure the segment registers accordingly.
Interrupt Descriptor Table
Entries
In order to make use of interrupts, you need an IDT.
When an interrupt is fired, the CPU uses the vector as an index into the IDT. The CPU reads the entry of the IDT in order to figure out what to do prior to calling the ISR, and what the address of the handler is.
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 uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR uint8_t reserved; // Set to zero uint8_t attributes; // Type and attributes; see the IDT page uint16_t isr_high; // The higher 16 bits of the ISR's address __attribute__((packed)) idt_entry_t;
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 uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR uint8_t ist; // The IST in the TSS that the CPU will load into RSP; set to zero for now uint8_t attributes; // Type and attributes; see the IDT page uint16_t isr_mid; // The higher 16 bits of the lower 32 bits of the ISR's address uint32_t isr_high; // The higher 32 bits of the ISR's address uint32_t reserved; // Set to zero __attribute__((packed)) idt_entry_t;
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
You will also need a special IDTR structure, which looks like:
typedef struct uint16_t limit; uint32_t base; __attribute__((packed)) idtr_t;
for a 32-bit IDT, or like:
typedef struct uint16_t limit; uint64_t base; __attribute__((packed)) idtr_t;
for a 64-bit IDT.
Don't forget to define an IDTR:
static idtr_t idtr;
ISRs
In a C source file, define a general exception handler:
__attribute__((noreturn)) void exception_handler(void); void exception_handler() __asm__ volatile ("cli; hlt"); // Completely hangs the computer
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: call exception_handler iret %endmacro ; if writing for 64-bit, use iretq instead %macro isr_no_err_stub 1 isr_stub_%+%1: call exception_handler iret %endmacro
Then, use these macros to define your 32 exception handlers:
extern exception_handler isr_no_err_stub 0 isr_no_err_stub 1 isr_no_err_stub 2 isr_no_err_stub 3 isr_no_err_stub 4 isr_no_err_stub 5 isr_no_err_stub 6 isr_no_err_stub 7 isr_err_stub 8 isr_no_err_stub 9 isr_err_stub 10 isr_err_stub 11 isr_err_stub 12 isr_err_stub 13 isr_err_stub 14 isr_no_err_stub 15 isr_no_err_stub 16 isr_err_stub 17 isr_no_err_stub 18 isr_no_err_stub 19 isr_no_err_stub 20 isr_no_err_stub 21 isr_no_err_stub 22 isr_no_err_stub 23 isr_no_err_stub 24 isr_no_err_stub 25 isr_no_err_stub 26 isr_no_err_stub 27 isr_no_err_stub 28 isr_no_err_stub 29 isr_err_stub 30 isr_no_err_stub 31
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: %assign i 0 %rep 32 dd isr_stub_%+i ; use DQ instead if targeting 64-bit %assign i i+1 %endrep
Assembling
Finally, we can assemble the IDT: We need to
- 1. Assign the IDT entries with the correct values,
- 2. Reload the IDTR register,
- 3. Set the interrupt flag.
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) idt_entry_t* descriptor = &idt[vector]; descriptor->isr_low = (uint32_t)isr & 0xFFFF; descriptor->kernel_cs = 0x08; // this value can be whatever offset your kernel code selector is in your GDT descriptor->attributes = flags; descriptor->isr_high = (uint32_t)isr >> 16; descriptor->reserved = 0;
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) idt_desc_t* descriptor = &idt[vector]; descriptor->base_low = (uint64_t)isr & 0xFFFF; descriptor->cs = GDT_OFFSET_KERNEL_CODE; descriptor->ist = 0; descriptor->attributes = flags; descriptor->base_mid = ((uint64_t)isr >> 16) & 0xFFFF; descriptor->base_high = ((uint64_t)isr >> 32) & 0xFFFFFFFF; descriptor->rsv0 = 0;
for a 64-bit IDT.
Finally, to set the entries at last, this is what the function would look like:
extern void* isr_stub_table[]; void idt_init(void); void idt_init() idtr.base = (uintptr_t)&idt[0]; idtr.limit = (uint16_t)sizeof(idt_desc_t) * IDT_MAX_DESCRIPTORS - 1; for (uint8_t vector = 0; vector < 32; vector++) idt_set_descriptor(vector, isr_stub_table[vector], 0x8E); vectors[vector] = true; __asm__ volatile ("lidt %0" : : "m"(idtr)); // load the new IDT __asm__ volatile ("sti"); // set the interrupt flag
Congratulations! You have successfully defined your IDT, loaded it, and enabled interrupts.
What to do next
After completing this tutorial, there is still much left for you to do to fully harness the power of interrupts.
You can:
- Initialize the PIC
- Make use of PIC IRQs
- Understand the NMI
- Configure the local APIC
- Write a driver for the IOAPIC
- Make use of Message Signaled Interrupts
See also
Threads
References
以上是关于Interrupts Tutorial的主要内容,如果未能解决你的问题,请参考以下文章