CS318 - Pintos
Pintos source browser for JHU CS318 course
gdt.c
Go to the documentation of this file.
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". */
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? */
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? */
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
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 }
uint64_t
unsigned long long int uint64_t
Definition: stdint.h:29
SEL_TSS
#define SEL_TSS
Task-state segment.
Definition: gdt.h:10
gdt.h
make_data_desc
static uint64_t make_data_desc(int dpl)
Returns a descriptor for a writable data segment with base at 0, a limit of 4 GB, and the given DPL.
Definition: gdt.c:124
CLS_CODE_DATA
Code or data segment.
Definition: gdt.c:60
make_code_desc
static uint64_t make_code_desc(int dpl)
GDT helpers.
Definition: gdt.c:116
make_tss_desc
static uint64_t make_tss_desc(void *laddr)
Returns a descriptor for an "available" 32-bit Task-State Segment with its base at the given linear a...
Definition: gdt.c:134
gdt
static uint64_t gdt[SEL_CNT]
The Global Descriptor Table (GDT).
Definition: gdt.c:25
SEL_KCSEG
#define SEL_KCSEG
Kernel code selector.
Definition: loader.h:30
uint16_t
unsigned short int uint16_t
Definition: stdint.h:23
SEL_UDSEG
#define SEL_UDSEG
User data selector.
Definition: gdt.h:9
uint32_t
unsigned int uint32_t
Definition: stdint.h:26
GRAN_PAGE
Limit has 4 kB granularity.
Definition: gdt.c:67
palloc.h
ASSERT
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
gdt_init
void gdt_init(void)
Sets up a proper GDT.
Definition: gdt.c:36
SEL_CNT
#define SEL_CNT
Number of segments.
Definition: gdt.h:11
CLS_SYSTEM
System segment.
Definition: gdt.c:59
SEL_UCSEG
#define SEL_UCSEG
Segment selectors.
Definition: gdt.h:8
vaddr.h
seg_granularity
seg_granularity
Limit has byte or 4 kB page granularity?
Definition: gdt.c:64
SEL_KDSEG
#define SEL_KDSEG
Kernel data selector.
Definition: loader.h:31
make_gdtr_operand
static uint64_t make_gdtr_operand(uint16_t limit, void *base)
Returns a descriptor that yields the given LIMIT and BASE when used as an operand for the LGDT instru...
Definition: gdt.c:143
tss.h
SEL_NULL
#define SEL_NULL
GDT selectors defined by loader.
Definition: loader.h:29
tss_get
struct tss * tss_get(void)
Returns the kernel TSS.
Definition: tss.c:93
seg_class
seg_class
System segment or code/data segment?
Definition: gdt.c:57
make_seg_desc
static uint64_t make_seg_desc(uint32_t base, uint32_t limit, enum seg_class class, int type, int dpl, enum seg_granularity granularity)
Returns a segment descriptor with the given 32-bit BASE and 20-bit LIMIT (whose interpretation depend...
Definition: gdt.c:82
debug.h
GRAN_BYTE
Limit has 1-byte granularity.
Definition: gdt.c:66