1 /*
2  * Copyright (c) 2009 Corey Tabaka
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <arch/x86/descriptor.h>
9 
10 #include <assert.h>
11 #include <lk/compiler.h>
12 
13 extern uint64_t _gdt[];
14 
x86_set_gdt_descriptor(seg_sel_t sel,void * base,uint32_t limit,uint8_t present,uint8_t ring,uint8_t sys,uint8_t type,uint8_t gran,uint8_t bits)15 void x86_set_gdt_descriptor(seg_sel_t sel, void *base, uint32_t limit,
16                      uint8_t present, uint8_t ring, uint8_t sys, uint8_t type, uint8_t gran, uint8_t bits) {
17     typedef struct {
18         struct {
19             uint16_t limit_15_0;
20             uint16_t base_15_0;
21             uint8_t base_23_16;
22 
23             uint8_t type : 4;
24             uint8_t s : 1;
25             uint8_t dpl : 2;
26             uint8_t p : 1;
27 
28             uint8_t limit_19_16 : 4;
29             uint8_t avl : 1;
30             uint8_t reserved : 1;
31             uint8_t d_b : 1;
32             uint8_t g : 1;
33 
34             uint8_t base_31_24;
35         } seg_desc_legacy;
36 
37 #if ARCH_X86_64
38         // some descriptors have additional fields for x86-64
39         struct {
40             uint32_t base_63_32;
41             uint32_t reserved;
42         } seg_desc_64;
43 #endif
44     } seg_desc_t;
45 
46 #if ARCH_X86_64
47     static_assert(sizeof(seg_desc_t) == 16, "seg_desc_t size mismatch");
48 #else
49     static_assert(sizeof(seg_desc_t) == 8, "seg_desc_t size mismatch");
50 #endif
51 
52     seg_desc_t desc = {0};
53 
54     desc.seg_desc_legacy.limit_15_0  = limit & 0x0000ffff;
55     desc.seg_desc_legacy.limit_19_16 = (limit & 0x000f0000) >> 16;
56 
57     desc.seg_desc_legacy.base_15_0   = ((uintptr_t) base) & 0x0000ffff;
58     desc.seg_desc_legacy.base_23_16  = (((uintptr_t) base) & 0x00ff0000) >> 16;
59     desc.seg_desc_legacy.base_31_24  = ((uintptr_t) base) >> 24;
60 
61     desc.seg_desc_legacy.type    = type & 0x0f;  // segment type
62     desc.seg_desc_legacy.s       = sys != 0;     // system / non-system
63     desc.seg_desc_legacy.dpl     = ring & 0x03;  // descriptor privilege level
64     desc.seg_desc_legacy.p       = present != 0; // present
65     desc.seg_desc_legacy.avl     = 0;
66     desc.seg_desc_legacy.reserved = 0;
67     desc.seg_desc_legacy.d_b     = bits != 0;    // 16 / 32 bit
68     desc.seg_desc_legacy.g       = gran != 0;    // granularity
69 
70     // convert selector into index, which are always 8 byte indexed
71     uint16_t index = sel >> 3;
72     seg_desc_t *entry = (seg_desc_t *)&_gdt[index];
73     entry->seg_desc_legacy = desc.seg_desc_legacy;
74 
75 #ifdef ARCH_X86_64
76     if (sys == 0) {
77         // some of the system descriptors have two more words
78         switch (type) {
79             case SEG_TYPE_TSS:
80             case SEG_TYPE_TSS_BUSY:
81             case SEG_TYPE_LDT:
82             case SEG_TYPE_CALL_GATE:
83                 // copy the lower 32 bits of the descriptor (base and limit)
84                 desc.seg_desc_64.base_63_32 = (uint32_t)((uintptr_t) base >> 32);
85                 desc.seg_desc_64.reserved = 0;
86 
87                 // copy the upper 64 bits of the descriptor
88                 entry->seg_desc_64 = desc.seg_desc_64;
89                 break;
90         }
91     }
92 #endif
93 }
94