X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fuserprog%2Ftss.c;h=f8ed9a90ab0d7cf0cad855842cac8570a1e720f6;hb=64e74e3536b95c4d0a46aec56bc373ad0f6d470b;hp=37d075b22ac5594a31d624ffe72cb0ecda751897;hpb=6c5c6fdfe80bad40c90c19b67f00226610d59a38;p=pintos-anon diff --git a/src/userprog/tss.c b/src/userprog/tss.c index 37d075b..f8ed9a9 100644 --- a/src/userprog/tss.c +++ b/src/userprog/tss.c @@ -1,15 +1,58 @@ -#include "tss.h" +#include "userprog/tss.h" +#include #include -#include "debug.h" -#include "gdt.h" -#include "mmu.h" -#include "palloc.h" +#include "userprog/gdt.h" +#include "threads/thread.h" +#include "threads/palloc.h" +#include "threads/vaddr.h" +/* The Task-State Segment (TSS). + + Instances of the TSS, an x86-specific structure, are used to + define "tasks", a form of support for multitasking built right + into the processor. However, for various reasons including + portability, speed, and flexibility, most x86 OSes almost + completely ignore the TSS. We are no exception. + + Unfortunately, there is one thing that can only be done using + a TSS: stack switching for interrupts that occur in user mode. + When an interrupt occurs in user mode (ring 3), the processor + consults the ss0 and esp0 members of the current TSS to + determine the stack to use for handling the interrupt. Thus, + we must create a TSS and initialize at least these fields, and + this is precisely what this file does. + + When an interrupt is handled by an interrupt or trap gate + (which applies to all interrupts we handle), an x86 processor + works like this: + + - If the code interrupted by the interrupt is in the same + ring as the interrupt handler, then no stack switch takes + place. This is the case for interrupts that happen when + we're running in the kernel. The contents of the TSS are + irrelevant for this case. + + - If the interrupted code is in a different ring from the + handler, then the processor switches to the stack + specified in the TSS for the new ring. This is the case + for interrupts that happen when we're in user space. It's + important that we switch to a stack that's not already in + use, to avoid corruption. Because we're running in user + space, we know that the current process's kernel stack is + not in use, so we can always use that. Thus, when the + scheduler switches threads, it also changes the TSS's + stack pointer to point to the new thread's kernel stack. + (The call is in thread_schedule_tail() in thread.c.) + + See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a + description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or + Interrupt-Handler Procedures" for a description of when and + how stack switching occurs during an interrupt. */ struct tss { uint16_t back_link, :16; - void *esp0; - uint16_t ss0, :16; + void *esp0; /* Ring 0 stack virtual address. */ + uint16_t ss0, :16; /* Ring 0 stack segment selector. */ void *esp1; uint16_t ss1, :16; void *esp2; @@ -29,20 +72,23 @@ struct tss uint16_t trace, bitmap; }; +/* Kernel TSS. */ static struct tss *tss; +/* Initializes the kernel TSS. */ void tss_init (void) { /* Our TSS is never used in a call gate or task gate, so only a few fields of it are ever referenced, and those are the only ones we initialize. */ - tss = palloc_get (PAL_ASSERT | PAL_ZERO); - tss->esp0 = ptov(0x20000); + tss = palloc_get_page (PAL_ASSERT | PAL_ZERO); tss->ss0 = SEL_KDSEG; tss->bitmap = 0xdfff; + tss_update (); } +/* Returns the kernel TSS. */ struct tss * tss_get (void) { @@ -50,9 +96,11 @@ tss_get (void) return tss; } +/* Sets the ring 0 stack pointer in the TSS to point to the end + of the thread stack. */ void -tss_set_esp0 (uint8_t *esp0) +tss_update (void) { ASSERT (tss != NULL); - tss->esp0 = esp0; + tss->esp0 = (uint8_t *) thread_current () + PGSIZE; }