The code in GlobalDescriptorTable::GlobalDescriptorTable() fails to reload the segment registers after creating a GDT and loading it with lgdt. As a result, the code expects that GRUB's values for the code and data segment selectors are the same as the OS. While the multiboot spec guarantees that the segment registers are loaded with flat descriptors with a maximum limit, the actual selector values can't be assumed to be specific values. In function GlobalDescriptorTable::GlobalDescriptorTable() in gdt.cpp change:
asm volatile("lgdt (%0)": :"p" (((uint8_t *) i)+2));
to:
asm volatile(
"lgdt %0\n"
"mov %2, %%ds\n" /* Set segments to the data selector */
"mov %2, %%es\n"
"mov %2, %%fs\n"
"mov %2, %%gs\n"
"mov %2, %%ss\n"
"push %1\n" /* Use Far Return to set CS:EIP */
"push $1f\n"
"retf\n"
"1:\n" /* Far return returns to this location */
:
: "m" (*(((uint8_t *) i)+2)),
"r"(CodeSegmentSelector()),
"r"(DataSegmentSelector())
: "memory"
);
Without this change, running QEMU with the -cdrom option may work but with -kernel it may not. QEMU isn't buggy in this regard but WYOOS is.
The code in
GlobalDescriptorTable::GlobalDescriptorTable()fails to reload the segment registers after creating a GDT and loading it withlgdt. As a result, the code expects that GRUB's values for the code and data segment selectors are the same as the OS. While the multiboot spec guarantees that the segment registers are loaded with flat descriptors with a maximum limit, the actual selector values can't be assumed to be specific values. In functionGlobalDescriptorTable::GlobalDescriptorTable()in gdt.cpp change:to:
Without this change, running QEMU with the
-cdromoption may work but with-kernelit may not. QEMU isn't buggy in this regard but WYOOS is.