Page 1 of 1

GPF when issuing syscall

Posted: Wed Oct 31, 2012 8:41 am
by Whitebird
Hello everyone!

I am re-writing my OS from the scratch to have a proper (higher half) kernel. For the moment, I am working on the TSS + ring 3 + issuing system calls.

So far, the TSS seems to be correct installed and I am able to get into ring 3. However, whenever I try to issue a system call I get a general protection fault. I used Bochs to check the TSS descriptor and it seems to be ok. Moreover, I have noticed that the machine is actually reading my TSS, because if I change the esp0 in the TSS structure, my stack is being set to that value.

Here is the portion of my code that is relevant to this problem:

Code: Select all

[SECTION .text]

setup:
	call setup_gdt
	call setup_idt
	
	call kmain

; Sets up GDT.
setup_gdt:
	call setup_tss

	; Load GDT.
	lgdt [gdtptr]
	
	; Reload code segment register.
	jmp 0x08:reload_cs
	reload_cs:
   
	; Reload data segment register.
	mov   ax, 0x10
	mov   ds, ax
	
	; Load TSS.
	mov eax, 0x2b
	ltr ax
	ret
	
; Sets up TSS.
setup_tss:
	mov eax, tss
	mov ebx, tss + 0x67
	mov word [gdt + 40], bx
	mov word [gdt + 42], ax
	shr eax, 16
	shr ebx, 16
	mov cl, al
	mov ch, 0xe9
	mov word [gdt + 44], cx
	and bl, 0xf
	mov cl, bl
	mov ch, ah
	mov word [gdt + 46], cx
	ret

[SECTION .data]
[EXTERN KDATA]

; Global descriptor table.
gdt:
	dq 0x0000000000000000 ; NULL segment descriptor.
	dq 0x00cf9a000000ffff ; Kernel code segment descriptor.
	dq 0x00cf92000000ffff ; Kernel data segment descriptor.
	dq 0x00cffa000000ffff ; User code segment descriptor.
	dq 0x00cff2000000ffff ; User data segment descriptor.
	dq 0x0000000000000000 ; TSS segment descriptor.

; Global descriptor table pointer.
gdtptr:
	dw 0x002f             ; GDT size.
	dd (KDATA + gdt - $$) ; GDT linear address.

; Task state segment.
ALIGN 4
tss:
	dd 0x00000000 ; Previous TSS in the TSS list.
	dd 0xfffffffc ; Ring 0 stack pointer.
	dd 0x00000010 ; Ring 0 stack segment.
	dd 0x00000000 ; Ring 1 stack pointer.
	dd 0x00000000 ; Ring 1 stack segment.
	dd 0x00000000 ; Ring 2 stack pointer.
	dd 0x00000000 ; Ring 2 stack segment.
	dd 0x00000000 ; cr3
	dd 0x00000000 ; eip
	dd 0x00000000 ; eflags
	dd 0x00000000 ; eax
	dd 0x00000000 ; ecx
	dd 0x00000000 ; edx
	dd 0x00000000 ; ebx
	dd 0x00000000 ; esp
	dd 0x00000000 ; ebp
	dd 0x00000000 ; esi
	dd 0x00000000 ; edi
	dd 0x00000000 ; es
	dd 0x00000000 ; cs
	dd 0x00000000 ; ss
	dd 0x00000000 ; ds
	dd 0x00000000 ; fs
	dd 0x00000000 ; gs
	dd 0x00000000 ; LDT selector.
	dw 0x0000     ; Debug trap bit.
	dw 0x0067     ; I/O map base.

Code: Select all

[GLOBAL _user_mode]

; Switches to user mode and enable interrupts.
_user_mode:
	mov ecx, [esp + 4]       ; User init function.
	mov edx, [esp + 8]       ; User stack pointer.
	
	; Load data segment selector.
	mov ax, 0x23
	mov ds, ax
	
	; Build fake interrupt stack.
	push dword 0x23          ; Push stack segment.
	push edx                 ; Push Stack pointer.
	pushfd
	or dword [esp], 0x200
	push dword 0x1b          ; Push code segment selector.
	push ecx                 ; Push eip.
	iretd

Code: Select all


void init()
{	
	int ret;

	__asm__ ("int $0x80" : "=a" (ret) : "0" (0)); // GENERAL PROTECTION FAULT!!
	
	while(1);
}

void kmain()
{
	...
	
	/* Switch to user mode and enable interrupts. */	
	_user_mode((addr_t)&init, KBASE - 4);
}
Thanks in advance,

Pedro

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 9:35 am
by Combuster
Since you have a GPF, check it's error code. Then, check the IDT accordingly.

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 10:55 am
by Whitebird
Thank you for your reply Combuster.

I have checked the error code, but it is quite weird: 0x402

From the Intel manual:
The general protection exception is a fault. In response to a general protection exception, the processor pushes an error code onto the exception handler's stack. If loading a descriptor causes the exception, the error code contains a selector to the descriptor; otherwise, the error code is null. The source of the selector in an error code may be any of the following:

1. An operand of the instruction.
2. A selector from a gate that is the operand of the instruction.
3. A selector from a TSS involved in a task switch.
So, what should I take in account so?

EDIT: I have checked IDT using Bochs debugger and it is correctly setup.

Thanks,

Pedro

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 11:09 am
by bluemoon

Code: Select all

_user_mode((addr_t)&init, KBASE - 4);
Should it be init instead of &init ?

AS a side note, in your code to enter usermode:

Code: Select all

pushfd
or dword [esp], 0x200
I would just do push 0x0202, in your code the eflags is not consistence, someone may start with ZF clear, other may have ZF set, etc - it may not be big deal but it's always better to be consistent.

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 11:17 am
by Whitebird
Hi Bluemoon,

Thank you for the advice. You're right it is better to change the way that I'm "setting" eflags, in order to be more consistent.

About init() the error is not there: it is being called. Furthermore, I can do what ever computation I want in init(), though I cannot make any syscall =/

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 11:45 am
by bluemoon
What's the DPL of the interrupt entry for your syscall?

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 11:52 am
by Whitebird
Not sure if I understood your question but, the code selector of this IDT entry is 0x8 (kernel code selector).

Is this what you have questioned me?

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 11:56 am
by bluemoon
No, int the IDT, each entry has the selector, address, and flags(this includes DPL).
usermode code simply cannot invoke interrupt with DPL=0 and #GP will be generated.

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 12:08 pm
by Whitebird
Indeed. The DPL was 0.

My mind was stuck, thinking that the problem was in setting up the TSS.

Thank you very much. The problem is solved now.

Cheers,

Pedro

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 12:11 pm
by bluemoon
Congrat, and I suspect you have some hidden issue in the #GP handler, since 402 is not logical code for this error.

Re: GPF when issuing syscall

Posted: Wed Oct 31, 2012 12:19 pm
by Whitebird
I thought it weird too, since the IDT is only 256 entries long. But, as you stated, it might have a problem in my stack_trace() function.

I'll check it. Thx again.

Re: GPF when issuing syscall

Posted: Thu Nov 01, 2012 4:03 am
by Combuster
I have checked the error code, but it is quite weird: 0x402
I see the interrupt number (0x80 << 3), and the "exception caused by interrupt" flag (0x2). You might want to read the manual again.