Page 1 of 1

[SOLVED] Triple-fault on loading a GDT

Posted: Wed Apr 19, 2017 3:10 pm
by isaacwoods
I am trying to load a GDT. I have an array of entries, with each entry created like so:

Code: Select all

static uint64_t gdtEntries[NUM_GDT_ENTRIES];
static struct gdt_pointer
{
  uint16_t limit;
  uint32_t firstEntryAddr;
} gdtPtr;

static uint64_t CreateGDTEntry(uint32_t base, uint32_t limit, uint16_t type)
{
  uint64_t entry = 0u;
  entry = limit & 0x000F0000;
  entry |= (type << 8u) & 0x00F0FF00;
  entry |= (base >> 16u) & 0x000000FF;
  entry |= base & 0xFF000000;

  entry <<= 32u;

  entry |= base << 16u;
  entry |= limit & 0x0000FFFF;

  return entry;
}

extern void FlushGDT(uint32_t);
void InitGDT()
{
  gdtPtr.limit = sizeof(uint64_t) * NUM_GDT_ENTRIES;
  gdtPtr.firstEntryAddr = (uint32_t)&gdtEntries;

  gdtEntries[0u] = CreateGDTEntry(0u, 0u, 0u);            // Null segment
  gdtEntries[1u] = CreateGDTEntry(0u, 0xFFFFFFFF, 0x9A);  // Kernel code segment
  gdtEntries[2u] = CreateGDTEntry(0u, 0xFFFFFFFF, 0x92);  // Kernel data segment

  FlushGDT((uint32_t)&gdtPtr);
}
I then load the GDT and switch to the new code and data segment like so:

Code: Select all

; Inputs: Physical address of GDT to be loaded
FlushGDT:
  mov eax, [esp+4]
  lgdt [eax]

  ; We can now switch to the new data selectors (0x10 is the selector for the new kernel data segment)
  mov ax, 0x10
  mov ds, ax
  mov es, ax
  mov fs, ax
  mov gs, ax
  mov ss, ax

  ; We now do a far jump to the new kernel code segment (0x08 is the selector for the new code segment)
  jmp 0x08:.flush
.flush:
  ret
It triple faults, but not where I thought it would (the far jump). Instead, it seems to start executing some random code after `mov ds, ax` (which is weird because I haven't changed the CS yet??). I usually try to be fairly self-sufficient with debugging, but I can't find the relevant part of the Intel manual and the wiki seems to have slightly conflicting methods. I would appreciate some pointers in what I've done wrong; thanks in advance!

Re: Triple-fault on loading a GDT

Posted: Wed Apr 19, 2017 3:32 pm
by zaval
this

Code: Select all

static struct gdt_pointer
{
  uint16_t limit;
  uint32_t firstEntryAddr;
} gdtPtr;
I am not into x86 now, but this structure won't be a 6-byte entity expected by lgdt instruction. Because of alignment. No? Make it packed or better define some union.

Re: Triple-fault on loading a GDT

Posted: Thu Apr 20, 2017 2:51 am
by isaacwoods
Okay, so I made that structure packed, and also realised I wasn't subtracting one from the size of the table, so I changed that line to:

Code: Select all

gdtPtr.limit = (sizeof(uint64_t) * NUM_GDT_ENTRIES) - 1u;
The GDT is now loaded correctly and DS,ES,FS,GS and SS are all set to 0x10. However, on the far jump (`jmp 0x08:.flush`), CS is set to 0xf000 and it starts executing code at 0xe05b (.flush is at 0x10022c), causing a triple-fault.
Thanks for the help anyways!

Re: Triple-fault on loading a GDT

Posted: Thu Apr 20, 2017 3:17 am
by Octocontrabass
BaconWraith wrote:However, on the far jump (`jmp 0x08:.flush`), CS is set to 0xf000 and it starts executing code at 0xe05b (.flush is at 0x10022c), causing a triple-fault.
The far jump to 0xF000:E05B is the result of a triple-fault, not the cause of it.

It looks like the triple fault is caused because your code segment descriptor is invalid. Are you sure you're calling CreateGDTEntry with the correct arguments?

Re: Triple-fault on loading a GDT

Posted: Thu Apr 20, 2017 3:56 am
by isaacwoods
Okay, I've been dissecting the actual GDT entries and found I hadn't set the Sz bit of the flags, so I guess it was trying to use 16-bit addresses. It now works, so thank you very much and sorry for the oversight.