diff options
Diffstat (limited to 'pintos-progos/userprog/gdt.c')
| -rw-r--r-- | pintos-progos/userprog/gdt.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/pintos-progos/userprog/gdt.c b/pintos-progos/userprog/gdt.c new file mode 100644 index 0000000..e866037 --- /dev/null +++ b/pintos-progos/userprog/gdt.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #include "userprog/gdt.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "userprog/tss.h" | ||
| 4 | #include "threads/palloc.h" | ||
| 5 | #include "threads/vaddr.h" | ||
| 6 | |||
| 7 | /* The Global Descriptor Table (GDT). | ||
| 8 | |||
| 9 | The GDT, an x86-specific structure, defines segments that can | ||
| 10 | potentially be used by all processes in a system, subject to | ||
| 11 | their permissions. There is also a per-process Local | ||
| 12 | Descriptor Table (LDT) but that is not used by modern | ||
| 13 | operating systems. | ||
| 14 | |||
| 15 | Each entry in the GDT, which is known by its byte offset in | ||
| 16 | the table, identifies a segment. For our purposes only three | ||
| 17 | types of segments are of interest: code, data, and TSS or | ||
| 18 | Task-State Segment descriptors. The former two types are | ||
| 19 | exactly what they sound like. The TSS is used primarily for | ||
| 20 | stack switching on interrupts. | ||
| 21 | |||
| 22 | For more information on the GDT as used here, refer to | ||
| 23 | [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor | ||
| 24 | Types". */ | ||
| 25 | static uint64_t gdt[SEL_CNT]; | ||
| 26 | |||
| 27 | /* GDT helpers. */ | ||
| 28 | static uint64_t make_code_desc (int dpl); | ||
| 29 | static uint64_t make_data_desc (int dpl); | ||
| 30 | static uint64_t make_tss_desc (void *laddr); | ||
| 31 | static uint64_t make_gdtr_operand (uint16_t limit, void *base); | ||
| 32 | |||
| 33 | /* Sets up a proper GDT. The bootstrap loader's GDT didn't | ||
| 34 | include user-mode selectors or a TSS, but we need both now. */ | ||
| 35 | void | ||
| 36 | gdt_init (void) | ||
| 37 | { | ||
| 38 | uint64_t gdtr_operand; | ||
| 39 | |||
| 40 | /* Initialize GDT. */ | ||
| 41 | gdt[SEL_NULL / sizeof *gdt] = 0; | ||
| 42 | gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0); | ||
| 43 | gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0); | ||
| 44 | gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3); | ||
| 45 | gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3); | ||
| 46 | gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ()); | ||
| 47 | |||
| 48 | /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor | ||
| 49 | Table Register (GDTR)", 2.4.4 "Task Register (TR)", and | ||
| 50 | 6.2.4 "Task Register". */ | ||
| 51 | gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt); | ||
| 52 | asm volatile ("lgdt %0" : : "m" (gdtr_operand)); | ||
| 53 | asm volatile ("ltr %w0" : : "q" (SEL_TSS)); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* System segment or code/data segment? */ | ||
| 57 | enum seg_class | ||
| 58 | { | ||
| 59 | CLS_SYSTEM = 0, /* System segment. */ | ||
| 60 | CLS_CODE_DATA = 1 /* Code or data segment. */ | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* Limit has byte or 4 kB page granularity? */ | ||
| 64 | enum seg_granularity | ||
| 65 | { | ||
| 66 | GRAN_BYTE = 0, /* Limit has 1-byte granularity. */ | ||
| 67 | GRAN_PAGE = 1 /* Limit has 4 kB granularity. */ | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* Returns a segment descriptor with the given 32-bit BASE and | ||
| 71 | 20-bit LIMIT (whose interpretation depends on GRANULARITY). | ||
| 72 | The descriptor represents a system or code/data segment | ||
| 73 | according to CLASS, and TYPE is its type (whose interpretation | ||
| 74 | depends on the class). | ||
| 75 | |||
| 76 | The segment has descriptor privilege level DPL, meaning that | ||
| 77 | it can be used in rings numbered DPL or lower. In practice, | ||
| 78 | DPL==3 means that user processes can use the segment and | ||
| 79 | DPL==0 means that only the kernel can use the segment. See | ||
| 80 | [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */ | ||
| 81 | static uint64_t | ||
| 82 | make_seg_desc (uint32_t base, | ||
| 83 | uint32_t limit, | ||
| 84 | enum seg_class class, | ||
| 85 | int type, | ||
| 86 | int dpl, | ||
| 87 | enum seg_granularity granularity) | ||
| 88 | { | ||
| 89 | uint32_t e0, e1; | ||
| 90 | |||
| 91 | ASSERT (limit <= 0xfffff); | ||
| 92 | ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA); | ||
| 93 | ASSERT (type >= 0 && type <= 15); | ||
| 94 | ASSERT (dpl >= 0 && dpl <= 3); | ||
| 95 | ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE); | ||
| 96 | |||
| 97 | e0 = ((limit & 0xffff) /* Limit 15:0. */ | ||
| 98 | | (base << 16)); /* Base 15:0. */ | ||
| 99 | |||
| 100 | e1 = (((base >> 16) & 0xff) /* Base 23:16. */ | ||
| 101 | | (type << 8) /* Segment type. */ | ||
| 102 | | (class << 12) /* 0=system, 1=code/data. */ | ||
| 103 | | (dpl << 13) /* Descriptor privilege. */ | ||
| 104 | | (1 << 15) /* Present. */ | ||
| 105 | | (limit & 0xf0000) /* Limit 16:19. */ | ||
| 106 | | (1 << 22) /* 32-bit segment. */ | ||
| 107 | | (granularity << 23) /* Byte/page granularity. */ | ||
| 108 | | (base & 0xff000000)); /* Base 31:24. */ | ||
| 109 | |||
| 110 | return e0 | ((uint64_t) e1 << 32); | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Returns a descriptor for a readable code segment with base at | ||
| 114 | 0, a limit of 4 GB, and the given DPL. */ | ||
| 115 | static uint64_t | ||
| 116 | make_code_desc (int dpl) | ||
| 117 | { | ||
| 118 | return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Returns a descriptor for a writable data segment with base at | ||
| 122 | 0, a limit of 4 GB, and the given DPL. */ | ||
| 123 | static uint64_t | ||
| 124 | make_data_desc (int dpl) | ||
| 125 | { | ||
| 126 | return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Returns a descriptor for an "available" 32-bit Task-State | ||
| 130 | Segment with its base at the given linear address, a limit of | ||
| 131 | 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. | ||
| 132 | See [IA32-v3a] 6.2.2 "TSS Descriptor". */ | ||
| 133 | static uint64_t | ||
| 134 | make_tss_desc (void *laddr) | ||
| 135 | { | ||
| 136 | return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE); | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | /* Returns a descriptor that yields the given LIMIT and BASE when | ||
| 141 | used as an operand for the LGDT instruction. */ | ||
| 142 | static uint64_t | ||
| 143 | make_gdtr_operand (uint16_t limit, void *base) | ||
| 144 | { | ||
| 145 | return limit | ((uint64_t) (uint32_t) base << 16); | ||
| 146 | } | ||
