aboutsummaryrefslogtreecommitdiff
path: root/src/gdt.h
blob: a86a14f91467d92fb1e54e0e2fca1f0f9d4f26bb (plain)
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
#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;

  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);