/* * 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 #include #include #include #include #include #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)); }