os/arch/i686/idt.c

166 lines
4.1 KiB
C

#include <kernel/tty.h>
#include <kernel/gdt.h>
#include <debugging.h>
#include "interrupt.h"
#include <stdint.h>
#include <stddef.h>
#define NUM_ENTRIES 0x100
struct InterruptDescriptor {
uint32_t offset;
uint32_t selector;
uint8_t gate_type;
uint8_t ring;
bool present;
};
struct IDT {
uint16_t size;
uint64_t* base;
} __attribute__((packed));
static uint64_t entries[NUM_ENTRIES];
struct IDT idt;
enum EntryTypes {
DivisionByZero = 0x00,
_Reserved0 = 0x01,
NMIInterrupt = 0x02,
Breakpoint = 0x03,
Overflow = 0x04,
BoundsExceeded = 0x05,
InvalidOpcode = 0x06,
DeviceNotAvail = 0x07,
DoubleFault = 0x08,
CoprocSegORun = 0x09,
InvalidTSS = 0x0A,
SegNotPresent = 0x0B,
StackSegFault = 0x0C,
GenerProtFault = 0x0D,
PageFault = 0x0E,
_Reserved1 = 0x0F,
x87FPUError = 0x10,
AlignmentCheck = 0x11,
MachineCheck = 0x12,
SIMDFloatExcep = 0x13,
};
static void encodeIdtEntry(uint8_t *target, struct InterruptDescriptor source)
{
// Encode the present bit
if (source.present) {
target[5] |= 0x80;
} else {
// Not present, return
return;
}
// Encode the offset
target[0] = (source.offset >> 0x00) & 0xFF;
target[1] = (source.offset >> 0x08) & 0xFF;
target[6] = (source.offset >> 0x10) & 0xFF;
target[7] = (source.offset >> 0x18) & 0xFF;
// Encode the base
target[2] = (source.selector >> 0x00) & 0xFF;
target[3] = (source.selector >> 0x08) & 0xFF;
// Encode the gate type
target[5] |= source.gate_type;
// Encode the DPL
target[5] |= (source.ring << 5);
}
static void add_entry(struct InterruptDescriptor entry, uint8_t index)
{
uint64_t descriptor = 0;
encodeIdtEntry((uint8_t*) &descriptor, entry);
entries[index] = descriptor;
}
static void load_idt()
{
idt.size = NUM_ENTRIES * 8 - 1;
idt.base = entries;
asm ( "lidt %0" : : "m"(idt) );
asm ( "sti" );
}
struct InterruptDescriptor generate_entry(bool present, void isr(struct interrupt_frame*), uint8_t gate_type, uint8_t ring, uint8_t selector)
{
struct InterruptDescriptor entry;
entry.present = present;
entry.offset = (uint32_t)isr;
entry.gate_type = gate_type;
entry.ring = ring;
entry.selector = selector;
return entry;
}
static inline void outb(uint16_t port, uint8_t val)
{
__asm__ volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory");
/* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint).
* Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint).
* The outb %al, %dx encoding is the only option for all other cases.
* %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */
}
void idt_init()
{
outb(0x21,0xfd);
outb(0xa1,0xff);
struct InterruptDescriptor entry;
entry = generate_entry(true, exception, 0xF, 0, 0x8);
for (int i = 0; i < 0x20; i++)
{
add_entry(entry, i);
}
entry = generate_entry(true, irq, 0xF, 0, 0x8);
for (int i = 0x20; i < 0x28; i++)
{
add_entry(entry, i);
}
entry = generate_entry(true, divide_by_zero, 0xF, 0, 0x8);
add_entry(entry, DivisionByZero);
entry = generate_entry(true, general_protection_fault, 0xF, 0, 0x8);
add_entry(entry, GenerProtFault);
entry = generate_entry(true, double_fault, 0xF, 0, 0x8);
add_entry(entry, DoubleFault);
struct InterruptDescriptor print_entry;
print_entry.present = true;
print_entry.offset = (uint32_t)print;
print_entry.gate_type = 0xE;
print_entry.ring = 3;
print_entry.selector = 0x8;
add_entry(print_entry, 0x80);
struct InterruptDescriptor input_entry;
input_entry.present = true;
input_entry.offset = (uint32_t)input;
input_entry.gate_type = 0xE;
input_entry.ring = 0;
input_entry.selector = 0x8;
add_entry(input_entry, 0x81);
load_idt();
tss.ss0 = 0x10;
asm("mov %%esp, %0" : "=r" (tss.esp0));
asm("mov $0x28, %ax");
asm("ltr %ax");
}