Break GDT, TSS out of init.c, mmu.h.
[pintos-anon] / src / threads / gdt.c
1 #include "gdt.h"
2 #include "debug.h"
3 #include "mmu.h"
4 #include "palloc.h"
5
6 /* System segment or code/data segment? */
7 enum seg_class
8   {
9     CLS_SYSTEM = 0,             /* System segment. */
10     CLS_CODE_DATA = 1           /* Code or data segment. */
11   };
12
13 /* Limit has byte or 4 kB page granularity? */
14 enum seg_granularity
15   {
16     GRAN_BYTE = 0,              /* Limit has 1-byte granularity. */
17     GRAN_PAGE = 1               /* Limit has 4 kB granularity. */
18   };
19
20 /* Returns a segment descriptor with the given 32-bit BASE and
21    20-bit LIMIT (whose interpretation depends on GRANULARITY).
22    The descriptor represents a system or code/data segment
23    according to CLASS, and TYPE is its type (whose interpretation
24    depends on the class).
25
26    The segment has descriptor privilege level DPL, meaning that
27    it can be used in rings numbered DPL or lower.  In practice,
28    DPL==3 means that user processes can use the segment and
29    DPL==0 means that only the kernel can use the segment.  See
30    [IA32-v3] section 4.5 for further discussion. */
31 static uint64_t
32 make_seg_desc (uint32_t base,
33                uint32_t limit,
34                enum seg_class class,
35                int type,
36                int dpl,
37                enum seg_granularity granularity)
38 {
39   uint32_t e0, e1;
40
41   ASSERT (limit <= 0xfffff);
42   ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
43   ASSERT (type >= 0 && type <= 15);
44   ASSERT (dpl >= 0 && dpl <= 3);
45   ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
46
47   e0 = ((limit & 0xffff)             /* Limit 15:0. */
48         | (base << 16));             /* Base 15:0. */
49
50   e1 = (((base >> 16) & 0xff)        /* Base 23:16. */
51         | (type << 8)                /* Segment type. */
52         | (class << 12)              /* 0=system, 1=code/data. */
53         | (dpl << 13)                /* Descriptor privilege. */
54         | (1 << 15)                  /* Present. */
55         | (limit & 0xf0000)          /* Limit 16:19. */
56         | (1 << 22)                  /* 32-bit segment. */
57         | (granularity << 23)        /* Byte/page granularity. */
58         | (base & 0xff000000));      /* Base 31:24. */
59
60   return e0 | ((uint64_t) e1 << 32);
61 }
62
63 /* Returns a descriptor for a readable code segment with base at
64    0, a limit of 4 GB, and the given DPL. */
65 static uint64_t
66 make_code_desc (int dpl)
67 {
68   return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
69 }
70
71 /* Returns a descriptor for a writable data segment with base at
72    0, a limit of 4 GB, and the given DPL. */
73 static uint64_t
74 make_data_desc (int dpl)
75 {
76   return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
77 }
78
79 /* Returns a descriptor for an "available" 32-bit Task-State
80    Segment with its base at the given linear address, a limit of
81    0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. */
82 static uint64_t
83 make_tss_desc (void *laddr)
84 {
85   return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
86 }
87
88 static uint64_t gdt[SEL_CNT];
89
90 struct tss *tss;
91
92 /* Sets up a proper GDT.  The bootstrap loader's GDT didn't
93    include user-mode selectors or a TSS. */
94 void
95 gdt_init (void)
96 {
97   uint64_t gdtr_operand;
98
99   /* Our TSS is never used in a call gate or task gate, so only a
100      few fields of it are ever referenced, and those are the only
101      ones we initialize. */
102   tss = palloc_get (PAL_ASSERT | PAL_ZERO);
103   tss->esp0 = (uint32_t) ptov(0x20000);
104   tss->ss0 = SEL_KDSEG;
105   tss->bitmap = 0xdfff;
106
107   /* Initialize GDT. */
108   gdt[SEL_NULL / sizeof *gdt] = 0;
109   gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
110   gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
111   gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
112   gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
113   gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss);
114
115   /* Load GDTR, TR. */
116   gdtr_operand = make_dtr_operand (sizeof gdt - 1, gdt);
117   asm volatile ("lgdt %0" :: "m" (gdtr_operand));
118   asm volatile ("ltr %w0" :: "r" (SEL_TSS));
119 }