KernOS
gdt.cpp
Go to the documentation of this file.
1 //
2 // Created on 5/20/20.
3 //
4 
5 #include <common.h>
6 #include <gdt.h>
7 #include <utilities.h>
8 #include <registers.h>
9 #include <accessright.h>
10 
11 namespace GDT
12 {
28  union [[gnu::packed]] GdtEntry
29  {
30  struct
31  {
32  uint16_t LimitLow;
33  uint16_t BaseLow;
34  uint8_t BaseMid;
35  uint8_t Access;
37  uint8_t BaseHigh;
38  };
39  struct
40  {
41  uint32_t Low;
42  uint32_t High;
43  };
44  };
45 
48  struct Selector
49  {
51  uint8_t m_Offset;
52  uint32_t m_Base;
53  uint32_t m_Limit;
54  uint8_t m_Access;
55  uint8_t m_Granularity;
56  };
57 
58  const uint32_t NULL_LIMIT = 0x00000000;
59  const uint32_t KERN_CS_LIMIT = 0x000FFFFF; // only low 20 bits used
60  const uint32_t KERN_DS_LIMIT = 0x000FFFFF; // only low 20 bits used
61  const uint32_t USER_CS_LIMIT = 0x000FFFFF; // only low 20 bits used
62  const uint32_t USER_DS_LIMIT = 0x000FFFFF; // only low 20 bits used
63 
64  const uint32_t NULL_BASE = 0x00000000;
65  const uint32_t KERN_CS_BASE = 0x00000000;
66  const uint32_t KERN_DS_BASE = 0x00000000;
67  const uint32_t USER_CS_BASE = 0x00000000;
68  const uint32_t USER_DS_BASE = 0x00000000;
69 
70  const uint8_t NULL_GRANULARITY = 0x00; // |G|D| |A| only high 4 bits used
71  const uint8_t KERN_CS_GRANULARITY = 0xC0; // |1|1|0|0|
72  const uint8_t KERN_DS_GRANULARITY = 0xC0; // |1|1|0|0|
73  const uint8_t USER_CS_GRANULARITY = 0xC0; // |1|1|0|0|
74  const uint8_t USER_DS_GRANULARITY = 0xC0; // |1|1|0|0|
75 
76  static GdtEntry gdt_table[GDT_ENTRIES];
77 
82  void SetGlobalDescriptorEntry(GdtEntry GdtTable[], const Selector SelectorTable[])
83  {
84  for (size_t Idx = 0; Idx < GDT_ENTRIES; ++Idx) {
85 
86  const auto &Sel = SelectorTable[Idx];
87 
88  GdtTable[Sel.m_Seg].BaseLow = (Sel.m_Base & 0xFFFF);
89  GdtTable[Sel.m_Seg].BaseMid = (Sel.m_Base >> 16) & 0xFF;
90  GdtTable[Sel.m_Seg].BaseHigh = (Sel.m_Base >> 24) & 0xFF;
91 
92  GdtTable[Sel.m_Seg].LimitLow = (Sel.m_Limit & 0xFFFF);
93  GdtTable[Sel.m_Seg].LimitHighAndGranularity = ((Sel.m_Limit >> 16) & 0x0F);
94 
95  GdtTable[Sel.m_Seg].LimitHighAndGranularity |= (Sel.m_Granularity & 0xF0);
96  GdtTable[Sel.m_Seg].Access = Sel.m_Access;
97  }
98  }
99 
104  inline void Load_gdt(void *gdtAddress, uint16_t LimitUse)
105  {
106  struct [[gnu::packed]]
107  {
108  uint16_t Limit;
109  void *Base;
110  } GTR = { LimitUse, gdtAddress };
111 
112  asm volatile (
113  "lgdt %0" // load interrupt descriptor table
114  : // no output
115  : "m"(GTR) // memory operand allowed, with any kind of address that the machine supports
116  );
117  }
118 
125  void Install_gdt()
126  {
127  constexpr struct Selector SelTable[GDT_ENTRIES] =
128  {
129  { Null, SEG_OFFSET(Null), NULL_BASE, NULL_LIMIT, AR::NULL_ACCESS, NULL_GRANULARITY },
130  { K_CS, SEG_OFFSET(K_CS), KERN_CS_BASE, KERN_CS_LIMIT, AR::KERN_CS_ACCESS, KERN_CS_GRANULARITY },
131  { K_DS, SEG_OFFSET(K_DS), KERN_DS_BASE, KERN_DS_LIMIT, AR::KERN_DS_ACCESS, KERN_DS_GRANULARITY },
132  { U_CS, SEG_OFFSET(U_CS), USER_CS_BASE, USER_CS_LIMIT, AR::USER_CS_ACCESS, USER_CS_GRANULARITY },
133  { U_DS, SEG_OFFSET(U_DS), USER_DS_BASE, USER_DS_LIMIT, AR::USER_DS_ACCESS, USER_DS_GRANULARITY }
134  };
135 
136  SetGlobalDescriptorEntry(gdt_table, SelTable);
137 
138  Load_gdt(gdt_table, GDT_ENTRIES * 8 - 1);
139 
140  kassert (ReadCR0() & DWord<CR0::PE>(), "PE bit of cr0 must be set for protected mode\n");
141 
142  // while grub sets CS, DS, ES, FS, GS, and SS, the exact values are undefined, thus manually set them
143  // http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state
144 
145  // set DS, ES, FS, GS, and SS
146  asm volatile (
147  "mov %%ax, %%ds\n\t"
148  "mov %%ax, %%es\n\t"
149  "mov %%ax, %%fs\n\t"
150  "mov %%ax, %%gs\n\t"
151  "mov %%ax, %%ss\n\t"
152  : // no output
153  : "a"(SelTable[K_DS].m_Offset)
154  : "memory"
155  );
156 
157  // set CS
158  asm volatile (
159  "ljmpl %0, $CS_LABEL\n\t" // dummy long jump to force kernel code selector update
160  "CS_LABEL:\n\t"
161  : // no output
162  : "n"(SelTable[K_CS].m_Offset) // SelTable is constexpr, thus allowed to be used as immediate integer operand
163  );
164  }
165 }
166 
167 namespace INIT
168 {
172  void gdt()
173  {
175  }
176 }
177 
void SetGlobalDescriptorEntry(GdtEntry GdtTable[], const Selector SelectorTable[])
Set entries in GdtTable from array of Selector.
Definition: gdt.cpp:82
const uint32_t KERN_CS_BASE
Definition: gdt.cpp:65
uint8_t Access
Definition: gdt.cpp:35
Segment m_Seg
Segment enum.
Definition: gdt.cpp:50
uint8_t m_Offset
Segment byte position in gdt_table.
Definition: gdt.cpp:51
uint8_t BaseMid
Definition: gdt.cpp:34
uint32_t m_Limit
Limit address of segment.
Definition: gdt.cpp:53
void gdt()
creates global descriptor table and loads it into CPU
Definition: gdt.cpp:172
user code segment
Definition: gdt.h:21
const uint32_t USER_CS_LIMIT
Definition: gdt.cpp:61
global descriptor table
Definition: gdt.h:10
null descriptor
Definition: gdt.h:18
const uint32_t USER_CS_BASE
Definition: gdt.cpp:67
uint16_t LimitLow
Definition: gdt.cpp:32
uint32_t m_Base
Base address of segment.
Definition: gdt.cpp:52
const uint32_t NULL_BASE
Definition: gdt.cpp:64
user data segment
Definition: gdt.h:22
const uint8_t KERN_CS_ACCESS
Kernel code segment access descriptor.
Definition: accessright.h:59
const uint8_t USER_DS_ACCESS
User data segment access descriptor.
Definition: accessright.h:86
uint8_t m_Granularity
Granularity setting.
Definition: gdt.cpp:55
uint16_t BaseLow
Definition: gdt.cpp:33
const uint32_t KERN_CS_LIMIT
Definition: gdt.cpp:59
const uint8_t USER_DS_GRANULARITY
Definition: gdt.cpp:74
uint8_t BaseHigh
Definition: gdt.cpp:37
contains all kernel initialization routines
Definition: cpu.h:10
const uint32_t NULL_LIMIT
Definition: gdt.cpp:58
void Install_gdt()
Creates global descriptor entries in gdt_table, and loads into CPU.
Definition: gdt.cpp:125
const uint8_t USER_CS_GRANULARITY
Definition: gdt.cpp:73
uint8_t m_Access
Access right bytes.
Definition: gdt.cpp:54
const uint8_t NULL_GRANULARITY
Definition: gdt.cpp:70
kernel data segment
Definition: gdt.h:20
const uint8_t GDT_ENTRIES
Definition: gdt.h:12
void Load_gdt(void *gdtAddress, uint16_t LimitUse)
assembly instruction to load gdt table to CPU
Definition: gdt.cpp:104
constexpr uint8_t SEG_OFFSET(const Segment Seg)
Translates code segment enum to code segment selector.
Definition: gdt.h:29
const uint32_t KERN_DS_BASE
Definition: gdt.cpp:66
Segment
Code segments.
Definition: gdt.h:16
kernel code segment
Definition: gdt.h:19
uint32_t High
Definition: gdt.cpp:42
const uint32_t KERN_DS_LIMIT
Definition: gdt.cpp:60
convenience structure used to set bits of GdtEntry
Definition: gdt.cpp:48
const uint8_t USER_CS_ACCESS
User code segment access descriptor.
Definition: accessright.h:68
const uint32_t USER_DS_BASE
Definition: gdt.cpp:68
const uint8_t KERN_DS_GRANULARITY
Definition: gdt.cpp:72
const uint32_t USER_DS_LIMIT
Definition: gdt.cpp:62
uint8_t LimitHighAndGranularity
Definition: gdt.cpp:36
uint32_t Low
Definition: gdt.cpp:41
Global descriptor table entry.
Definition: gdt.cpp:28
const uint8_t KERN_DS_ACCESS
Kernel data segment access descriptor.
Definition: accessright.h:77
const uint8_t NULL_ACCESS
Definition: accessright.h:50
const uint8_t KERN_CS_GRANULARITY
Definition: gdt.cpp:71