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
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]);
}
}