|
|
- #include <defs.h>
- #include <x86.h>
- #include <mmu.h>
- #include <memlayout.h>
- #include <pmm.h>
-
- /* *
- * 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();
- }
-
|