os/drivers/gdt.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;
}