Files
vystem/docs/shelter/irq/gdt.md
2026-05-27 19:34:54 +02:00

3.7 KiB

GDT

The GDT management is defined in shelter/lib/include/irq/gdt.h and implemented in shelter/lib/include/irq/gdt.c.

64 bits GDT

The 64 bits GDT can contains two types of entries:

  • sh_gdt_GDT_ENTRY_64: standard GDT entry
  • sh_gdt_GDT_ENTRY_128: special 128 bits GDT entry for TSS

The standard 64 bits GDT used during the boot process is contained in this structure:

#pragma pack(1)
typedef struct {
  sh_gdt_GDT_ENTRY_64 null;
  sh_gdt_GDT_ENTRY_64 kernel_code;
  sh_gdt_GDT_ENTRY_64 kernel_data;
  sh_gdt_GDT_ENTRY_64 user_code;
  sh_gdt_GDT_ENTRY_64 user_data;
  sh_gdt_GDT_ENTRY_128 tss;
} sh_gdt_GDT;
#pragma pack()

We also use this struct for generating the 10 bytes to use with lgdt:

#pragma pack(1)
typedef struct {
  sh_uint16 limit;
  sh_uint64 base;
} sh_gdt_GDTR;
#pragma pack()

Regarding the API, we provide the following functions:

  • sh_gdt_fill_access_byte(sh_bool present,sh_uint8 dpl,sh_bool descriptor_type,sh_uint8 segment_type): generate access byte
  • sh_gdt_fill_flags_byte(sh_bool g,sh_bool db,sh_bool l,sh_bool avl): generate flags byte
  • sh_gdt_fill_gdt_entry_64(sh_uint32 limit,sh_uint32 base,sh_uint8 access,sh_uint8 flags): fill a 64 bits GDT entry, expect flags as sh_gdt_fill_flags_byte would return it
  • sh_gdt_fill_gdt_entry_128(sh_uint32 limit,sh_uint64 base,sh_uint8 access,sh_uint8 flags): fill a 128 bits GDT entry, expect flags as sh_gdt_fill_flags_byte would return it
  • sh_gdt_fill_gdt(sh_tss_TSS *tss,sh_gdt_GDT *gdt): fill the standard 64 bits GDT with the provided TSS
  • sh_gdt_load_gdtr(sh_gdt_GDT *gdt): load the standard 64 bits GDT using lgdt
  • sh_gdt_reload_registers(): an ASM stub to reload segment registers

All these functions are intended to be used with the standard 64 bits GDT defined with sh_gdt_GDT, which should only be used on the bootstrap CPU

APs GDT

The APs use a special GDT containing sufficient spaces for the TSS entries of all APs.

For that we use a special shared 64 bits GDT between all APs, defined in this struct:

#pragma pack(1)
typedef struct {
  sh_gdt_GDT_ENTRY_64 null;
  sh_gdt_GDT_ENTRY_64 kernel_code;
  sh_gdt_GDT_ENTRY_64 kernel_data;
  sh_gdt_GDT_ENTRY_64 user_code;
  sh_gdt_GDT_ENTRY_64 user_data;
  sh_gdt_GDT_ENTRY_128 tss[256];
} sh_gdt_GDT_AP;
#pragma pack()

The APs GDT is generated using the function sh_gdt_fill_gdt_ap(sh_tss_TSS *tss,sh_uint64 tss_count,sh_gdt_GDT_AP *gdt). This allows to fill a GDT with up to 256 TSS entries. The GDTR for this shared GDT is generated using sh_gdt_make_gdtr_ap(sh_gdt_GDT_AP *gdt).

32 bits GDT

In order for the APs to exit 16 bits mode, they need a 32 bits GDT. Here is the structure for a 32 bits GDT entry:

#pragma pack(1)
typedef struct {
  sh_uint16 limit_low;
  sh_uint16 base_low;
  sh_uint8 base_middle;
  sh_uint8 access;
  sh_uint8 granularity;
  sh_uint8 base_high;
} sh_gdt_GDT_ENTRY_32;
#pragma pack()

The 32 bits GDT used to exit 16 bits mode is structured like this:

#pragma pack(1)
typedef struct {
  sh_gdt_GDT_ENTRY_32 null;
  sh_gdt_GDT_ENTRY_32 code_64;
  sh_gdt_GDT_ENTRY_32 code;
  sh_gdt_GDT_ENTRY_32 data;
} sh_gdt_GDT_32;
#pragma pack()

The 32 bits GDT implementation provides the following functions:

  • sh_gdt_fill_access_byte_32(sh_bool present,sh_uint8 dpl,sh_bool descriptor_type,sh_uint8 segment_type): generate access byte
  • sh_gdt_fill_granularity_byte_32(sh_bool granularity,sh_bool db,sh_bool l,sh_bool avl): generate granularity byte
  • sh_gdt_fill_gdt_entry_32(sh_uint32 limit,sh_uint32 base,sh_uint8 access,sh_uint8 granularity): fill a GDT entry for a 32 bits GDT, expect flags as sh_gdt_fill_flags_byte_32 would return it
  • sh_gdt_fill_gdt_32(sh_gdt_GDT_32 *gdt_32): fill the 32 bits GDT structure