Page 1 of 1

Encountering General Protection Fault on SYSRET

Posted: Tue Mar 19, 2024 9:48 pm
by 2wx
I am currently working on my os code, and trying to switch to the user mode.
This is how I set my gdts:

Code: Select all

uint64 gdt[6];
gdt[0] = 0UL;                   // dummy
gdt[1] = 0x00a0980000000000UL;  // kernel code
gdt[2] = 0x00c0920000000000UL;  // kernel data
gdt[3] = 0UL;                   // reserved
gdt[4] = 0x00c0f20000000000UL;  // user data
gdt[5] = 0x00a0f80000000000UL;  // user code
struct gdtp {
  uint16 size;
  uint64 addr;
} __attribute__((packed)) _gdtp;
_gdtp.size = 8 * 6 - 1;
_gdtp.addr = (uint64)&gdt[0];
cli();
__asm__ __volatile__ ("lgdt 0x1031d0"); // addr for _gdtp
And this is how I try to switch to usermode:

Code: Select all

global jump_usermode
extern user_test
jump_usermode:
;enable system call extensions that enables sysret and syscall
    mov rcx, 0xc0000082
    wrmsr
    mov rcx, 0xc0000080
    rdmsr
    or eax, 1
    wrmsr ;enable syscall/sysret
    mov rcx, 0xc0000081
    rdmsr
    mov edx, 0x00180008
    wrmsr ; CS/SS selectors
    mov rcx, user_test ; to be loaded into RIP
    ;mov rcx, user_test
    mov r11, 0x202 ; to be loaded into EFLAGS
    o64 sysret
This is how the system looks before "usermode()":

Code: Select all

Servicing hardware INT=0x20
   192: v=20 e=0000 i=0 cpl=0 IP=0008:0000000000101852 pc=0000000000101852 SP=0000:0000000000110ef0 env->regs[R_EAX]=0000000000000010
RAX=0000000000000010 RBX=0000000000000000 RCX=0000000000000008 RDX=00000000000003f8
RSI=0000000000000030 RDI=00000000000003f8 RBP=0000000000110fe0 RSP=0000000000110ef0
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000000
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=0000000000101852 RFL=00000212 [----A--] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 0000000000000000 00000000 00000000
CS =0008 0000000000000000 00000000 00209800 DPL=0 CS64 [---]
SS =0000 0000000000000000 ffffffff 00c09300 DPL=0 DS   [-WA]
DS =0000 0000000000000000 00000000 00000000
FS =0000 0000000000000000 00000000 00000000
GS =0000 0000000000000000 00000000 00000000
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     0000000000100284 0000000f
IDT=     00000000001031e0 00000fff
CR0=80000011 CR2=0000000000000000 CR3=0000000000105000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000010 CCD=0000000000000001 CCO=EFLAGS
EFER=0000000000000500
And after executing "SYSRET", it encountered a General Protect Fault, followed by a Stack-Segment Fault:

Code: Select all

check_exception old: 0xffffffff new 0xd
   193: v=0d e=0000 i=0 cpl=3 IP=002b:00000000001013bd pc=00000000001013bd SP=0023:0000000000110e90 env->regs[R_EAX]=00000000000003fd
RAX=00000000000003fd RBX=0000000000000000 RCX=00000000001019ee RDX=00000000000003fd
RSI=0000000000000030 RDI=00000000000003fd RBP=0000000000110e90 RSP=0000000000110e90
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000202
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=00000000001013bd RFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0000 0000000000000000 00000000 00000000
CS =002b 0000000000000000 ffffffff 00a0fb00 DPL=3 CS64 [-RA]
SS =0023 0000000000000000 ffffffff 00c0f300 DPL=3 DS   [-WA]
DS =0000 0000000000000000 00000000 00000000
FS =0000 0000000000000000 00000000 00000000
GS =0000 0000000000000000 00000000 00000000
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     00000000001031a0 0000002f
IDT=     00000000001031e0 00000fff
CR0=80000011 CR2=0000000000000000 CR3=0000000000105000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000005 CCD=00000000000003fd CCO=ADDL
EFER=0000000000000501
check_exception old: 0xd new 0xc
   194: v=08 e=0000 i=0 cpl=3 IP=002b:00000000001013bd pc=00000000001013bd SP=0023:0000000000110e90 env->regs[R_EAX]=00000000000003fd
RAX=00000000000003fd RBX=0000000000000000 RCX=00000000001019ee RDX=00000000000003fd
RSI=0000000000000030 RDI=00000000000003fd RBP=0000000000110e90 RSP=0000000000110e90
R8 =0000000000000000 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000202
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=00000000001013bd RFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0000 0000000000000000 00000000 00000000
CS =002b 0000000000000000 ffffffff 00a0fb00 DPL=3 CS64 [-RA]
SS =0023 0000000000000000 ffffffff 00c0f300 DPL=3 DS   [-WA]
DS =0000 0000000000000000 00000000 00000000
FS =0000 0000000000000000 00000000 00000000
GS =0000 0000000000000000 00000000 00000000
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     00000000001031a0 0000002f
IDT=     00000000001031e0 00000fff
CR0=80000011 CR2=0000000000000000 CR3=0000000000105000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000005 CCD=00000000000003fd CCO=ADDL
EFER=0000000000000501
check_exception old: 0x8 new 0xc
Then the system crashed.

I have also set the page table to user-mode:

Code: Select all

setup_page_tables:
	mov eax, page_table_l3
	or eax, 0b111 ; present, writable, usermode.
	mov [page_table_l4], eax

	mov eax, page_table_l2
	or eax, 0b111 ; present, writable, usermode.
	mov [page_table_l3], eax

	mov ecx, 0 ; counter
.loop:
	mov eax, 0x200000 ; 2MiB
	mul ecx
	or eax, 0b10000111 ; present, writable, huge page, usermode.
	mov [page_table_l2 + ecx * 8], eax ; 1G does this indicates that, the whole memory is merely copy of 2MiB?

	inc ecx ; increment counter
	cmp ecx, 512 ; checks if the whole table is mapped
	jne .loop
	ret

Re: Encountering General Protection Fault on SYSRET

Posted: Tue Jun 11, 2024 9:48 pm
by Octocontrabass
2wx wrote:This is how I set my gdts:
Your inline assembly is wrong. You should use something like this:

Code: Select all

asm volatile ( "lgdt %0" :: "m"(_gdtp) );
2wx wrote:And after executing "SYSRET", it encountered a General Protect Fault, followed by a Stack-Segment Fault:
You successfully switched to user mode. Your user_test function is causing the general protection fault. The other faults are because you haven't set up a TSS.

Re: Encountering General Protection Fault on SYSRET

Posted: Tue Jun 11, 2024 10:02 pm
by MichaelPetch
This question was asked on Stackoverflow as well. As Octo pointed out the hard coded address to LGDT was fixed address, and it was a good idea to rewrite the inline assembly to handle it properly. They also issued a port instruction in user mode (ring 3), and eventually figured out they hadn't properly set up their TSS.