Help, I try to load 64-bit TSS, but failed

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
torshie
Member
Member
Posts: 89
Joined: Sun Jan 11, 2009 7:41 pm

Help, I try to load 64-bit TSS, but failed

Post by torshie »

After enter 64-bit mode, I try to load TSS and enable interrupt, but invoking method loadTaskRegister causes triple fault.
The CPU should be in 64-bit mode (I'm not sure, I don't know how to check through bochs). All methods of System are static methods. callDebugger contains only one line: asm volatile("xchg %bx, %bx"). This is bochs' message after callDebugger:
<bochs:4> info gdt
Global Descriptor Table (base=0x00000000001062c0, limit=40):
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x01]=Code segment, laddr=00000000, limit=f0000 * 4Kbytes, Execute-Only, 16-bit
GDT[0x02]=Data segment, laddr=00000000, limit=f0000 * 4Kbytes, Read/Write
GDT[0x03]=32-Bit TSS (Busy) at 0x00104040, length 0x02068
GDT[0x04]=??? descriptor hi=0x00000000, lo=0x00000000
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
<bochs:5> info idt 0
Interrupt Descriptor Table (base=0x00000000001060c0, limit=512):
IDT[0x00]=64-Bit Interrupt Gate target=0x0008:0000000000100ae0, DPL=0
<bochs:6> c
00260411420e[CPU0 ] LTR: doesn't point to an available TSS descriptor!
00261010082e[CPU0 ] read_RMW_virtual_byte_64(): canonical failure, 1ecf1f61
00261010082e[CPU0 ] interrupt(long mode): gate descriptor is not valid sys seg
00261010082e[CPU0 ] interrupt(long mode): gate descriptor is not valid sys seg
00261010082i[CPU0 ] CPU is in long mode (active)
00261010082i[CPU0 ] CS.d_b = 16 bit
00261010082i[CPU0 ] SS.d_b = 32 bit
00261010082i[CPU0 ] EFER = 0x00000500
00261010082i[CPU0 ] | RAX=c03160661ecf1f61 RBX=0000000000000000
00261010082i[CPU0 ] | RCX=00000000a9a7e8d8 RDX=000000000000002e
00261010082i[CPU0 ] | RSP=00000000000ffe68 RBP=00000000000ffe78
00261010082i[CPU0 ] | RSI=000000000000002e RDI=c03160661ecf1f61
00261010082i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00261010082i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00261010082i[CPU0 ] | R12=00000000001062d8 R13=0000000000030d48
00261010082i[CPU0 ] | R14=0000000000100018 R15=0000000000000010
00261010082i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af pf cf
00261010082i[CPU0 ] | SEG selector base limit G D
00261010082i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00261010082i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 f0000fff 1 0
00261010082i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00261010082i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00261010082i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00261010082i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00261010082i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00261010082i[CPU0 ] | MSR_FS_BASE:0000000000000000
00261010082i[CPU0 ] | MSR_GS_BASE:0000000000000000
00261010082i[CPU0 ] | RIP=0000000000102045 (0000000000102045)
00261010082i[CPU0 ] | CR0=0xc000003f CR2=0x0000000000000000
00261010082i[CPU0 ] | CR3=0x00207000 CR4=0x00000020
(0).[261010082] [0x00102045] 0008:0000000000102045 (unk. ctxt): add byte ptr ds:[rax], al ; 0000
00261010082e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
Bochs' message shows that my code segment is 16-bit and TSS segment selector is 32-bit. I double checked amd's manual, I think the format of the two selectors are correct 64-bit code segment selector and 64-bit TSS selector (???)

Page map is identity map.

Attached file is the compressed binary. The binary has multiboot header

Source code.

Code: Select all

#include "System.h"
#include "bit.h"

namespace kernel {

void System::init() {
	System::loadGlobalDescriptorTable();
	System::loadInterruptGateDescriptorTable();
	System::callDebugger();
	System::loadTaskRegister();
	System::enableInterrupt();
}

void System::loadTaskRegister() {
	asm volatile("mov $24, %ax;"
			"ltr %ax");
}

void System::enableInterrupt() {
	asm volatile("sti");
}

struct TaskStateSegment {
	U32 ignored0;
	U64 rsp0;
	U64 rsp1;
	U64 rsp2;
	U64 ist[8]; /* ist[0] is ignored, ist[1] to ist[7] will be used */
	U64 ignored1;
	U16 ignored2;
	U16 ioMapBase;
	char ioPermissionMap[8192];

	TaskStateSegment() :
			ioMapBase((char*)(&(this->ioPermissionMap)) - (char*)this) {}
} __attribute__((packed));

struct DescriptorTablePointer {
	U16 limit;
	void* base;
} __attribute__((packed));

struct NullSegmentDescriptor {
	U64 ignored;

	NullSegmentDescriptor() : ignored(0) {}
} __attribute__((packed));

struct DataSegmentDescriptor {
	U32 ignored;
	U32 attribute;

	DataSegmentDescriptor() : attribute(DEFAULT) {}

	enum {
		DEFAULT = 0x008f9200,
		P = 15,
	};
} __attribute__((packed));

struct CodeSegmentDescriptor {
	U32 ignored;
	U32 attribute;

	CodeSegmentDescriptor() : attribute(DEFAULT) {}

	enum {
		DEFAULT = 0x00af9800,
		C = 10,
		P = 15,
		L = 21,
		D = 22,
		DPL_LOW = 11,
		DPL_HIGH = 12,
	};
} __attribute__((packed));

struct SystemSegmentDescriptor {
	U16 limit;
	U16 base01;
	U8 base2;
	U8 typeAndAttribute;
	U8 limitAndAttribute;
	U8 base3;
	U32 baseHigh;
	U32 zero;

	SystemSegmentDescriptor() : limit(0), base01(0), base2(0),
			typeAndAttribute(0x89), limitAndAttribute(0), base3(0),
			baseHigh(0), zero(0) {}

	void setType(U8 t) {
		this->typeAndAttribute &= 0xf0;
		this->typeAndAttribute |= t & 0x0f;
	}

	void setLimit(U32 l) {
		this->limit = (U16)l;
		this->limitAndAttribute |= (l & 0x0f0000) >> 16;
	}

	void setBaseAddress(void* base) {
		UPointer value = (UPointer)base;
		this->base01 = (U16)value;
		this->base2 = (U8)(value >> 16);
		this->base3 = (U8)(value >> 24);
		this->baseHigh = (U32)(value >> 32);
	}

	enum {
		DPL_LOW = 5,
		DPL_HIGH = 6,

		TYPE_LDT = 2,
		TYPE_AVAILABLE_TSS = 9,
		TYPE_BUSY_TSS = 0xb,
		TYPE_CALL_GATE = 0xc,
		TYPE_INTERRUPT_GATE = 0xe,
		TYPE_TRAP_GATE = 0xf,
	};
} __attribute__((packed));

struct InterruptGateDescriptor {
	U16 offsetLow;
	U16 selector;
	U16 attribute;
	U16 offsetMiddle;
	U32 offsetHigh;
	U32 ignored;
} __attribute__((packed));

static void installGeneralDescriptor(char* table, const void* descriptor,
		int size) {
	const char* p = (const char*)descriptor;
	for (int i = 0; i < size; ++i, ++p, ++table) {
		*table = *p;
	}
}

static TaskStateSegment taskStateSegment;

extern "C" DescriptorTablePointer globalDescriptorTablePointer;
DescriptorTablePointer globalDescriptorTablePointer;

void System::loadGlobalDescriptorTable() {
	static char globalDescriptorTable[128];
	globalDescriptorTablePointer.base = globalDescriptorTable;
	int offset = 0;

	NullSegmentDescriptor nullSegmentDescriptor;
	installGeneralDescriptor(globalDescriptorTable + offset,
			&nullSegmentDescriptor, sizeof(nullSegmentDescriptor));
	offset += sizeof(nullSegmentDescriptor);

	CodeSegmentDescriptor codeSegment;
	installGeneralDescriptor(globalDescriptorTable + offset,
			&codeSegment, sizeof(codeSegment));
	offset += sizeof(codeSegment);

	DataSegmentDescriptor dataSegment;
	installGeneralDescriptor(globalDescriptorTable + offset,
			&dataSegment, sizeof(dataSegment));
	offset += sizeof(dataSegment);

	SystemSegmentDescriptor taskStateSegmentDescriptor;
	taskStateSegmentDescriptor.setType(SystemSegmentDescriptor::TYPE_BUSY_TSS);
	taskStateSegmentDescriptor.setBaseAddress(&taskStateSegment);
	taskStateSegmentDescriptor.setLimit(sizeof(taskStateSegment));
	installGeneralDescriptor(globalDescriptorTable + offset,
			&taskStateSegmentDescriptor, sizeof(taskStateSegmentDescriptor));
	offset += sizeof(taskStateSegmentDescriptor);

	globalDescriptorTablePointer.limit = offset;
	asm volatile("lgdt globalDescriptorTablePointer");
}

extern "C" void isr0(void); extern "C" void isr1(void);
extern "C" void isr2(void); extern "C" void isr3(void);
extern "C" void isr4(void); extern "C" void isr5(void);
extern "C" void isr6(void); extern "C" void isr7(void);
extern "C" void isr8(void); extern "C" void isr9(void);
extern "C" void isr10(void); extern "C" void isr11(void);
extern "C" void isr12(void); extern "C" void isr13(void);
extern "C" void isr14(void); extern "C" void isr15(void);
extern "C" void isr16(void); extern "C" void isr17(void);
extern "C" void isr18(void); extern "C" void isr19(void);
extern "C" void isr20(void); extern "C" void isr21(void);
extern "C" void isr22(void); extern "C" void isr23(void);
extern "C" void isr24(void); extern "C" void isr25(void);
extern "C" void isr26(void); extern "C" void isr27(void);
extern "C" void isr28(void); extern "C" void isr29(void);
extern "C" void isr30(void); extern "C" void isr31(void);

static void installInterruptGateDescriptor(InterruptGateDescriptor* vector,
		unsigned short attribute, void (*function)(void)) {
	UPointer address = (UPointer)function;
	vector->offsetLow = address & 0xffff;
	vector->selector = System::CODE_SELECTOR;
	vector->attribute = attribute;
	vector->offsetMiddle = (U16)((0xffff0000 & address) >> 16);
	vector->offsetHigh = (U32)(address >> 32);
}

extern "C" DescriptorTablePointer interruptDescriptorTablePointer;
DescriptorTablePointer interruptDescriptorTablePointer;

void System::loadInterruptGateDescriptorTable() {
	static bool enabled = false;
	if (enabled) return;

	static InterruptGateDescriptor table[32];
	installInterruptGateDescriptor(table, IDT_ENTRY_ATTRIBUTE, isr0);
	installInterruptGateDescriptor(table + 1, IDT_ENTRY_ATTRIBUTE, isr1);
	installInterruptGateDescriptor(table + 2, IDT_ENTRY_ATTRIBUTE, isr2);
	installInterruptGateDescriptor(table + 3, IDT_ENTRY_ATTRIBUTE, isr3);
	installInterruptGateDescriptor(table + 4, IDT_ENTRY_ATTRIBUTE, isr4);
	installInterruptGateDescriptor(table + 5, IDT_ENTRY_ATTRIBUTE, isr5);
	installInterruptGateDescriptor(table + 6, IDT_ENTRY_ATTRIBUTE, isr6);
	installInterruptGateDescriptor(table + 7, IDT_ENTRY_ATTRIBUTE, isr7);
	installInterruptGateDescriptor(table + 8, IDT_ENTRY_ATTRIBUTE, isr8);
	installInterruptGateDescriptor(table + 9, 0, 0);
	installInterruptGateDescriptor(table + 10, IDT_ENTRY_ATTRIBUTE, isr10);
	installInterruptGateDescriptor(table + 11, IDT_ENTRY_ATTRIBUTE, isr11);
	installInterruptGateDescriptor(table + 12, IDT_ENTRY_ATTRIBUTE, isr12);
	installInterruptGateDescriptor(table + 13, IDT_ENTRY_ATTRIBUTE, isr13);
	installInterruptGateDescriptor(table + 14, IDT_ENTRY_ATTRIBUTE, isr14);
	installInterruptGateDescriptor(table + 15, 0, 0);
	installInterruptGateDescriptor(table + 16, IDT_ENTRY_ATTRIBUTE, isr16);
	installInterruptGateDescriptor(table + 17, IDT_ENTRY_ATTRIBUTE, isr17);
	installInterruptGateDescriptor(table + 18, IDT_ENTRY_ATTRIBUTE, isr18);
	installInterruptGateDescriptor(table + 19, IDT_ENTRY_ATTRIBUTE, isr19);

	installInterruptGateDescriptor(table + 20, 0, 0); 
	installInterruptGateDescriptor(table + 21, 0, 0); 
	installInterruptGateDescriptor(table + 22, 0, 0); 
	installInterruptGateDescriptor(table + 23, 0, 0); 
	installInterruptGateDescriptor(table + 24, 0, 0); 
	installInterruptGateDescriptor(table + 25, 0, 0); 
	installInterruptGateDescriptor(table + 26, 0, 0); 
	installInterruptGateDescriptor(table + 27, 0, 0); 
	installInterruptGateDescriptor(table + 28, 0, 0); 
	installInterruptGateDescriptor(table + 29, 0, 0); 

	installInterruptGateDescriptor(table + 30, IDT_ENTRY_ATTRIBUTE, isr30);

	installInterruptGateDescriptor(table + 31, 0, 0); 

	interruptDescriptorTablePointer.limit = sizeof(table);
	interruptDescriptorTablePointer.base = table;
	asm volatile("lidt interruptDescriptorTablePointer");
	enabled = true;
}

}
EDIT: The following solution solves the ltr problem
1. Zero out the TaskStateSegment
2. Set TaskStateSegmentSelector to AVAILABEL instead of BUSY

But I get another problem:
After firing the instruction "sti" every branch (jmp, call, ret) instruction causes triple fault. Bochs's message is:
00231397233e[CPU0 ] read_RMW_virtual_byte_64(): canonical failure, eefed6c6
00231397233e[CPU0 ] interrupt(long mode): gate descriptor is not valid sys seg
00231397233e[CPU0 ] interrupt(long mode): gate descriptor is not valid sys seg
00231397233i[CPU0 ] CPU is in long mode (active)
00231397233i[CPU0 ] CS.d_b = 16 bit
00231397233i[CPU0 ] SS.d_b = 32 bit
00231397233i[CPU0 ] EFER = 0x00000500
00231397233i[CPU0 ] | RAX=c6c600c6eefed6c6 RBX=0000000000000000
00231397233i[CPU0 ] | RCX=00000000c6c60030 RDX=000000000000002e
00231397233i[CPU0 ] | RSP=00000000000ffd00 RBP=00000000000ffd10
00231397233i[CPU0 ] | RSI=7000000000001040 RDI=c6c600c6eefed6c6
00231397233i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00231397233i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00231397233i[CPU0 ] | R12=00000000001062d8 R13=0000000000030d48
00231397233i[CPU0 ] | R14=0000000000100018 R15=0000000000206fd0
00231397233i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf af pf cf
00231397233i[CPU0 ] | SEG selector base limit G D
00231397233i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00231397233i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 f0000fff 1 0
00231397233i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00231397233i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00231397233i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00231397233i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00231397233i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00231397233i[CPU0 ] | MSR_FS_BASE:0000000000000000
00231397233i[CPU0 ] | MSR_GS_BASE:0000000000000000
00231397233i[CPU0 ] | RIP=0000000000102049 (0000000000102049)
00231397233i[CPU0 ] | CR0=0xc000003f CR2=0x0000000000000000
00231397233i[CPU0 ] | CR3=0x00207000 CR4=0x00000020
Could anybody help me?

Thanks
Attachments
kernel.bin.gz
(4.65 KiB) Downloaded 156 times
Craze Frog
Member
Member
Posts: 368
Joined: Sun Sep 23, 2007 4:52 am

Re: Help, I try to load 64-bit TSS, but failed

Post by Craze Frog »

U64 ist[8] should be U64 ist[7], I think.
Post Reply