Skip to content

Instantly share code, notes, and snippets.

@SamJakob
Last active August 10, 2021 07:40
Show Gist options
  • Save SamJakob/650557c47b257a108f2fd0642ccaf629 to your computer and use it in GitHub Desktop.
Save SamJakob/650557c47b257a108f2fd0642ccaf629 to your computer and use it in GitHub Desktop.
GDT Descriptor Macro

GDT Descriptor Macro

See: https://wiki.osdev.org/GDT_Tutorial#Some_stuff_to_make_your_life_easy

This is a preprocessor macro version of the function shown on the page for creating x64 descriptor table entries in 64-bit integer form.

Accompanied, is a test implementation file that additionally compares the two implementations.

/**
 * @brief Computes a GDT Descriptor struct from human-readable parameters.
 * @version 1.1
 */
#define gdtd(base, limit, flag) {                                                     \
    .limit15_0                  = (((uint32_t)limit)  & 0x0000FFFF),                  \
    .base15_0                   = (((uint32_t)base)   & 0x0000FFFF),                  \
                                                                                      \
    .base23_16                  = (char) ((((uint32_t)base) >> 16) & 0x000000FF),     \
    .type                       = (char) (((uint16_t)flag) & 0x000000FF),             \
                                                                                      \
    .limit19_16_and_flags       = ((((uint32_t)limit) >> 16) & 0x0000000F) |          \
                                    (uint8_t) (((uint16_t)flag)   >> 8),              \
    .base31_24                  = (char) ((((uint32_t)base)  >> 24) & 0x000000FF)     \
}


#pragma pack (1)

struct gdt_descriptor {
    uint16_t limit15_0;
    uint16_t base15_0;

    uint8_t base23_16;
    uint8_t type;

    uint8_t limit19_16_and_flags;
    uint8_t base31_24;
};

#pragma pack ()
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
// Each define here is for a specific flag in the descriptor.
// Refer to the intel documentation for a description of what each one does.
#define SEG_DESCTYPE(x) ((x) << 0x04) // Descriptor type (0 for system, 1 for code/data)
#define SEG_PRES(x) ((x) << 0x07) // Present
#define SEG_SAVL(x) ((x) << 0x0C) // Available for system use
#define SEG_LONG(x) ((x) << 0x0D) // Long mode
#define SEG_SIZE(x) ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32)
#define SEG_GRAN(x) ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB)
#define SEG_PRIV(x) (((x) & 0x03) << 0x05) // Set privilege level (0 - 3)
#define SEG_DATA_RD 0x00 // Read-Only
#define SEG_DATA_RDA 0x01 // Read-Only, accessed
#define SEG_DATA_RDWR 0x02 // Read/Write
#define SEG_DATA_RDWRA 0x03 // Read/Write, accessed
#define SEG_DATA_RDEXPD 0x04 // Read-Only, expand-down
#define SEG_DATA_RDEXPDA 0x05 // Read-Only, expand-down, accessed
#define SEG_DATA_RDWREXPD 0x06 // Read/Write, expand-down
#define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed
#define SEG_CODE_EX 0x08 // Execute-Only
#define SEG_CODE_EXA 0x09 // Execute-Only, accessed
#define SEG_CODE_EXRD 0x0A // Execute/Read
#define SEG_CODE_EXRDA 0x0B // Execute/Read, accessed
#define SEG_CODE_EXC 0x0C // Execute-Only, conforming
#define SEG_CODE_EXCA 0x0D // Execute-Only, conforming, accessed
#define SEG_CODE_EXRDC 0x0E // Execute/Read, conforming
#define SEG_CODE_EXRDCA 0x0F // Execute/Read, conforming, accessed
#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_CODE_EXRD
#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_DATA_RDWR
#define GDT_CODE_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(3) | SEG_CODE_EXRD
#define GDT_DATA_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(3) | SEG_DATA_RDWR
/**
* @brief Computes a GDT Descriptor struct from human-readable parameters.
* @version 1.1
*/
#define gdtd(base, limit, flag) { \
.limit15_0 = (((uint32_t)limit) & 0x0000FFFF), \
.base15_0 = (((uint32_t)base) & 0x0000FFFF), \
\
.base23_16 = (char) ((((uint32_t)base) >> 16) & 0x000000FF), \
.type = (char) (((uint16_t)flag) & 0x000000FF), \
\
.limit19_16_and_flags = ((((uint32_t)limit) >> 16) & 0x0000000F) | \
(uint8_t) (((uint16_t)flag) >> 8), \
.base31_24 = (char) ((((uint32_t)base) >> 24) & 0x000000FF) \
}
#pragma pack (1)
struct gdt_descriptor {
uint16_t limit15_0;
uint16_t base15_0;
uint8_t base23_16;
uint8_t type;
uint8_t limit19_16_and_flags;
uint8_t base31_24;
};
#pragma pack ()
long long unsigned int
create_descriptor(uint32_t base, uint32_t limit, uint16_t flag)
{
long long unsigned int descriptor;
// Create the high 32 bit segment
descriptor = limit & 0x000F0000; // set limit bits 19:16
descriptor |= (flag << 8) & 0x00F0FF00; // set type, p, dpl, s, g, d/b, l and avl fields
descriptor |= (base >> 16) & 0x000000FF; // set base bits 23:16
descriptor |= base & 0xFF000000; // set base bits 31:24
// Shift by 32 to allow for low part of segment
descriptor <<= 32;
// Create the low 32 bit segment
descriptor |= base << 16; // set base bits 15:0
descriptor |= limit & 0x0000FFFF; // set limit bits 15:0
printf("0x%.16llX\n", descriptor);
return descriptor;
}
int main(void) {
uint32_t base = 0xFF002040;
uint32_t limit = 0x0F000FFF;
uint16_t flag = GDT_CODE_PL3;
printf("Theirs:\r\n");
long long unsigned int theirDescValue = create_descriptor(base, limit, flag);
printf("\r\n");
printf("Mine:\r\n");
struct gdt_descriptor myDesc = gdtd(base, limit, flag);
long long unsigned int myDescValue = *((long long unsigned int*) &myDesc);
printf("0x%.16llX\n", myDescValue);
if (myDescValue == theirDescValue) {
printf("\r\nIt's a match!\r\n");
} else printf("\r\nFail!\r\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment