71 lines
2.8 KiB
C
71 lines
2.8 KiB
C
//
|
|
// descriptor_tables.c - Initialises the GDT and IDT, and defines the
|
|
// default ISR and IRQ handler.
|
|
// Based on code from Bran's kernel development tutorials.
|
|
// Rewritten for JamesM's kernel development tutorials.
|
|
//
|
|
|
|
#include "gdt.h"
|
|
#include "../libc/memory.h"
|
|
#include <stdint.h>
|
|
|
|
static void gdt_set_gate(int32_t num,uint32_t base,uint32_t limit,uint8_t access,uint8_t gran);
|
|
static void write_tss(int32_t num, uint16_t ss0, uint32_t esp0);
|
|
|
|
gdt_entry_t gdt_entries[6];
|
|
gdt_ptr_t gdt_ptr;
|
|
tss_entry_t tss_entry;
|
|
|
|
void init_gdt() {
|
|
gdt_ptr.limit=(sizeof(gdt_entry_t)*6)-1;
|
|
gdt_ptr.base =(uint32_t)&gdt_entries;
|
|
|
|
gdt_set_gate(0, 0, 0, 0, 0); // Null segment
|
|
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
|
|
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
|
|
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
|
|
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment
|
|
write_tss(5, 0x10, 0x80000);
|
|
asm volatile("lgdt (%%eax)"::"a"((uint32_t)&gdt_ptr));
|
|
asm volatile("mov $0x2B, %ax; \
|
|
ltr %ax; \
|
|
");
|
|
}
|
|
|
|
static void gdt_set_gate(int32_t num,uint32_t base,uint32_t limit,uint8_t access,uint8_t gran) {
|
|
gdt_entries[num].base_low=(base&0xFFFF);
|
|
gdt_entries[num].base_middle=(base>>16)&0xFF;
|
|
gdt_entries[num].base_high=(base>>24)&0xFF;
|
|
gdt_entries[num].limit_low =(limit&0xFFFF);
|
|
gdt_entries[num].granularity=(limit>>16)&0x0F;
|
|
gdt_entries[num].granularity|=gran & 0xF0;
|
|
gdt_entries[num].access=access;
|
|
}
|
|
|
|
static void write_tss(int32_t num, uint16_t ss0, uint32_t esp0) {
|
|
// Firstly, let's compute the base and limit of our entry into the GDT.
|
|
uint32_t base = (uint32_t) &tss_entry;
|
|
uint32_t limit = base + sizeof(tss_entry);
|
|
|
|
// Now, add our TSS descriptor's address to the GDT.
|
|
gdt_set_gate(num, base, limit, 0xE9, 0x00);
|
|
|
|
// Ensure the descriptor is initially zero.
|
|
memory_set((char*)&tss_entry, 0, sizeof(tss_entry));
|
|
tss_entry.ss0 = ss0; // Set the kernel stack segment.
|
|
tss_entry.esp0 = esp0; // Set the kernel stack pointer.
|
|
|
|
// Here we set the cs, ss, ds, es, fs and gs entries in the TSS. These specify what
|
|
// segments should be loaded when the processor switches to kernel mode. Therefore
|
|
// they are just our normal kernel code/data segments - 0x08 and 0x10 respectively,
|
|
// but with the last two bits set, making 0x0b and 0x13. The setting of these bits
|
|
// sets the RPL (requested privilege level) to 3, meaning that this TSS can be used
|
|
// to switch to kernel mode from ring 3.
|
|
tss_entry.cs = 0x0b;
|
|
tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13;
|
|
}
|
|
|
|
void set_kernel_stack(uint32_t stack) {
|
|
tss_entry.esp0 = stack;
|
|
}
|