#pragma once #include /* * 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: 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 : 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 = false; // must be 1 for every active segment constexpr operator uint8_t() const { return static_cast(accessed) | static_cast(r_w << 1) | static_cast(direction << 2) | static_cast(exe << 3) | static_cast(segment << 4) | static_cast(privilege << 5) | static_cast(present << 7); } } __attribute__((packed)); constexpr SegmentDescriptor() { access.present = false; }; template 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)); enum SegmentMap { null0 = 0, kcode = 1, kdata = 2 }; GDT(); ~GDT() = default; static constexpr uint16_t descriptor(GDT::SegmentMap i) { return static_cast(i * sizeof(SegmentDescriptor)); } };