I have a problem by setting up the IDT.
In fact every time I get an interrupt everything do just fine until the end of my handling routine.
When I call "iret", I get everytime an GPF. I loked for hours through the code, but I didn't found the problem.
"descriptor_tables.h"
Code:
#ifndef _DESCRIPTOR_TABLES_H_
#define _DESCRIPTOR_TABLES_H_
struct cpu_state
{
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
uint32_t interrupt_number;
uint32_t error;
uint32_t eip;
uint32_t cs;
uint32_t eflags;
uint32_t esp;
uint32_t ss;
};
void init_gdt(void);
void init_idt(void);
void handle_interrupt(struct cpu_state *cpu);
#endif
gdt.c
Code:
#include "descriptor_tables.h"
#define GDT_FLAG_DATASEG 0x02
#define GDT_FLAG_CODESEG 0x0a
#define GDT_FLAG_TSS 0x09
#define GDT_FLAG_SEGMENT 0x10
#define GDT_FLAG_RING0 0x00
#define GDT_FLAG_RING3 0x60
#define GDT_FLAG_PRESENT 0x80
#define GDT_FLAG_4K 0x800
#define GDT_FLAG_32_BIT 0x400
#define GDT_ENTRIES 5
static uint64_t gdt[GDT_ENTRIES];
static void gdt_set_entry(uint16_t i, uint32_t base, uint32_t limit, uint16_t flags);
static void load_gdt(void);
void init_gdt(void)
{
gdt_set_entry(0, 0, 0, 0);
gdt_set_entry(1, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_CODESEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING0);
gdt_set_entry(2, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_DATASEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING0);
gdt_set_entry(3, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_CODESEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
gdt_set_entry(4, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_DATASEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
load_gdt();
}
static void gdt_set_entry(uint16_t i, uint32_t base, uint32_t limit, uint16_t flags)
{
if(i>=GDT_ENTRIES)
return;
uint64_t *gdt_cp;
gdt_cp = (gdt + i);
*gdt_cp = limit & 0xffffLL;
*gdt_cp |= (base & 0xffffffLL) << 16;
*gdt_cp |= (flags & 0xffLL) << 40;
*gdt_cp |= ((limit & 0xf0000LL) >> 16) << 48;
*gdt_cp |= ((flags & 0xf00LL) >> 8) << 52;
*gdt_cp |= ((base & 0xff000000LL) >> 24) << 56;
}
static void load_gdt(void)
{
struct
{
uint16_t limit;
void *pointer;
}
__attribute__((packed)) gdtp =
{
.limit = GDT_ENTRIES * 8 - 1,
.pointer = gdt,
};
// GDT neu laden
asm volatile("lgdt %0" : : "m" (gdtp));
// Segmentregister neu laden, damit die neuen GDT-Eintraege auch wirklich
// benutzt werden
asm volatile(
"mov $0x10, %ax;"
"mov %ax, %ds;"
"mov %ax, %es;"
"mov %ax, %ss;"
"ljmp $0x8, $.1;"
".1:"
);
}
idt.c
Code:
#include "descriptor_tables.h"
extern void intr_stub_0(void);
extern void intr_stub_1(void);
extern void intr_stub_2(void);
extern void intr_stub_3(void);
extern void intr_stub_4(void);
extern void intr_stub_5(void);
extern void intr_stub_6(void);
extern void intr_stub_7(void);
extern void intr_stub_8(void);
extern void intr_stub_9(void);
extern void intr_stub_10(void);
extern void intr_stub_11(void);
extern void intr_stub_12(void);
extern void intr_stub_13(void);
extern void intr_stub_14(void);
extern void intr_stub_15(void);
extern void intr_stub_16(void);
extern void intr_stub_17(void);
extern void intr_stub_18(void);
extern void intr_stub_32(void);
extern void intr_stub_33(void);
extern void intr_stub_48(void);
#define IDT_FLAG_PRESENT 0x80
#define IDT_FLAG_RING0 0x00
#define IDT_FLAG_RING3 0x60
#define IDT_FLAG_32BIT 0x8
#define IDT_FLAG_INTERRUPT_GATE 0x6
#define IDT_FLAG_TRAP_GATE 0x7
#define IDT_FLAG_TASK_GATE 0x5
#define IDT_SELECTOR_RING0 0x0
#define IDT_SELECTOR_RING3 0x3
#define IDT_SELECTOR_GDT 0x0
#define IDT_SELECTOR_LDT 0x4
#define IDT_ENTRIES 256
static uint64_t idt[IDT_ENTRIES];
#define PIC_IRQ0 0x20
static uint16_t build_selector(uint8_t flags, uint16_t table_entry);
static void idt_set_entry(uint16_t i, void (*function)(), uint8_t flags, uint16_t selector);
static void load_idt(void);
static inline void outb(uint16_t port, uint8_t data);
static void init_pic(void);
static void pic_send_eoi(uint8_t irq);
void init_idt(void)
{
init_pic();
idt_set_entry(0, intr_stub_0, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(1, intr_stub_1, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(2, intr_stub_2, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(3, intr_stub_3, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(4, intr_stub_4, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(5, intr_stub_5, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(6, intr_stub_6, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(7, intr_stub_7, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(8, intr_stub_8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(9, intr_stub_9, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(10, intr_stub_10, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(11, intr_stub_11, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(12, intr_stub_12, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(13, intr_stub_13, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(14, intr_stub_14, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(15, intr_stub_15, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(16, intr_stub_16, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(17, intr_stub_17, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(18, intr_stub_18, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(32, intr_stub_32, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(33, intr_stub_33, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
idt_set_entry(48, intr_stub_48, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING3 | IDT_FLAG_PRESENT, build_selector(IDT_SELECTOR_RING0 | IDT_SELECTOR_GDT, 1));
load_idt();
asm volatile("sti");
}
void handle_interrupt(struct cpu_state *cpu)
{
if(!isBeginOfLine())
kprintf("\n\r");
if (cpu->interrupt_number <= 0x1f)
{
kprintf("exception %d, kernel stopped!\n\r", cpu->interrupt_number);
// TODO Care about the exception
while(1)
{
asm volatile("cli; hlt");
// Prozessor anhalten
}
}
else
if (cpu->interrupt_number >= PIC_IRQ0 && cpu->interrupt_number <= PIC_IRQ0 + 0xf)
{
kprintf("IRQ %d.\n\r", cpu->interrupt_number - PIC_IRQ0);
pic_send_eoi(cpu->interrupt_number - PIC_IRQ0);
}
else
{
kprintf("Unbekannter Interrupt oder Syscall. Nummer: %d\n\rKernel angehalten!\n\r", cpu->interrupt_number);
while(1)
{
// Prozessor anhalten
asm volatile("cli; hlt");
}
}
}
static uint16_t build_selector(uint8_t flags, uint16_t table_entry)
{
uint16_t selector;
selector = (flags & 0x7LL);
selector |= ((table_entry & 0x1fffLL) << 3);
return selector;
}
static void idt_set_entry(uint16_t i, void (*function)(void), uint8_t flags, uint16_t selector)
{
if (i>=IDT_ENTRIES)
return;
uint32_t handler = (uint32_t) function;
uint64_t *idt_cp;
idt_cp = (idt + i);
*idt_cp = (handler & 0xffffLL);
*idt_cp |= ((selector & 0xffffLL) << 16);
*idt_cp |= ((uint64_t)(flags & 0xffLL) << 40);
*idt_cp |= (((uint64_t)(handler & 0xffff0000LL) >> 16) << 48);
}
static void load_idt(void)
{
struct
{
uint16_t limit;
void *pointer;
}
__attribute__((packed)) idtp =
{
.limit = IDT_ENTRIES * 8 - 1,
.pointer = idt,
};
asm volatile("lidt %0" : : "m" (idtp));
}
static inline void outb(uint16_t port, uint8_t data)
{
asm volatile ("outb %0, %1" : : "a" (data), "Nd" (port));
}
#define PIC_MASTER_COMMAND 0x20
#define PIC_MASTER_DATA 0x21
#define PIC_MASTER_IMR 0x21
#define PIC_SLAVE_COMMAND 0xA0
#define PIC_SLAVE_DATA 0xA1
#define PIC_SLAVE_IMR 0xA1
#define END_OF_INTERRUPT 0x20
static void init_pic(void)
{
outb(PIC_MASTER_COMMAND, 0x11);
outb(PIC_SLAVE_COMMAND, 0x11);
outb(PIC_MASTER_DATA, PIC_IRQ0);
outb(PIC_SLAVE_DATA, PIC_IRQ0 + 0x8);
outb(PIC_MASTER_DATA, 0x04);
outb(PIC_SLAVE_DATA, 0x02);
outb(PIC_MASTER_DATA, 0x01);
outb(PIC_SLAVE_DATA, 0x01);
outb(PIC_MASTER_IMR, 0x0);
outb(PIC_SLAVE_IMR, 0x0);
}
static void pic_send_eoi(uint8_t irq)
{
if (irq >= 0x8)
{
outb(PIC_SLAVE_COMMAND, END_OF_INTERRUPT);
}
outb(PIC_MASTER_COMMAND, END_OF_INTERRUPT);
}
int_stub.S
Code:
.macro intr_stub nr
.global intr_stub_\nr
intr_stub_\nr:
pushl $0
pushl $\nr
jmp intr_common_handler
.endm
.macro intr_stub_error_code nr
.global intr_stub_\nr
intr_stub_\nr:
pushl $\nr
jmp intr_common_handler
.endm
// Exceptions
intr_stub 0
intr_stub 1
intr_stub 2
intr_stub 3
intr_stub 4
intr_stub 5
intr_stub 6
intr_stub 7
intr_stub_error_code 8
intr_stub 9
intr_stub_error_code 10
intr_stub_error_code 11
intr_stub_error_code 12
intr_stub_error_code 13
intr_stub_error_code 14
intr_stub 15
intr_stub 16
intr_stub_error_code 17
intr_stub 18
// IRQs
intr_stub 32
intr_stub 33
// etc. until 47
// Syscall
intr_stub 48
.extern handle_interrupt
intr_common_handler:
push %ebp
push %edi
push %esi
push %edx
push %ecx
push %ebx
push %eax
push %esp
call handle_interrupt
add $4, %esp
pop %eax
pop %ebx
pop %ecx
pop %edx
pop %esi
pop %edi
pop %ebp
add $8, %esp
iret