Page 1 of 1

Problems while switching to user-mode (x86_64)

Posted: Sat Jun 11, 2016 5:41 pm
by heat
Hello,

I've depared myself with a problem while switching to user-mode in my x86_64 kernel. If I try to execute an ELF file with my TSS register loaded, it triple faults with the CR2 set to 0xfffff79ffffffff8. If I don't, the program never gets executed.

I'm sure that the task switching part of my code is correct, or at least the kernel thread part, as I have to start using some features like the gs and fs segment registers.
I'm sure the addresses of the executable are mapped correctly, with the user-bit set and everything. The user-space code doesn't even do anything special, it's just a while(1) loop, without C library initialization or anything.

Qemu's register dump of the triple fault:

Code: Select all

CPU Reset (CPU 0)
RAX=0000000000000000 RBX=0000000000000000 RCX=0000000000000000 RDX=0000000000000000
RSI=0000000000000000 RDI=0000000000000000 RBP=0000000000000000 RSP=00007a0000002000
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=0000000008048080 RFL=00010002 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0023 0000000000000000 00000fff 00a0f300 DPL=3 DS   [-WA]
CS =001b 0000000000000000 00000fff 00a0fb00 DPL=3 CS64 [-RA]
SS =0023 0000000000000000 00000fff 00a0f300 DPL=3 DS   [-WA]
DS =0023 0000000000000000 00000fff 00a0f300 DPL=3 DS   [-WA]
FS =0023 0000000000000000 00000fff 00a0f300 DPL=3 DS   [-WA]
GS =0023 0000000000000000 00000fff 00a0f300 DPL=3 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =002b ffffffff8010c000 00000067 0000eb00 DPL=3 TSS64-busy
GDT=     ffffffff80100131 00000038
IDT=     ffffffff801199e0 00000fff
CR0=80000013 CR2=fffff79ffffffff8 CR3=0000000000101000 CR4=00000620
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000000 CCD=0000000000000000 CCO=DYNAMIC 
EFER=0000000000000d01
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
XMM08=00000000000000000000000000000000 XMM09=00000000000000000000000000000000
XMM10=00000000000000000000000000000000 XMM11=00000000000000000000000000000000
XMM12=00000000000000000000000000000000 XMM13=00000000000000000000000000000000
XMM14=00000000000000000000000000000000 XMM15=00000000000000000000000000000000
As I can see below, I know I technically switched to ring 3, but it faults on the very first instruction.

Thanks for the help

Re: Problems while switching to user-mode (x86_64)

Posted: Sat Jun 11, 2016 10:22 pm
by BrightLight
I'm assuming your page fault handler didn't get called, so it's likely your TSS has an error, your IDT has an error, or the pages are not user-accessible.

Re: Problems while switching to user-mode (x86_64)

Posted: Sun Jun 12, 2016 5:16 am
by Combuster
First reaction: why does the task register have bits 0-1 set?

Re: Problems while switching to user-mode (x86_64)

Posted: Sun Jun 12, 2016 6:22 am
by heat
This is my GDT:

Code: Select all

tmp_gdt:
	DQ	0x0000000000000000
	DQ	0x00A09A0000000000
	DQ	0x00A0920000000000
	DQ	0x00A0FA0000000000
	DQ	0x00A0F20000000000
	DW	0x67,0xc000
	DB	0x10, 0xE9, 0
	DB	0x80
	DD	0xffffffff
With the reply above, I assume something is wrong with it?
TSS code:

Code: Select all

extern tss_entry_t tss;
extern void tss_flush();
void init_tss()
{
	printf("tss: %x\n",&tss);
	memset(&tss, 0, sizeof(tss_entry_t));
	tss.stack0 = (uint64_t)vmm_allocate_virt_address(VM_KERNEL, 2, VMM_TYPE_STACK);
	vmm_map_range((void*)tss.stack0, 2, 0x3);
	tss.ist[0] = tss.stack0;
	tss_flush();
}
tss is a label I define in the linker script.
TSS struct:

Code: Select all

typedef struct tss_entry
{
	uint32_t reserved0;
	uint64_t stack0;
	uint64_t stack1;
	uint64_t stack2;
	uint64_t reserved1;
	uint64_t ist[7];
	uint64_t reserved2;
	uint16_t reserved3;
	uint16_t iomap_base;
} __attribute__((packed)) tss_entry_t;
Also, if the TSS is invalid, why would that contribute to having a #PF? Especially in a such high address.

Re: Problems while switching to user-mode (x86_64)

Posted: Sun Jun 12, 2016 6:32 am
by iansjack
The GDT entry for your user mode code segment seems to be all zeros. That doesn't look right.

Re: Problems while switching to user-mode (x86_64)

Posted: Sun Jun 12, 2016 6:59 am
by heat
iansjack wrote:The GDT entry for your user mode code segment seems to be all zeros. That doesn't look right.
Oh, yeah I forgot to explain that it contains the 32-bit GDT segments it uses to bootstrap itself to 64-bit. Let me edit that out.