1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#pragma once
#include <types.h>
/*
* http://lowlevel.eu/wiki/Global_Descriptor_Table
* https://wiki.osdev.org/GDT_Tutorial
*
* From OSDev wiki:
*
* Segment
* a logically contiguous chunk of memory with consistent properties (CPU's speaking)
*
* Segment Register
* a register of your CPU that refers to a segment for a special use (e.g. SS, CS, DS ...)
*
* Selector
* a reference to a descriptor you can load into a segment register; the selector is an offset of a descriptor table
* entry. These entries are 8 bytes long. Therefore, bits 3 and up only declare the descriptor table entry offset, while
* bit 2 specifies if this selector is a GDT or LDT selector (LDT - bit set, GDT - bit cleared), and bits 0 - 1 declare
* the ring level that needs to correspond to the descriptor table entry's DPL field. If it doesn't, a General
* Protection Fault occurs; if it does correspond then the CPL level of the selector used is changed accordingly.
*
* Descriptor
* a memory structure (part of a table) that tells the CPU the attributes of a given segment
*/
class GDT {
public:
GDT();
~GDT() = default;
uint16_t codeDescriptor() const;
struct Pointer {
uint16_t limit;
uint32_t base;
} __attribute((packed));
class SegmentDescriptor {
public:
enum Ring : unsigned int { Ring0 = 0x0, Ring1 = 0x1, Ring2 = 0x2, Ring3 = 0x3 };
struct Access {
bool accessed : 1 = false; // if 0, is set by processor when accessed
bool r_w : 1 = false; // on code segment: read toggle
// on data segment: write toggle
bool direction_conforming : 1 = false; // TODO
bool exe : 1 = false; // true for code segment, false for data segment
bool segment : 1 = false; // segment bit: 1 for code/data segments
// 0 for gates and tss
Ring privilege : 2 = Ring0; // descriptor privilege level
bool present : 1 = true; // must be 1 for every active segment
} __attribute__((packed));
SegmentDescriptor() { access.present = false; };
template <uint32_t limit>
static SegmentDescriptor make(uint32_t base, Access type) {
static_assert(limit <= 0xffffu || (limit & 0xfff) == 0xfff);
return SegmentDescriptor(base, limit, type);
}
[[nodiscard]] uint32_t base() const;
[[nodiscard]] uint32_t limit() const;
private:
SegmentDescriptor(uint32_t base, uint32_t limit, Access type);
/*
* |31| | | | | | |24|23|22|21|20|19| | |16|15| | |12|11| | | 8| 7| | | | | | | 0|
* | base_31_24 | G|DB| | A| lim_19_16 | P| DPL | S| Type | base_23_16 |
* | base_15_0 | limit_15_0 |
* |31| | | | | | | | | | | | | | |16|15| | | | | | | | | | | | | | | 0|
*
* limit size of segment - 1, either in bytes or in 4KiB chunks (check flags)
* base address of segment
* access
* flags defines the segment chunks and 16/32 bit
*/
unsigned int limit_15_0 : 16 = 0; // low bits of segment limit
unsigned int base_15_0 : 16 = 0; // low bits of segment base address
unsigned int base_23_16 : 8 = 0; // middle bits of segment base address
/* access byte */
Access access;
unsigned int limit_19_16 : 4 = 0; // high bits of segment limit
/* flags */
[[maybe_unused]] unsigned int a : 1 = 0; // unused, available for software use
[[maybe_unused]] unsigned int rsv : 1 = 0; // reserved
unsigned int db : 1 = 0; // 0: 16-bit segment; 1: 32-bit segment
unsigned int granularity : 1 = 0; // limit scaled by 4k when set
unsigned int base_31_24 : 8 = 0; // high bits of segment address
} __attribute__((packed));
private:
SegmentDescriptor segments[256];
};
static_assert(sizeof(GDT::Pointer) == 6);
static_assert(sizeof(GDT::SegmentDescriptor) == 8);
static_assert(sizeof(GDT::SegmentDescriptor::Access) == 1);
|