#include #include #include #include #include /* * * Task State Segment: * * The TSS may reside anywhere in memory. A special segment register called * the Task Register (TR) holds a segment selector that points a valid TSS * segment descriptor which resides in the GDT. Therefore, to use a TSS * the following must be done in function gdt_init: * - create a TSS descriptor entry in GDT * - add enough information to the TSS in memory as needed * - load the TR register with a segment selector for that segment * * There are several fileds in TSS for specifying the new stack pointer when a * privilege level change happens. But only the fields SS0 and ESP0 are useful * in our os kernel. * * The field SS0 contains the stack segment selector for CPL = 0, and the ESP0 * contains the new ESP value for CPL = 0. When an interrupt happens in protected * mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value * into SS and ESP respectively. * */ static struct taskstate ts = {0}; /* * * Global Descriptor Table: * * The kernel and user segments are identical (except for the DPL). To load * the %ss register, the CPL must equal the DPL. Thus, we must duplicate the * segments for the user and the kernel. Defined as follows: * - 0x0 : unused (always faults -- for trapping NULL far pointers) * - 0x8 : kernel code segment * - 0x10: kernel data segment * - 0x18: user code segment * - 0x20: user data segment * - 0x28: defined for tss, initialized in gdt_init * */ static struct segdesc gdt[] = { SEG_NULL, [SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL), [SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL), [SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER), [SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER), [SEG_TSS] = SEG_NULL, }; static struct pseudodesc gdt_pd = { sizeof(gdt) - 1, (uint32_t)gdt }; /* * * lgdt - load the global descriptor table register and reset the * data/code segement registers for kernel. * */ static inline void lgdt(struct pseudodesc *pd) { asm volatile ("lgdt (%0)" :: "r" (pd)); asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS)); asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS)); asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS)); asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS)); asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS)); // reload cs asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS)); } /* temporary kernel stack */ uint8_t stack0[1024]; /* gdt_init - initialize the default GDT and TSS */ static void gdt_init(void) { // Setup a TSS so that we can get the right stack when we trap from // user to the kernel. But not safe here, it's only a temporary value, // it will be set to KSTACKTOP in lab2. ts.ts_esp0 = (uint32_t)&stack0 + sizeof(stack0); ts.ts_ss0 = KERNEL_DS; // initialize the TSS filed of the gdt gdt[SEG_TSS] = SEG16(STS_T32A, (uint32_t)&ts, sizeof(ts), DPL_KERNEL); gdt[SEG_TSS].sd_s = 0; // reload all segment registers lgdt(&gdt_pd); // load the TSS ltr(GD_TSS); } /* pmm_init - initialize the physical memory management */ void pmm_init(void) { gdt_init(); }