Page 1 of 2

Usermode GPF

Posted: Sat Nov 26, 2016 6:55 am
by Octacone
Hey everyone. I started entering ring 3 like 10 days ago and I am still not inside it.
BSOD-GPF.png
BSOD-GPF.png (7.25 KiB) Viewed 4922 times
I don't have a clue what is going on?

Assembly Related Code:

Code: Select all

	Enter_User_Mode:
	cli
	mov ax, 0x23
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov eax, esp
	push 0x23
	push eax
	pushf
	pop eax
	or eax, 0x200
	push eax
	push 0x1B
	iret
	ret
TSS Related Code:

Code: Select all

void TSS_Write(int32_t number, uint16_t ss0, uint32_t esp0)
{
	uint32_t base = (uint32_t) & TSS_Entry;
	uint32_t limit = base + sizeof(TSS_Entry);
	GDT_Set_Gate(number, base, limit, 0xE9, 0x00);
	Memory_Set(& TSS_Entry, 0x0, sizeof(TSS_Entry));
	TSS_Entry.ss0 = ss0;
	TSS_Entry.esp0 = esp0;
	TSS_Entry.cs = 0x0B;
	TSS_Entry.ss = 0x13;
	TSS_Entry.ds = 0x13;
	TSS_Entry.es = 0x13;
	TSS_Entry.fs = 0x13;
	TSS_Entry.gs = 0x13;
	TSS_Entry.iomap_base = sizeof(TSS_Entry);
}
Inside GDT_Install:
...
GDT_Set_Gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
GDT_Set_Gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);
TSS_Write(5, 0x10, 0x0);
...
TSS_Flush();
...
I just can't figure it out. :|

Re: Usermode GPF

Posted: Sat Nov 26, 2016 7:00 am
by BrightLight
If I were you, I'd try to enter usermode without the interrupt flag set, and the usermode code would be just a jmp $ or while(1) for testing.
The error code of the fault says the cause is descriptor 0x42 of the LDT, and I assume you don't have an LDT.
Have you tried running in Bochs? What does the log say? What does your usermode code do? Use Bochs' debugger to dump the GDT and take a look if your usermode descriptors contain what they should.

Re: Usermode GPF

Posted: Sat Nov 26, 2016 7:09 am
by Octacone
omarrx024 wrote:If I were you, I'd try to enter usermode without the interrupt flag set, and the usermode code would be just a jmp $ or while(1) for testing.
The error code of the fault says the cause is descriptor 0x42 of the LDT, and I assume you don't have an LDT.
Have you tried running in Bochs? What does the log say? What does your usermode code do? Use Bochs' debugger to dump the GDT and take a look if your usermode descriptors contain what they should.
Okay I will remove the flag. My usermode code does not do anything it is just a while(1) loop that halts the CPU. I do not have an LDT. I have a problem with Bochs it uses my entire CPU power and makes my system unstable. It takes around 5-10 seconds for anything to draw, loading BIOS screen (Bochs default) takes around one minute to load.

Update: without that flag being set:

Re: Usermode GPF

Posted: Sat Nov 26, 2016 7:10 am
by BrightLight
Bochs is easy to set up. Try using the default options; there shouldn't be anything CPU intensive to the degree you describe. It's the easiest way to debug such low-level code.

Re: Usermode GPF

Posted: Sat Nov 26, 2016 7:27 am
by Gigasoft
In Enter_User_Mode, you forget to push the actual address to jump to. There is also another problem: The user mode code is using the kernel mode stack.

Re: Usermode GPF

Posted: Sat Nov 26, 2016 7:27 am
by Octacone
omarrx024 wrote:Bochs is easy to set up. Try using the default options; there shouldn't be anything CPU intensive to the degree you describe. It's the easiest way to debug such low-level code.
I am using default options. It is just like line by line, really really really slow. Can't debug with something that runs at 0.05 FPS.

Re: Usermode GPF

Posted: Sat Nov 26, 2016 7:33 am
by Octacone
Gigasoft wrote:In Enter_User_Mode, you forget to push the actual address to jump to. There is also another problem: The user mode code is using the kernel mode stack.
Now I am getting a triple fault.

Code: Select all

Enter_User_Mode:
	cli
	mov ax, 0x23
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov eax, esp
	push 0x23
	push eax
	pushf
	pop eax
	;or eax, 0x200
	push eax
	push 0x1B
	push LoopOn
	iret
LoopOn:
	hlt
	jmp LoopOn
Bochs reports: (unk. ctxt): hlt ; f4

Update:
after removing "LoopOn" code I am getting this: (unk. ctxt): mov ax, 0x002b

Re: Usermode GPF

Posted: Sat Nov 26, 2016 8:44 am
by crunch
Try using a separate stack for the ring 3 code. Make sure that the ring 3 stack and code pages are marked as user accessible.

Re: Usermode GPF

Posted: Sat Nov 26, 2016 9:27 am
by Kevin
Enough people have already mentioned that trying to get into userspace without doing a context switch (i.e. staying on the kernel stack and continue using the kernel state of the registers) is unlikely to result in anything useful, but it shouldn't be in the way of getting into usermode and crashing or hanging there.

The problem with hlt is probably that it's a privileged instruction. So you were in usermode, but the transition back to kernel mode to handle the GPF didn't work.

Re: Usermode GPF

Posted: Sat Nov 26, 2016 9:49 am
by Octacone
crunch wrote:Try using a separate stack for the ring 3 code. Make sure that the ring 3 stack and code pages are marked as user accessible.
How am I supposed to use a separate stack? I am not running any code at all (except while(1)).

Re: Usermode GPF

Posted: Sat Nov 26, 2016 10:03 am
by Octacone
(unk. ctxt): ltr ax

TSS seems to be a problem?

Re: Usermode Triple Fault

Posted: Sat Nov 26, 2016 11:16 am
by Octacone
I have no idea what is going on. I checked everything twice. I just don't get it. Other people have entered user mode without any problem, every tutorial I've ever seen just magically works.

Re: Usermode Triple Fault

Posted: Sat Nov 26, 2016 12:38 pm
by crunch
octacone wrote:I have no idea what is going on. I checked everything twice. I just don't get it. Other people have entered user mode without any problem, every tutorial I've ever seen just magically works.
I understand the feeling. Took me about 5 days of banging my head on the wall to get it working. And it was because I wasn't using a separate user-access paged stack.

Code: Select all

enter_usermode:
	;push ebp
	mov ebp, esp
	cli

	mov ax, 0x20 | 3
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	push 0x20 | 3	; push ss3

	mov ecx, [ebp+8]
	push ecx ; push esp3

	pushf  ; push flags onto stack
	pop eax ; pop into eax
	or eax, 0x200 ; set IF (enable interrupts)
	push eax ; push eflags
	push 0x18 | 3 ; push CS, requested priv. level = 3

	xor eax, eax  ; Clear eax
	mov eax, [ebp+4] ; Load new IP into eax
	push eax ; Push EIP onto stack

	iret

To use this, allocate a new physical page for the user-level stack. Map that physical page to some address and make sure that you set the user-accessible flag in the page table entry. enter_usermode(address_of_function, user_stack). Remember that the stack grows down, so if for instance, you chose to map 0x0-0x1000 for your user stack, pass the function 0xF00 as a test.

Re: Usermode GPF

Posted: Mon Nov 28, 2016 12:47 am
by Boris
if you read what's inside your instruction pointer you will see that it is set jusy after some instruction it was normal to do.. But in user land, it is not!

HLT is a ring0 only instruction.
You should for now do nothing, then later call a yield syscall of sorts.

The reason HLT is privileged is because you can lock a CPU if interrupts are disabled ( which can happen sometimes right after context switch, because sti takes some times before action )

Re: Usermode GPF

Posted: Mon Nov 28, 2016 9:40 am
by Octacone
Boris wrote:if you read what's inside your instruction pointer you will see that it is set jusy after some instruction it was normal to do.. But in user land, it is not!

HLT is a ring0 only instruction.
You should for now do nothing, then later call a yield syscall of sorts.

The reason HLT is privileged is because you can lock a CPU if interrupts are disabled ( which can happen sometimes right after context switch, because sti takes some times before action )
I figured as much. Forgot that "hlt" was privileged instruction. #-o