Skip to content

Segment registers (CS, DS, ES, SS, FS, GS) not reloaded with new values after LGDT instruction #44

@mpetch

Description

@mpetch

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions