IDT - Waaal/BobaOS GitHub Wiki

Interrupt Descriptor Table

IDT is a struct which lives in memory and is used by the CPU to find interrupt and exception handlers. A interrupt can be a signal send from a device, a Exception generatet by the CPU to alert the Kernel or send from a Userspace programm to communicate with the kernel. If the CPU accepts a interrupt, it will stop the current task and process the interrupt. A IDT can have up to 256 entries.

Explenation

There are 3 different types of interrupts:

  • Exception: Generated by the CPU to alert kernel.
  • Interrupt Request (IRQ) (Hardware interrupt): Generatet by a external chipset.
  • Software Interrupts: Signaled by software running on CPU. Mostly done by System Calls with INT instruction.

For hardware interrupts please see HardwareInterrupts

Different interrupts and exceptions have Numbers which are called interrupt vectors. With a IDT a CPU can look up a interrupt number and find the handler for this specific event.

The vector number is define by the order in the IDT. So Entry 0 has the vector number 0. Entry 5 has the vector number 5 and so on.

Vectore 0 - 31 are pre defined by the CPU for CPU exceptions. Numbers from 32 - 255 can be used by the operating system.

IDT Entry:

On entry is 128 bits in long mode and 64 bits in protected mode and is structured like this:

 <---------LONG MODE---------> 
 127         96 95          64 63      48 47           40 39  34 34    32 31           16 15           0
  |   //////   |    Offset    |  Offset  |  Attributes   | //// |   IST  |    Selector   |    Offset   |

Offset: 64 bit value (protected mode 32 bit). Is the address of the entry point of the Interrupt service routine.

Selector: A Segment Selector (Points to a valid code entry in the GDT). Selector this interrupt is bound to. For example, if the selector is the Kernel code then when the IDT jumps to the interrupt routine it populates the CS segment with this selector

IST: Offset in the interrupt stack table. If bits are all zero IST is not beeing used.

Attributes:

 7   6   5   4   3   2   1   0
|P |  DPL |  0|   Gate Type   |

P: Present bit. Must be 1 for this entry to be valid. If Value is 0 means this interrupt is not/cannot be used

DPL: A priviledge level, which are allowed to access this interrupt via INT instruction. Hardware interrupts ignore this.

Gate Type: Describe the type of gate, this interrupt descriptor represents.

  • 0x5 (0b0101) = Task gate (protected mode only).
  • 0xE (0b1110) = 64/32 bit Interrupt Gate
  • 0xb (0b1111) = 64/32 bit Trap Gate

Task gate (obsolete)

Task gate references the TSS descriptor and can assist in multi-tasking when exceptions occure

Interrupt Gate

Used for interrupts that can be invoked with the INT instruction or hardware events from a IRQ. (They also automatically disable interrupts on entry and re-enable them on iret instruction)

Trap Gate

Used for exceptions raised by the CPU. (The CPU places error codes on the stack for some exceptions)

Note: Interrupt and Trap gate are basically the same the only differnece is that a Interrupt Gate automatically disables interrupts and re-enable them

Load a IDT

To load a Interrupt Descriptor Table we need a pointer to this table. The pointer needs to have the following structure:

32-Bit:

31          16 15       0
|   Offset    |  Size   |

64-Bit:

63          16 15       0
|   Offset    |  Size   |

Size: Size of the IDT - 1;

Offset: Staring address of the IDT.

Load the IDT with the lidt instruction. The argument of the lidt is

lidt [AddressOfPointer]

CPU Exceptions Error codes

The Vectors 0-31 are predefined by the CPU for exceptions. For some of these Exceptions the CPU places a error code on the stack that need to be popped from the stack before the iret instruction.

Vectors that place a error code are:

  • Vector 8 (Double fault)
  • Vector 10 (Invalid TSS)
  • Vector 11 (Segment Not Present)
  • Vector 12 (Stack segment fault)
  • Vector 13 (General protection fault)
  • Vector 14 (Page Fault)
  • Vector 17 (Alignment Check)
  • Vector 21 (Control Protection Exception)
  • Vector 29 (VMM Communication Exception)
  • Vector 30 (Security Exception)

Implementation

The IDT needs to be implemented in assembly and C code. We need to save all the registers before enetering the interrupt service routine.

Assembly wrapper (32 bit)

int21h:
    pushad                      ; Pushad pushes all general purpos registers onto stack
    call int21h_handler_c       ; Call c handler
    popad                       ; restores all generap purpose registers onto stack
    iret                        ; iret return back

But with this method we needed to create a wrapper for each interrupt we want to do. So it is useful to us NASM macros to create a single assembly wrapper for each interrupt service routine.

C Code (32 bit)

//Define structs
struct idt_entry
{
    uint16_t offset_1;  // Offset bits 0 - 15
    uint16_t selector; // Segment selector
    uint8_t zero; // Reserved and IST. we dont use interrupt stack table, so always zero
    uint8_t attributes;
    uint16_t offset_2; // Offset bits 16 - 31
} __attribute__((packed));

struct idt_ptr
{
    uint16_t limit; //Size -1 
    uint32_t base; //Address of start of IDT
} __attribute__((packed));


//Set a idt entry
void idt_set(int vector, void* address)
{
    struct idt_entry* entry = &idt_entry_array[vector];

    entry->offset_1 = (uint32_t)address & 0x0000FFFF;
    entry->offset_2 = (uint32_t)address >> 16; 
    entry->selector = 0x8;
    entry->zero = 0x0;
    entry->attributes = 0xEE; //P = 1, DPL = 3, Gate Type = Interrupt gate
}
⚠️ **GitHub.com Fallback** ⚠️