The getGlobal<> function template is a simple singleton pattern implementation.
This is the definition of the global descriptor table.
Code: Select all
class __attribute__((__packed__)) GlobalDescriptorTable {
public:
enum DescriptorOffset {
OFFSET_KERNEL_CODE = 8,
OFFSET_KERNEL_DATA = 16,
OFFSET_CODE32 = 24,
OFFSET_USER_DATA = 32,
OFFSET_USER_CODE = 40
};
NullDescriptor _null;
CodeSegmentDescriptor kernelCode;
DataSegmentDescriptor kernelData;
NullDescriptor _code32;
DataSegmentDescriptor userData;
CodeSegmentDescriptor userCode;
SystemSegmentDescriptor taskState;
struct __attribute__((__packed__)) {
uint16_t limit;
void* base;
};
GlobalDescriptorTable();
void load() const;
};
GlobalDescriptorTable::GlobalDescriptorTable()
: limit(sizeof(GlobalDescriptorTable) - sizeof(limit) - sizeof(base)),
base(this) {
userCode.privilege = 3;
userData._privilege = 3;
TaskStateSegment& tss = getGlobal<TaskStateSegment>();
taskState.setBase((uint64_t)&tss);
taskState.setLimit(sizeof(TaskStateSegment) - 1);
taskState.type = SystemSegmentDescriptor::AVAILABLE_TSS;
}
void GlobalDescriptorTable::load() const {
__asm__ __volatile__(
"lgdt %0\n"
"mov %%ax, %%ds\n"
"mov %%ax, %%ss\n"
"mov %%ax, %%es\n"
"mov %%ax, %%fs\n"
"mov %%ax, %%gs\n"
:
: "m"(limit), "a"((char*)&kernelData - (char*)this)
);
}
Code: Select all
struct __attribute__((__packed__)) TaskStateSegment {
uint32_t _reserved0;
uint64_t rsp[3];
uint64_t ist[8]; /* ist[0] cannot bet used */
uint64_t _reserved1;
uint16_t _reserved2;
uint16_t base;
TaskStateSegment();
};
TaskStateSegment::TaskStateSegment()
: _reserved0(0), _reserved1(0), _reserved2(0), base(0) {
memset(rsp, 0, sizeof(rsp));
memset(ist, 0, sizeof(ist));
/* Check doc/memory-layout for details */
ist[1] = KERNEL_SPACE_BASE + 0x180000;
rsp[0] = ist[1];
}
Code: Select all
class __attribute__((__packed__)) SystemSegmentDescriptor {
friend class GlobalDescriptorTable;
public:
enum SegmentDescriptorType {
AVAILABLE_TSS = 9, BUSY_TSS = 0xB, CALL_GATE = 0xC,
INTERRUPT_GATE = 0xE, TRAP_GATE = 0xF
};
private:
uint16_t limit0;
uint32_t base0:24;
uint8_t type:4;
uint8_t _zero:1;
uint8_t privilege:2;
uint8_t present:1;
uint8_t limit1:4;
uint8_t available:1;
uint8_t _ignored:2;
uint8_t granularity:1;
uint64_t base1:40;
uint32_t _reserved;
SystemSegmentDescriptor()
: _zero(0), privilege(0), present(1), _ignored(0), granularity(0),
_reserved(0) {}
void setLimit(uint32_t limit) {
limit0 = limit;
limit1 = limit >> 16;
}
void setBase(uint64_t base) {
base0 = base;
base1 = base >> 24;
}
};
Code: Select all
GlobalDescriptorTable& gdt = getGlobal<GlobalDescriptorTable>();
gdt.load();
getGlobal<InterruptDescriptorTable>().load(); <=== Load interrupt descriptor table, works as expected.
__asm__ __volatile__ (
"ltr %%ax\n" <==== This line seems to be ok, probably because interrupts are disabled.
"sti\n" <==== This instruction is interesting, sometimes I get a double fault after this instruction.
"ltr %%ax\n" <==== #GP fault here, the error code is 0x30 which is the selector of TSS descriptor in GDT
:
: "a"((char*)(&gdt.taskState) - (char*)&gdt)
);
TR is shown as the following by QEMU:
Code: Select all
TR =0030 ffffffff80221440 00000067 00008900 DPL=0 TSS64-avl
Any idea what goes wrong?
Thanks in advance.
-torshie