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
 |