forked from BSB-WS23/mpstubs
204 lines
8.7 KiB
C++
204 lines
8.7 KiB
C++
/*! \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_CODE = 1, ///< 64-bit (D/B = 0, L = 1)
|
|
SIZE_64BIT_DATA = 0, ///< 64-bit (D/B = 0, L = 0)
|
|
};
|
|
|
|
/*! \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));
|
|
|
|
/*! \brief Fields specific for Code Segment
|
|
* (for debugging purposes)
|
|
* \see [ISDMv3, 3.4.5.1; Code- and Data-Segment Descriptor Types](intel_manual_vol3.pdf#page=98)
|
|
*/
|
|
struct {
|
|
uint64_t : 40; ///< Ignored (set via `limit_low` and `base_low` )
|
|
|
|
/* `type` field bits */
|
|
bool code_accessed : 1; ///< If set, the code segment was used since the last reset of this value
|
|
bool readable : 1; ///< If set, the code is readable (otherwise only executable)
|
|
|
|
/*! \brief If set, the execution of code from this segment is only allowed when running at a privilege of
|
|
* numerically less than or equal to privilege_level (i.e. the executor has the same or higher
|
|
* privileges). However, the executor's privileges remain unchanged.
|
|
* For nonconforming code segments (i.e., conforming is set to `0`), execution is allowed only if
|
|
* the privileges are equal.
|
|
* Execution will cause a GPF in case of privilege violation.
|
|
*/
|
|
bool conforming : 1;
|
|
bool code : 1; ///< Has to be set to `true`
|
|
|
|
uint64_t : 9; ///< Ignored (set via `privilege_level` ... `available`)
|
|
|
|
Size operation_size : 2; ///< Default address width (`custom` field bit)
|
|
|
|
uint64_t : 0; ///< Remainder ignored (set via `base_high`)
|
|
} __attribute__((packed));
|
|
|
|
/*! \brief Fields specific for Data Segment
|
|
* (for debugging purposes)
|
|
* \see [ISDMv3, 3.4.5.1; Code- and Data-Segment Descriptor Types](intel_manual_vol3.pdf#page=98)
|
|
*/
|
|
struct {
|
|
uint64_t : 40; ///< Ignored (set via `limit_low` and `base_low`)
|
|
bool data_accessed : 1; ///< If set, the data segment was used since the last reset of this value
|
|
bool writeable : 1; ///< If set, data is writable (otherwise read only)
|
|
bool expand_down : 1; ///< Growing direction for dynamically growing segments
|
|
bool notData : 1; ///< Has to be cleared (`false`)
|
|
uint64_t : 9; ///< Ignored (set via `privilege_level` ... `available`)
|
|
uint64_t reserved : 1; ///< Reserved, always set to `0`!
|
|
|
|
/*! \brief Size of the stack pointer (`false` = 16 bit, `true` = 32 bit)
|
|
* \warning Has a different meaning in case expand_down is set to `1`.
|
|
*/
|
|
bool big : 1;
|
|
|
|
uint64_t : 0; ///< Remainder ignored
|
|
} __attribute__((packed));
|
|
|
|
uint64_t value; ///!< Merged value; useful for debugging
|
|
|
|
/*! \brief Constructor for a specific value */
|
|
constexpr SegmentDescriptor(uint64_t val = 0) : value(val) {} //NOLINT due to copy-initialization
|
|
|
|
/*! \brief Constructor for a code/data GDT entry.
|
|
* \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
|
|
*/
|
|
constexpr SegmentDescriptor(uintptr_t base, uint32_t limit, bool code, int ring, Size size) :
|
|
limit_low(limit >> (limit > 0xFFFFF ? 12 : 0) & 0xFFFF),
|
|
base_low(base & 0xFFFFFF),
|
|
type(code ? 0xA : 0x2), // code readable / non-conforming, data writeable and not expanding down
|
|
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) {}
|
|
|
|
} __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<typename T, size_t LEN>
|
|
explicit constexpr Pointer(const T (&desc)[LEN]) : limit(LEN * sizeof(T) - 1), base(const_cast<T*>(desc)) {}
|
|
|
|
/*! \brief Constructor
|
|
* \param desc Address of the GDT segment descriptors
|
|
* \param len Number of entries
|
|
*/
|
|
constexpr 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
|
|
*/
|
|
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
|