Nucleus
Barry Global Descriptor Tables b9a1b73 (3 years, 3 months ago)
/*
* This file deals with the Global Descriptor Table. It creates a simple GDT,
* suitable for use in the kernel, and attaches a TSS to the end. The TSS,
* which can be used to switch from ring 3 to ring 0, allows processes to access
* all IO ports. Every logical processor requires a unique TSS, and each one is
* also given a unique GDT, to save space, since the number of logical
* processors is not known until load time.
*/
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <nucleus/memory.h>
#include <nucleus/panic.h>
#include <nucleus/cpu.h>
#include "desc.h"
/* Structure for a GDT Entry */
static struct GDTEntry {
uint16_t limitLower, baseLower;
uint8_t baseMiddle, access, gran, baseHigher;
} __attribute__((packed)) *GDT[MAX_CPUS]; /* Per CPU */
/* Structure for a TSS Entry */
static struct TSSEntry {
uint32_t prevTSS;
uint32_t esp0, ss0;
uint32_t esp1, ss1;
uint32_t esp2, ss2;
uint32_t cr3, eip, eflags;
uint32_t eax, ecx, edx, ebx;
uint32_t esp, ebp, esi, edi;
uint32_t es, cs, ss, ds, fs, gs;
uint32_t ldt;
uint16_t trap, iomapBase;
} __attribute__((packed)) *TSS[MAX_CPUS]; /* Per CPU */
/* Set a gate of the GDT */
static void
gdt_set_gate(uint8_t num, uint32_t base, uint32_t limit, uint8_t access,
uint8_t gran)
{
GDT[CPUID][num].baseLower = (base & 0xFFFF);
GDT[CPUID][num].baseMiddle = (base >> 16) & 0xFF;
GDT[CPUID][num].baseHigher = (base >> 24) & 0xFF;
GDT[CPUID][num].limitLower = (limit & 0xFFFF);
GDT[CPUID][num].gran = (limit >> 16) & 0x0F;
GDT[CPUID][num].gran |= gran & 0xF0;
GDT[CPUID][num].access = access;
}
/* Initialise the GDT */
void
init_gdt(void)
{
}
/* Load the GDT */
void
cpu_load_gdt(void)
{
/*
* Each page can fit multiple GDT/TSS structures. If there is space in
* an existing page, the new structures should be allocated in there.
* Otherwise, a new page frame should be allocated.
*/
size_t size = (sizeof(struct GDTEntry) * 6) + sizeof(struct TSSEntry);
off_t idx = CPUID % (PAGE_SIZE / size);
off_t block = (CPUID / (PAGE_SIZE / size)) * (PAGE_SIZE / size);
if (idx == 0)
GDT[CPUID] = (void *) alloc_frame();
else
GDT[CPUID] = (void *) GDT[block] + (size * idx);
memset(GDT[CPUID], 0, size);
gdt_set_gate(0, 0x00000000, 0x00000000, 0x00, 0x00); /* Null */
/* Ring 0 */
gdt_set_gate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF); /* Code */
gdt_set_gate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF); /* Data */
/* Ring 3 */
gdt_set_gate(3, 0x00000000, 0xFFFFFFFF, 0xFA, 0xCF); /* Code */
gdt_set_gate(4, 0x00000000, 0xFFFFFFFF, 0xF2, 0xCF); /* Data */
uint32_t addr = (uint32_t) GDT[CPUID] + (sizeof(struct GDTEntry) * 6);
gdt_set_gate(5, addr, sizeof(struct TSSEntry) - 1, 0xE9, 0);
TSS[CPUID] = (void *) addr;
TSS[CPUID]->ss0 = 0x10;
TSS[CPUID]->esp0 = 0xF0800000 - sizeof(uintptr_t);
TSS[CPUID]->cs = 0x08 | 0;
TSS[CPUID]->ds = TSS[CPUID]->es = TSS[CPUID]->ss = 0x10 | 0;
TSS[CPUID]->fs = TSS[CPUID]->gs = 0x10 | 3;
TSS[CPUID]->iomapBase = sizeof(struct TSSEntry);
struct DescRecord ptr = {
.limit = sizeof(struct GDTEntry) * 6,
.base = (uintptr_t) GDT[CPUID],
};
asm volatile("lgdt %0" :: "m" (ptr));
asm volatile("ltr %w0" :: "q" (0x28 | 3));
}