/*! \file * \brief The \ref GDT "Global Descriptor Table (GDT)". */ #pragma once #include "../types.h" /*! \brief Abstracts the GDT that, primarily, contains descriptors to memory * segments. * \ingroup memory * * The GDT is a table that primarily contains segment descriptors. Segment * descriptors has a size of 8 Bytes and contains the size, position, access * rights, and purpose of such a segment. Unlike the LDT, the GDT is shared * between all processes and may contain TSS and LDT descriptors. For the * kernel, the first entry is required to be a null descriptor and the code and * data segments. To support user-mode processes, additional TSS, code, and data * segments for ring 3 must be added. * * The base address and size of the GDT are written to the GDTR register during * boot (via. `lgdt`). * * \see [ISDMv3, 2.4.1; Global Descriptor Table Register * (GDTR)](intel_manual_vol3.pdf#page=72) * \see [ISDMv3, 3.5.1; Segment * Descriptor Tables](intel_manual_vol3.pdf#page=99) */ namespace GDT { enum Segments { SEGMENT_NULL = 0, SEGMENT_KERNEL_CODE, SEGMENT_KERNEL_DATA, }; /*! \brief Unit of the segment limit */ enum Granularity { GRANULARITY_BYTES = 0, ///< Segment limit in Bytes GRANULARITY_4KBLOCK = 1 ///< Segment limit in blocks of 4 Kilobytes }; /*! \brief Descriptor type */ enum DescriptorType { DESCRIPTOR_SYSTEM = 0, ///< entry is a system segment DESCRIPTOR_CODEDATA = 1, ///< entry is a code/data segment }; /*! \brief Address width */ enum Size { SIZE_16BIT = 0, ///< 16-bit (D/B = 0, L = 0) SIZE_32BIT = 2, ///< 32-bit (D/B = 1, L = 0) SIZE_64BIT = 1, ///< 64-bit (D/B = 0, L = 1) }; /*! \brief Type flags for used descriptor types */ enum TypeFlags { TYPE_DATA_RW = 0b0010ull, ///< Data rw, not expanding down TYPE_CODE_RX = 0b1010ull, ///< Code rx, non-conforming }; /*! \brief Describes the structure of segment descriptors * * A data structure that contains size, position, access rights, and purpose of * any segment. Segment descriptors are used in both the GDT, as well as in * LDTs. * * \see [ISDMv3, 3.4.5; Segment Descriptors](intel_manual_vol3.pdf#page=95) * \see [AAPMv2, 4.7 Legacy Segment Descriptors](amd64_manual_vol2.pdf#page=132) * \see [AAPMv2, 4.8 Long-Mode Segment * Descriptors](amd64_manual_vol2.pdf#page=140) */ union SegmentDescriptor { // Universally valid values (shared across all segment types) struct { uint64_t limit_low : 16; ///< Least-significant bits of segment size ///< (influenced by granularity!) uint64_t base_low : 24; ///< Least-significant bits of base address uint64_t type : 4; ///< Meaning of those 4 bits depends on descriptor_type below DescriptorType descriptor_type : 1; ///< Descriptor type (influences the ///< meaning of the 3 bits above) uint64_t privilege_level : 2; ///< Ring for this segment bool present : 1; ///< Entry is valid iff set to `true` uint64_t limit_high : 4; ///< Most-significant bits of segment size bool available : 1; ///< Bit which can be used for other purposes (in ///< software) uint64_t custom : 2; ///< Meaning of those 2 bits relate to descriptor_type ///< and type Granularity granularity : 1; ///< Unit used as granularity for the segment limit uint64_t base_high : 8; ///< most-significant bits of base address } __attribute__((packed)); uint64_t value; ///!< Merged value /*! \brief Explicitly constructs a null descriptor. */ consteval static SegmentDescriptor Null() { return SegmentDescriptor{ .value = 0, }; } /*! \brief Constructs a code/data segment descriptor. * \param base Base Address of segment * \param limit Size of segment * \param code Code or data segment * \param ring Privilege level * \param size Address width */ consteval static SegmentDescriptor Segment(uintptr_t base, uint32_t limit, bool code, uint64_t ring, Size size) { return SegmentDescriptor{ .limit_low = limit >> (limit > 0xFFFFF ? 12 : 0) & 0xFFFF, .base_low = base & 0xFFFFFF, .type = code ? TYPE_CODE_RX : TYPE_DATA_RW, .descriptor_type = DESCRIPTOR_CODEDATA, .privilege_level = ring, .present = true, .limit_high = (limit > 0xFFFFF ? (limit >> 28) : (limit >> 16)) & 0xF, .available = false, .custom = size, .granularity = limit > 0xFFFFF ? GRANULARITY_4KBLOCK : GRANULARITY_BYTES, .base_high = (base >> 24) & 0xFF, }; } /*! \brief Constructs a 64bit code/data segment descriptor. * \param code Code or data segment * \param ring Privilege level */ consteval static SegmentDescriptor Segment64(bool code, int ring) { return SegmentDescriptor::Segment(0, 0, code, ring, SIZE_64BIT); } } __attribute__((packed)); static_assert(sizeof(SegmentDescriptor) == 8, "GDT::SegmentDescriptor has wrong size"); /*! \brief Structure that describes a GDT Pointer (aka GDT Descriptor) * * It contains both the length (in bytes) of the GDT (minus 1 byte) and the * pointer to the GDT. The pointer to the GDT can be loaded using the * instruction `lgdt`. * * \note As Intel uses little endian for representing multi-byte values, the * GDT::Pointer structure can be used for 16, 32, and 64 bit descriptor tables: * \verbatim * | 16 bit | 16 bit | 16 bit | 16 bit | 16 bit | * +--------+---------------------------------------+ * Pointer | limit | base (up to 64 bit) | * +--------+---------+---------+---------+---------+ * | used for 16 bit | ignored... | * | used for 32 bit | ignored... | * | used for 64 bit | * \endverbatim * * \see [ISDMv3, Figure 2-6; Memory Management * Registers](intel_manual_vol3.pdf#page=72) */ struct Pointer { uint16_t limit; //!< GDT size in bytes (minus 1 byte) void* base; //!< GDT base address /*! \brief Constructor (automatic length) * \param desc Array of GDT segment descriptors -- must be defined in the same * module! */ template explicit constexpr Pointer(const T (&desc)[LEN]) : limit(LEN * sizeof(T) - 1), base(const_cast(desc)) {} /*! \brief Constructor * \param desc Address of the GDT segment descriptors * \param len Number of entries */ consteval Pointer(void* desc, size_t len) : limit(len * sizeof(SegmentDescriptor) - 1), base(desc) {} /*! \brief Set an address * \note On change, `lgdt` must be executed again * \param desc Address of the GDT segment descriptors * \param len Number of entries */ constexpr void set(void* desc, size_t len) { limit = len * sizeof(SegmentDescriptor) - 1; base = desc; } } __attribute__((packed)); static_assert(sizeof(Pointer) == 10, "GDT::Pointer has wrong size"); } // namespace GDT