Why the gdt_flush is getting stuck?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Why the gdt_flush is getting stuck?

Post by gamingjam60 »

I want to implement TSS along with GDT so I have described the following structures

Code: Select all

struct gdt_entry
{// 128-Bit
    uint16_t limit_low;       // Lower 16 bits of the segment limit
    uint16_t base_low;        // Lower 16 bits of the base address
    uint8_t base_middle;      // Next 8 bits of the base address
    uint8_t access;           // Access byte
    uint8_t granularity;      // Flags and upper limit
    uint8_t base_high;        // Next 8 bits of the base address

    uint32_t base_upper;     // Upper 32 bits of the base address (for 64-bit TSS)
    uint32_t reserved;       // Reserved, must be zero
}__attribute__((packed, aligned(16)));
typedef struct gdt_entry gdt_entry_t;

Code: Select all

// Structure for GDTR
struct gdtr {
    uint16_t limit;
    uint64_t base; // Use uint64_t for 64-bit systems
} __attribute__((packed));
typedef struct gdtr gdtr_t;

Code: Select all

struct tss_entry{ // 576 bit
    uint32_t reserved0;
    uint64_t rsp0;  // Ring 0 Stack Pointer
    uint64_t rsp1;
    uint64_t rsp2;
    uint64_t reserved1;
    uint64_t ist1;  // Interrupt Stack Table (optional)
    uint64_t ist2;
    uint64_t ist3;
    uint64_t reserved2;
    uint16_t reserved3;
    uint16_t iopb_offset;
} __attribute__((packed, aligned(16)));
typedef struct tss_entry tss_entry_t;
The whole gdt.c code is

Code: Select all

#define STACK_SIZE 0x4000  // 16 KB
#define GDT_ENTRIES_COUNT 6

extern void gdt_flush(gdtr_t *gdtr_instance);
extern void tss_flush(uint16_t selector);

gdt_entry_t gdt_entries_bootstrap[GDT_ENTRIES_COUNT]; // GDT entries
gdtr_t gdtr_bootstrap;                // Global Descriptor Table Register
tss_entry_t tss_entry_bootstrap;

// Setup GDT entry
void gdt_setup_bootstrap(uint8_t idx, uint64_t base, uint32_t limit, uint8_t access, uint8_t granularity) {
    gdt_entries_bootstrap[idx].limit_low    = limit & 0xFFFF;          // Lower 16 bits of limit
    gdt_entries_bootstrap[idx].base_low     = base & 0xFFFF;           // Lower 16 bits of base
    gdt_entries_bootstrap[idx].base_middle  = (base >> 16) & 0xFF;     // Middle 8 bits of base
    gdt_entries_bootstrap[idx].access       = access;                  // Access byte
    gdt_entries_bootstrap[idx].granularity  = (limit >> 16) & 0x0F;   // Upper 4 bits of limit
    gdt_entries_bootstrap[idx].granularity |= granularity & 0xF0;     // Flags
    gdt_entries_bootstrap[idx].base_high    = (base >> 24) & 0xFF;    // Next 8 bits of base
    gdt_entries_bootstrap[idx].base_upper   = (base >> 32) & 0xFFFFFFFF; // Upper 32 bits of base
    gdt_entries_bootstrap[idx].reserved     = 0;                       // Reserved, set to 0

    printf("GDT Entry %d: Base: %x, Limit: %x, Access: %x, Granularity: %x\n", idx, base, limit, access, granularity);
}

// Initialize GDT for Bootstrap CPU
void init_gdt_bootstrap_cpu() {
    gdt_setup_bootstrap(0, 0, 0x0,    0x0,  0x0);     // Null descriptor selector : 0x0
    gdt_setup_bootstrap(1, 0, 0xFFFF, 0x9A, 0xA0);    // Kernel mode code segment, selector : 0x8
    gdt_setup_bootstrap(2, 0, 0xFFFF, 0x92, 0xA0);    // Kernel mode data segment, selector : 0x10
    gdt_setup_bootstrap(3, 0, 0xFFFF, 0xFA, 0xA0);    // User mode code segment, selector : 0x18 
    gdt_setup_bootstrap(4, 0, 0xFFFF, 0xF2, 0xA0);    // User mode data segment, selector : 0x20

    // Calculate the GDT limit and base address
    gdtr_bootstrap.limit = (uint16_t) (GDT_ENTRIES_COUNT * sizeof(gdt_entry_t) - 1);
    gdtr_bootstrap.base  = (uint64_t) &gdt_entries_bootstrap;

    printf("Loading GDT: Base: %x, Limit: %x\n", gdtr_bootstrap.base, gdtr_bootstrap.limit);

    // Load the new GDT
    gdt_flush((gdtr_t *) &gdtr_bootstrap);

    printf("Successfully GDT initialized for Bootstrap CPU.\n");
}

// Setup TSS entry in GDT
void tss_setup_bootstrap(uint8_t idx, uint64_t base, uint32_t limit, uint8_t access, uint8_t granularity) {
    gdt_entries_bootstrap[idx].limit_low = limit & 0xFFFF;
    gdt_entries_bootstrap[idx].base_low = base & 0xFFFF;
    gdt_entries_bootstrap[idx].base_middle = (base >> 16) & 0xFF;
    gdt_entries_bootstrap[idx].access = access;

    gdt_entries_bootstrap[idx].granularity = (limit >> 16) & 0x0F;
    gdt_entries_bootstrap[idx].granularity |= (granularity & 0xF0);

    gdt_entries_bootstrap[idx].base_high = (base >> 24) & 0xFF;
    gdt_entries_bootstrap[idx].base_upper = (base >> 32) & 0xFFFFFFFF;
    gdt_entries_bootstrap[idx].reserved = 0;

    printf("TSS Entry: Base: %x, Limit: %x\n", base, limit);
    printf("TSS Entry: %x %x %x %x %x %x %x %x\n",
        gdt_entries_bootstrap[idx].limit_low,
        gdt_entries_bootstrap[idx].base_low,
        gdt_entries_bootstrap[idx].base_middle,
        gdt_entries_bootstrap[idx].access,
        gdt_entries_bootstrap[idx].granularity,
        gdt_entries_bootstrap[idx].base_high,
        gdt_entries_bootstrap[idx].base_upper,
        gdt_entries_bootstrap[idx].reserved);
}

// Initialize TSS for Bootstrap CPU
void init_tss_bootstrap_cpu() {
    printf("Starting TSS setup\n");
    // Clear the TSS structure
    memset(&tss_entry_bootstrap, 0, sizeof(tss_entry_t));

    // Allocate a kernel stack for Ring 0
    tss_entry_bootstrap.rsp0 = (uint64_t) kmalloc_a(STACK_SIZE, true);
    if (!tss_entry_bootstrap.rsp0) {
        printf("Error: Failed to allocate kernel stack for TSS.\n");
        return;
    }

    // Set I/O Permission Bitmap offset (no I/O bitmap)
    tss_entry_bootstrap.iopb_offset = sizeof(tss_entry_t);

    // Add the TSS to GDT (selector 0x28)
    tss_setup_bootstrap(5, (uint64_t)&tss_entry_bootstrap, sizeof(tss_entry_t) - 1, 0x89, 0x00);

    // Load the TSS into the CPU's TR register
    tss_flush(0x28);

    printf("Successfully TSS initialized for Bootstrap CPU.\n");
    printf("Kernel stack allocated at: %x\n", tss_entry_bootstrap.rsp0);
}



// Initialize GDT and TSS for Bootstrap CPU
void start_bootstrap_gdt_tss() {
    // Setting GDTs
    gdt_setup_bootstrap(0, 0, 0x0,    0x0,  0x0);     // Null descriptor selector : 0x0
    gdt_setup_bootstrap(1, 0, 0xFFFF, 0x9A, 0xA0);    // Kernel mode code segment, selector : 0x8
    gdt_setup_bootstrap(2, 0, 0xFFFF, 0x92, 0xA0);    // Kernel mode data segment, selector : 0x10
    gdt_setup_bootstrap(3, 0, 0xFFFF, 0xFA, 0xA0);    // User mode code segment, selector : 0x18 
    gdt_setup_bootstrap(4, 0, 0xFFFF, 0xF2, 0xA0);    // User mode data segment, selector : 0x20

    // Setting TSS
    memset(&tss_entry_bootstrap, 0, sizeof(tss_entry_t));
    // Allocate a kernel stack for Ring 0
    tss_entry_bootstrap.rsp0 = (uint64_t) kmalloc_a(STACK_SIZE, true);
    if (!tss_entry_bootstrap.rsp0) {
        printf("Error: Failed to allocate kernel stack for TSS.\n");
        return;
    }
    // Set I/O Permission Bitmap offset (no I/O bitmap)
    tss_entry_bootstrap.iopb_offset = sizeof(tss_entry_t);
    // Add the TSS to GDT (selector 0x28)
    tss_setup_bootstrap(5, (uint64_t)&tss_entry_bootstrap, sizeof(tss_entry_t) - 1, 0x89, 0x00);

    gdtr_bootstrap.limit = (uint16_t) (GDT_ENTRIES_COUNT * sizeof(gdt_entry_t)) - 1; // 6*16 - 1 = 95 bytes
    gdtr_bootstrap.base = (uint64_t) &gdt_entries_bootstrap;  // 0xFFFFFFFF80459F80                  

    for (int i = 0; i < GDT_ENTRIES_COUNT; i++) {
        printf("GDT Entry %d: %x %x %x\n", i, 
               *(uint64_t*)&gdt_entries_bootstrap[i], 
               *((uint64_t*)&gdt_entries_bootstrap[i] + 1),
               *((uint64_t*)&gdt_entries_bootstrap[i] + 1));
    }

    
    printf("limit: %d\n", gdtr_bootstrap.limit);
    printf("base: %x\n", gdtr_bootstrap.base);
    gdt_flush((gdtr_t *) &gdtr_bootstrap);
    printf("gdt flush completed.\n");
    tss_flush(0x28);
    printf("tss flush completed.\n");

    printf("Successfully started GDT and TSS for Bootstrap CPU.\n");
}
and below whole assembly code for gdt.asm is

Code: Select all

%define KERNEL_CODE 0x08
%define KERNEL_DATA 0x10



section .text
global gdt_flush
global reloadSegments
global tss_flush

gdt_flush:
   cli
   LGDT  [RDI]
   jmp reloadSegments
   sti
   RET


reloadSegments:
   ; Reload CS register:
   PUSH KERNEL_CODE          ; Push code segment to stack, 0x08 is a stand-in for kernel code segment
   LEA RAX, [rel reload_CS]  ; Load address of reload_CS into RAX, LEA (Load Effective Address), 
   PUSH RAX                  ; Push this value to the stack
   RETFQ                     ; Perform a far return, RETFQ or LRETQ depending on syntax


reload_CS:
   ; Reload data segment registers
   MOV   AX, KERNEL_DATA ; 0x10 is a stand-in for kernel data segment
   MOV   DS, AX
   MOV   ES, AX
   MOV   FS, AX
   MOV   GS, AX
   MOV   SS, AX
   RET


tss_flush:
   mov ax, di      ; Move the selector into AX (DI is used to pass the argument in x86_64 System V ABI)
   ltr ax          ; Load Task Register with the selector in AX
   ret
I am getting following output:

Code: Select all

GDT Entry 0: Base: 0x0, Limit: 0x0, Access: 0x0, Granularity: 0x0
GDT Entry 1: Base: 0x0, Limit: 0xFFFF, Access: 0x9A, Granularity: 0xA0
GDT Entry 2: Base: 0x0, Limit: 0xFFFF, Access: 0x92, Granularity: 0xA0
GDT Entry 3: Base: 0x0, Limit: 0xFFFF, Access: 0xFA, Granularity: 0xA0
GDT Entry 4: Base: 0x0, Limit: 0xFFFF, Access: 0xF2, Granularity: 0xA0
TSS Entry: Base: 0xFFFFFFFF8045A000, Limit: 0x4F
TSS Entry: 0x4F 0xA000 0x45 0x89 0x0 0x80 0xFFFFFFFF 0x0
GDT Entry 0: 0x0 0x0 0x0
GDT Entry 1: 0xA09A000000FFFF 0x0 0x0
GDT Entry 2: 0xA092000000FFFF 0x0 0x0
GDT Entry 3: 0xA0FA000000FFFF 0x0 0x0
GDT Entry 4: 0xA0F2000000FFFF 0x0 0x0
GDT Entry 5: 0x80008945A000004F 0xFFFFFFFF 0xFFFFFFFF
limit: 95
base: 0xFFFFFFFF80459F80
Which is showing gdt_flush never run.
I need help to resolve this issue. I have checked 64 bit gdt_entries without TSS worked fine with same gdt_flush but not in this case.
MichaelPetch
Member
Member
Posts: 829
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Why the gdt_flush is getting stuck?

Post by MichaelPetch »

Well for one, GDT entries in 64-bit mode are 64-bits EXCEPT for the system segment entries (TSS and LDT entries) which are 128-bit.
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Re: Why the gdt_flush is getting stuck?

Post by gamingjam60 »

MichaelPetch wrote: Fri Mar 14, 2025 11:22 am Well for one, GDT entries in 64-bit mode are 64-bits EXCEPT for the system segment entries (TSS and LDT entries) which are 128-bit.
Yes as here I want to use TSS so I have implemented 128 bit gdt_entry_t. If I am right that you are saying this.
Whatever I pushed the latest code into my GitHub repository. Can you please look it?
Last edited by gamingjam60 on Fri Mar 14, 2025 2:44 pm, edited 1 time in total.
MichaelPetch
Member
Member
Posts: 829
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Why the gdt_flush is getting stuck?

Post by MichaelPetch »

64-bit GDT entries need to be 64-bit. The system segments (TSS or LDT) need to be 128-bits. You can't make them all 128-bit as that will mess up the 64-bit entries. It is easiest to treat GDT entries as 64-bit entries. For the TSS (or LDT - you don't have an LDT I realize that) system segment treat that as 2 consecutive 64-bit entries in the GDT (totaling 128-bit). When I have a chance later I can look at your repo.
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Re: Why the gdt_flush is getting stuck?

Post by gamingjam60 »

MichaelPetch wrote: Fri Mar 14, 2025 2:01 pm 64-bit GDT entries need to be 64-bit. The system segments (TSS or LDT) need to be 128-bits. You can't make them all 128-bit as that will mess up the 64-bit entries. It is easiest to treat GDT entries as 64-bit entries. For the TSS (or LDT - you don't have an LDT I realize that) system segment treat that as 2 consecutive 64-bit entries in the GDT (totaling 128-bit). When I have a chance later I can look at your repo.

So you are saying I need to change

Code: Select all

struct gdt_entry
{// 128-Bit
    uint16_t limit_low;       // Lower 16 bits of the segment limit
    uint16_t base_low;        // Lower 16 bits of the base address
    uint8_t base_middle;      // Next 8 bits of the base address
    uint8_t access;           // Access byte
    uint8_t granularity;      // Flags and upper limit
    uint8_t base_high;        // Next 8 bits of the base address

    uint32_t base_upper;     // Upper 32 bits of the base address (for 64-bit TSS)
    uint32_t reserved;       // Reserved, must be zero
}__attribute__((packed, aligned(16)));
typedef struct gdt_entry gdt_entry_t;
Into new 64 bit

Code: Select all


struct gdt_entry
{// 64-Bit
    uint16_t limit_low;       // Lower 16 bits of the segment limit
    uint16_t base_low;        // Lower 16 bits of the base address
    uint8_t base_middle;      // Next 8 bits of the base address
    uint8_t access;           // Access byte
    uint8_t granularity;      // Flags and upper limit
    uint8_t base_high;        // Next 8 bits of the base address
}__attribute__((packed, aligned(16)));
typedef struct gdt_entry gdt_entry_t;
Am I right?
Octocontrabass
Member
Member
Posts: 5754
Joined: Mon Mar 25, 2013 7:01 pm

Re: Why the gdt_flush is getting stuck?

Post by Octocontrabass »

gamingjam60 wrote: Fri Mar 14, 2025 10:31 am

Code: Select all

    gdt_setup_bootstrap(1, 0, 0xFFFF, 0x9A, 0xA0);    // Kernel mode code segment, selector : 0x8
    gdt_setup_bootstrap(2, 0, 0xFFFF, 0x92, 0xA0);    // Kernel mode data segment, selector : 0x10
    gdt_setup_bootstrap(3, 0, 0xFFFF, 0xFA, 0xA0);    // User mode code segment, selector : 0x18 
    gdt_setup_bootstrap(4, 0, 0xFFFF, 0xF2, 0xA0);    // User mode data segment, selector : 0x20
Segment selectors contain the index into a list of eight-byte segment descriptors. Your gdt_entry_t is sixteen bytes long, which means each gdt_entry_t contains two descriptors. None of these selectors point to the descriptor you expect because you have a bunch of extra descriptors in your GDT.
MichaelPetch
Member
Member
Posts: 829
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Why the gdt_flush is getting stuck?

Post by MichaelPetch »

I submitted a pull request to simplify the code in gdt.c and gdt.h removing most of the code duplication. Since you are using C11, I used a union with anonymous structs to define `gdt_entry_t` in an effort make the code that creates the system segment descriptors (ie: TSS and LDT) easier to maintain. See: https://github.com/baponkar/KeblaOS/pull/12
Post Reply