A couple of strange things in the GDT. Your GDT structure is 9 bytes in size rather than 8 bytes. You have this for SegmentDescriptor:
Code:
class SegmentDescriptor
{
private:
uint16_t limit_low;
uint16_t base_low;
uint8_t base_high;
uint8_t access;
uint8_t limit_high;
uint8_t flags;
uint8_t base_vhi;
public:
SegmentDescriptor(uint32_t base, uint32_t limit, uint8_t access);
uint32_t Base();
uint32_t Limit();
} __attribute__((packed));
When it should be:
Code:
class SegmentDescriptor
{
private:
uint16_t limit_low;
uint16_t base_low;
uint8_t base_high;
uint8_t access;
uint8_t limit_high;
uint8_t base_vhi;
public:
SegmentDescriptor(uint32_t base, uint32_t limit, uint8_t access);
uint32_t Base();
uint32_t Limit();
} __attribute__((packed));
I've removed
flags in your GlobalDescriptorTable constructor and you seem to have the size and the address mixed up (and the inline assembly is a problematic). A serious problem is that after the LGDT instruction they never loaded the segment registers with proper values. The way they did it may work by chance on some environments and not others. You had:
Code:
GlobalDescriptorTable::GlobalDescriptorTable():
nullSegmentSelector(0,0,0),
freeSegmentSelector(0,0,0),
codeSegmentSelector(0,64*1024*1024,0x9A),
dataSegmentSelector(0,64*1024*1024,0x92)
{
uint32_t i[2];
i[0] = (uint32_t)this;
i[1] = sizeof(GlobalDescriptorTable) << 16;
asm volatile("lgdt (%0)": : "p" (((uint8_t *) i)+2));
}
It should probably look something like:
Code:
GlobalDescriptorTable::GlobalDescriptorTable():
nullSegmentSelector(0,0,0),
freeSegmentSelector(0,0,0),
codeSegmentSelector(0,64*1024*1024,0x9A),
dataSegmentSelector(0,64*1024*1024,0x92)
{
uint32_t i[2];
i[1] = (uint32_t)this;
i[0] = (sizeof(GlobalDescriptorTable)-1) << 16;
asm volatile(
"lgdt %0\n\t"
"mov %1, %%ss\n\t"
"mov %1, %%ds\n\t"
"mov %1, %%es\n\t"
"mov %1, %%fs\n\t"
"mov %1, %%gs\n\t"
"push %k2\n\t"
"push $1f\n\t"
"ljmp *(%%esp)\n"
"1:\n\t"
"add $8, %%esp\n\t"
:
: "m" (*(((uint8_t *) i)+2)),
"r" (DataSegmentSelector()),
"r" (CodeSegmentSelector())
: "memory");
}
Not sure why the tutorial author limited the segments to 64*1024*1024 (64MiB). You might want to consider 4UL*1024*1024*1024-1 for the full 4GiB address space.
Getting the GDT right is the important first step since interrupts will inevitably attempt to restore segment registers especially CS on an IRET as well as DS, ES, SS, GS, FS. If the GDT is wrong then you will get an exception when reloading the segment registers with a new selector.
Edit: An additional issue with your code - you will likely want to ensure you aren't generating 32-bit position independent code (PIC). To get that working requires extra effort so I recommend compiling your C/C++ files with
-fno-PIC. Depending on the default options of your compiler you could be generating PIC and you may not have the glue in place to support it, and that could cause unexpected behaviour in your program.
I wrote some other posts on this forum about the WYOOS tutorial
here and
here. The second link has some additional information about problems in the Exception Handlers since some exceptions have error codes pushed by the processor and others don't.
One other problem you may encounter is that you aren't currently sending EOI (end of interrupts) to the PIC(s) so you may get one or two interrupts and then no further interrupts will be received. The original WYOOS code handled it but your interrupt handlers don't do anything.