You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
7.1 KiB
C++
200 lines
7.1 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 = 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 <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
|
|
*/
|
|
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
|