diff options
Diffstat (limited to 'pintos-progos/userprog/tss.c')
| -rw-r--r-- | pintos-progos/userprog/tss.c | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/pintos-progos/userprog/tss.c b/pintos-progos/userprog/tss.c new file mode 100644 index 0000000..f8ed9a9 --- /dev/null +++ b/pintos-progos/userprog/tss.c | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | #include "userprog/tss.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include "userprog/gdt.h" | ||
| 5 | #include "threads/thread.h" | ||
| 6 | #include "threads/palloc.h" | ||
| 7 | #include "threads/vaddr.h" | ||
| 8 | |||
| 9 | /* The Task-State Segment (TSS). | ||
| 10 | |||
| 11 | Instances of the TSS, an x86-specific structure, are used to | ||
| 12 | define "tasks", a form of support for multitasking built right | ||
| 13 | into the processor. However, for various reasons including | ||
| 14 | portability, speed, and flexibility, most x86 OSes almost | ||
| 15 | completely ignore the TSS. We are no exception. | ||
| 16 | |||
| 17 | Unfortunately, there is one thing that can only be done using | ||
| 18 | a TSS: stack switching for interrupts that occur in user mode. | ||
| 19 | When an interrupt occurs in user mode (ring 3), the processor | ||
| 20 | consults the ss0 and esp0 members of the current TSS to | ||
| 21 | determine the stack to use for handling the interrupt. Thus, | ||
| 22 | we must create a TSS and initialize at least these fields, and | ||
| 23 | this is precisely what this file does. | ||
| 24 | |||
| 25 | When an interrupt is handled by an interrupt or trap gate | ||
| 26 | (which applies to all interrupts we handle), an x86 processor | ||
| 27 | works like this: | ||
| 28 | |||
| 29 | - If the code interrupted by the interrupt is in the same | ||
| 30 | ring as the interrupt handler, then no stack switch takes | ||
| 31 | place. This is the case for interrupts that happen when | ||
| 32 | we're running in the kernel. The contents of the TSS are | ||
| 33 | irrelevant for this case. | ||
| 34 | |||
| 35 | - If the interrupted code is in a different ring from the | ||
| 36 | handler, then the processor switches to the stack | ||
| 37 | specified in the TSS for the new ring. This is the case | ||
| 38 | for interrupts that happen when we're in user space. It's | ||
| 39 | important that we switch to a stack that's not already in | ||
| 40 | use, to avoid corruption. Because we're running in user | ||
| 41 | space, we know that the current process's kernel stack is | ||
| 42 | not in use, so we can always use that. Thus, when the | ||
| 43 | scheduler switches threads, it also changes the TSS's | ||
| 44 | stack pointer to point to the new thread's kernel stack. | ||
| 45 | (The call is in thread_schedule_tail() in thread.c.) | ||
| 46 | |||
| 47 | See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a | ||
| 48 | description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or | ||
| 49 | Interrupt-Handler Procedures" for a description of when and | ||
| 50 | how stack switching occurs during an interrupt. */ | ||
| 51 | struct tss | ||
| 52 | { | ||
| 53 | uint16_t back_link, :16; | ||
| 54 | void *esp0; /* Ring 0 stack virtual address. */ | ||
| 55 | uint16_t ss0, :16; /* Ring 0 stack segment selector. */ | ||
| 56 | void *esp1; | ||
| 57 | uint16_t ss1, :16; | ||
| 58 | void *esp2; | ||
| 59 | uint16_t ss2, :16; | ||
| 60 | uint32_t cr3; | ||
| 61 | void (*eip) (void); | ||
| 62 | uint32_t eflags; | ||
| 63 | uint32_t eax, ecx, edx, ebx; | ||
| 64 | uint32_t esp, ebp, esi, edi; | ||
| 65 | uint16_t es, :16; | ||
| 66 | uint16_t cs, :16; | ||
| 67 | uint16_t ss, :16; | ||
| 68 | uint16_t ds, :16; | ||
| 69 | uint16_t fs, :16; | ||
| 70 | uint16_t gs, :16; | ||
| 71 | uint16_t ldt, :16; | ||
| 72 | uint16_t trace, bitmap; | ||
| 73 | }; | ||
| 74 | |||
| 75 | /* Kernel TSS. */ | ||
| 76 | static struct tss *tss; | ||
| 77 | |||
| 78 | /* Initializes the kernel TSS. */ | ||
| 79 | void | ||
| 80 | tss_init (void) | ||
| 81 | { | ||
| 82 | /* Our TSS is never used in a call gate or task gate, so only a | ||
| 83 | few fields of it are ever referenced, and those are the only | ||
| 84 | ones we initialize. */ | ||
| 85 | tss = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 86 | tss->ss0 = SEL_KDSEG; | ||
| 87 | tss->bitmap = 0xdfff; | ||
| 88 | tss_update (); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Returns the kernel TSS. */ | ||
| 92 | struct tss * | ||
| 93 | tss_get (void) | ||
| 94 | { | ||
| 95 | ASSERT (tss != NULL); | ||
| 96 | return tss; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Sets the ring 0 stack pointer in the TSS to point to the end | ||
| 100 | of the thread stack. */ | ||
| 101 | void | ||
| 102 | tss_update (void) | ||
| 103 | { | ||
| 104 | ASSERT (tss != NULL); | ||
| 105 | tss->esp0 = (uint8_t *) thread_current () + PGSIZE; | ||
| 106 | } | ||
