1 #include "userprog/gdt.h"
3 #include "userprog/tss.h"
4 #include "threads/mmu.h"
5 #include "threads/palloc.h"
7 /* The Global Descriptor Table (GDT).
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
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.
22 For more information on the GDT as used here, refer to
23 [IA32-v3] sections 3.2 through 3.5. */
24 static uint64_t gdt[SEL_CNT];
27 static uint64_t make_code_desc (int dpl);
28 static uint64_t make_data_desc (int dpl);
29 static uint64_t make_tss_desc (void *laddr);
30 static uint64_t make_gdtr_operand (uint16_t limit, void *base);
32 /* Sets up a proper GDT. The bootstrap loader's GDT didn't
33 include user-mode selectors or a TSS, but we need both now. */
37 uint64_t gdtr_operand;
40 gdt[SEL_NULL / sizeof *gdt] = 0;
41 gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
42 gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
43 gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
44 gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
45 gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
47 /* Load GDTR, TR. See [IA32-v3] 2.4.1, 2.4.4, 6.2.3. */
48 gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
49 asm volatile ("lgdt %0" :: "m" (gdtr_operand));
50 asm volatile ("ltr %w0" :: "r" (SEL_TSS));
53 /* System segment or code/data segment? */
56 CLS_SYSTEM = 0, /* System segment. */
57 CLS_CODE_DATA = 1 /* Code or data segment. */
60 /* Limit has byte or 4 kB page granularity? */
63 GRAN_BYTE = 0, /* Limit has 1-byte granularity. */
64 GRAN_PAGE = 1 /* Limit has 4 kB granularity. */
67 /* Returns a segment descriptor with the given 32-bit BASE and
68 20-bit LIMIT (whose interpretation depends on GRANULARITY).
69 The descriptor represents a system or code/data segment
70 according to CLASS, and TYPE is its type (whose interpretation
71 depends on the class).
73 The segment has descriptor privilege level DPL, meaning that
74 it can be used in rings numbered DPL or lower. In practice,
75 DPL==3 means that user processes can use the segment and
76 DPL==0 means that only the kernel can use the segment. See
77 [IA32-v3] section 4.5 for further discussion. */
79 make_seg_desc (uint32_t base,
84 enum seg_granularity granularity)
88 ASSERT (limit <= 0xfffff);
89 ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
90 ASSERT (type >= 0 && type <= 15);
91 ASSERT (dpl >= 0 && dpl <= 3);
92 ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
94 e0 = ((limit & 0xffff) /* Limit 15:0. */
95 | (base << 16)); /* Base 15:0. */
97 e1 = (((base >> 16) & 0xff) /* Base 23:16. */
98 | (type << 8) /* Segment type. */
99 | (class << 12) /* 0=system, 1=code/data. */
100 | (dpl << 13) /* Descriptor privilege. */
101 | (1 << 15) /* Present. */
102 | (limit & 0xf0000) /* Limit 16:19. */
103 | (1 << 22) /* 32-bit segment. */
104 | (granularity << 23) /* Byte/page granularity. */
105 | (base & 0xff000000)); /* Base 31:24. */
107 return e0 | ((uint64_t) e1 << 32);
110 /* Returns a descriptor for a readable code segment with base at
111 0, a limit of 4 GB, and the given DPL. */
113 make_code_desc (int dpl)
115 return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
118 /* Returns a descriptor for a writable data segment with base at
119 0, a limit of 4 GB, and the given DPL. */
121 make_data_desc (int dpl)
123 return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
126 /* Returns a descriptor for an "available" 32-bit Task-State
127 Segment with its base at the given linear address, a limit of
128 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0.
129 See [IA32-v3] 6.2.2. */
131 make_tss_desc (void *laddr)
133 return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
137 /* Returns a descriptor that yields the given LIMIT and BASE when
138 used as an operand for the LGDT instruction. */
140 make_gdtr_operand (uint16_t limit, void *base)
142 return limit | ((uint64_t) (uint32_t) base << 16);