cassemblyx86osdevgdt

Why do the x86 32bit protected mode register not giving right value?


I have set 32 bit protected mode and try to load Global Descriptor table(GDT) by C and assembly and try to print the different registers value by check_gdt() function where

    void check_gdt() {
    uint16_t cs, ds, es, fs, gs, ss;

    // Inline assembly to get the values of segment registers
    asm volatile("mov %%cs, %0" : "=r"(cs));    
    asm volatile("mov %%ds, %0" : "=r"(ds));
    asm volatile("mov %%es, %0" : "=r"(es));
    asm volatile("mov %%fs, %0" : "=r"(fs));
    asm volatile("mov %%gs, %0" : "=r"(gs));
    asm volatile("mov %%ss, %0" : "=r"(ss));

    // Print the segment register values
    printf("CS: 0x%x\n", cs);
    printf("DS: 0x%x\n", ds);
    printf("ES: 0x%x\n", es);
    printf("FS: 0x%x\n", fs);
    printf("GS: 0x%x\n", gs);
    printf("SS: 0x%x\n", ss);
}

Initialize GDT by following

void initGdt(){
    gdt_ptr.limit = (sizeof(struct gdt_entry_struct) * 5) - 1;
    gdt_ptr.base = (uint32_t)&gdt_entries;

    setGdtGate(0,0,0,0,0);                      // Null segment                           _________________                   _______________
                                                // Explanation:                          |P|DPL|S|E|DC|RW|A|                 |G|DB|L|Res|    |
    setGdtGate(1,0,0xFFFFFFFF, 0x9A, 0xCF);     // Kernel code segment : access = 0x9A =>|1|00 |1|1|0 |1 |0|, gran = 0xCF => |1|1 |0|0  |1111|
    setGdtGate(2,0,0xFFFFFFFF, 0x92, 0xCF);     // Kernel data segment : access = 0x92 =>|1|00 |1|0|0 |1 |0|, gran = 0xCF => |1|1 |0|0  |1111|                  
                                                //                                        _________________                   _______________
    setGdtGate(3,0,0xFFFFFFFF, 0xFA, 0xCF);     // User code segment   : access = 0xFA =>|1|11 |1|1|0 |1 |0|, gran = 0xCF => |1|1 |0|0  |1111|
    setGdtGate(4,0,0xFFFFFFFF, 0xF2, 0xCF);     // User data segment   : access = 0x9A =>|1|11 |1|0|0 |1 |0|, gran = 0xCF => |1|1 |0|0  |1111|
                                                //                                        -----------------                   ---------------
    gdt_flush((uint32_t) &gdt_ptr);
}



void setGdtGate(uint32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran){

    gdt_entries[num].base_low = (base & 0xFFFF);        // Assign lower base 16 bit
    gdt_entries[num].base_middle = (base >> 16) & 0xFF; // Assign middle base 8 bit
    gdt_entries[num].base_high = (base >> 24) & 0xFF;   // Assign higher base 8 bit

    gdt_entries[num].limit_low = (limit & 0xFFFF);      // Assign lower limit 16 bit
    gdt_entries[num].flags = (limit >> 16) & 0x0F;      // Assign flags lower 4 bit
    gdt_entries[num].flags |= (gran & 0xF0);            // Assign flags upper 4 bit

    gdt_entries[num].access = access;                   // Assign access 8 bit
}

and loading GDT in CPU by gdt_flush function

gdt_flush:
    ; [esp] Return address (from CALL)
    ; Load the address of the GDT descriptor
    MOV eax, [esp + 4]  ; To load the content of [esp+4] address which is the first argument of gdt_flush((uint32_t) &gdt_ptr)
    LGDT [eax]      ; Load the GDT into the CPU

    MOV ax, 0x10    ; Load kernel data segment selector (0x10)
    MOV ds, ax      ; Set data segment register to kernel data segment
    MOV es, ax      ; Set extra segment register to kernel data segment
    MOV fs, ax      ; Set general-purpose segment to kernel data segment
    MOV gs, ax      ; Set general-purpose segment to kernel data segment
    MOV ss, ax      ; Set stack segment to kernel data segment

    JMP 0x08:.flush ;Far jump to selector 0x08 (code segment) and the address of flush
                    ; Entry 1: Kernel code segment (selector = 0x08)

.flush:
    RET

I am expecting from above gdt loading

CS : 0x08
DS : 0x10
ES : 0X10
FS : 0X10
GS : 0X10
SS : 0X10

But why do I am getting wrong result like below? enter image description here


Solution

  • Yes @Jester and @ Michael Petch both are right my printf function was broken.

    I am using print_hex(cs) ... works fine where print_hex in vga.c is

        // Outputs a hexadecimal number to the screen.
    void print_hex(uint32_t n) {
        char hex_chars[] = "0123456789ABCDEF";
        print("0x");
    
        for (int i = 28; i >= 0; i -= 4) // process each nibble (4 bits)
        {
            putchar(hex_chars[(n >> i) & 0xF]);
        }
    }