Page 1 of 2

GPF on jum to less privalged code

Posted: Fri May 19, 2006 1:16 pm
by ed_tait
hello,

i'm new to kernel development and have sucessfully switched to pmode and implemented an idt and have turned on paging.

when i try to jump to code at dpl 3 i get a general protection fault.

my gdt looks like this

Code: Select all

gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
        dd 0
        dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0
        
gdt_interrupts:         ; Interrupt segment, read/execute, nonconforming
        dw 0FFFFh
        dw 01000h       ; Start at address 01000h
        db 0
        db 10011010b
        db 11001111b
        db 0
gdt_apps:         ; Interrupt segment, read/execute, nonconforming            
        dw 0FFFFh
        dw 0
        db 0
        db 10111011b
        db 11001111b
        db 0      

gdt_end:                ; Used to calculate the size of the GDT


gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                  ; Address of the GDT
when i perform a jump to some test code the GPf fires:

Code: Select all

mov ax, 0febch ; for debuging 

jmp 18h:test_user

; we never get here there is a General protection fault
test_user
mov ax, 1234h ; for debuging
int 3 ;break point
The hex dump indicates that the problem is with the jmp instruction scince the value of ax is still 0febch.

bochs prints the message:
check_cs: non-conforming code seg descriptor dpl != cpl

but my kerel runs at ring 0 !?

does anyone know why i cant jum to this code?

Thank you.

Re:GPF on jum to less privalged code

Posted: Fri May 19, 2006 2:48 pm
by bluecode
hi,

I don't think it is possible to just jump to a less priviledged code!
You have to at least add a TSS and do software or hardware based task switching to execute some cpl3 code.

Re:GPF on jum to less privalged code

Posted: Fri May 19, 2006 5:16 pm
by Ryu
The GDT had conforming bit off, what was in the Intel manual:
Nonconforming code segment (without using a call gate): The DPL Indicates the privilege level that a program or task must be at to access the segment. For an example if the DPL of a nonconforming code segment is 0, only programs running at CPL of 0 can access the segment.

Try setting all GDTs to conforming and create a callgate descriptor for the destination DPL 3 descriptor.

edit: Just to add to this, you can use a nonconforming segment to access a conforming segment if you have a call gate style, so basically the call gate descripter should be the same DPL as the nonconforming CPL and must be nonconforming. But this wouldn't make sense to me, as you are in CPL 0 segment trying to get into a DPL 3, you should be using a conforming descriptor, and DPL 3 descriptor should also be conforming. If your in a nonconforming segment your stuck in the same CPL and cannot enter conforming segments even at the same privilege level, otherwise GPF will get you.

edit: Okay, after reading it throughly, I was mixed up with CPL 0 and lowest privilege ???.

Re:GPF on jum to less privalged code

Posted: Mon May 22, 2006 1:29 pm
by ed_tait
hello again,

i've looked into task switching and have sucessfully immplemnted task switching between kernel threads

how ever, i still cant switch from the kernel to a user therte useing the change stack -> iret system, bochs produces the same error:

check_cs: non-conforming code seg descriptor dpl != cpl

the following things are on the stack before the iret:

Code: Select all

ss ; 28h
esp ;100000h
eflags  ; 0
cs ; 20h
eip ; task
has any one got any ideas what i'm doing wrong?

thank you.

Re:GPF on jum to less privalged code

Posted: Mon May 22, 2006 2:16 pm
by Brendan
Hi,
ed_tait wrote:how ever, i still cant switch from the kernel to a user therte useing the change stack -> iret system, bochs produces the same error:

check_cs: non-conforming code seg descriptor dpl != cpl

the following things are on the stack before the iret:

Code: Select all

ss ; 28h
esp ;100000h
eflags  ; 0
cs ; 20h
eip ; task
The CPU's CPL is stored in the lowest 2 bits of the CS register and the lowest 2 bits of the SS register. The DPL comes from the descriptor in the GDT. If the DPL in the descriptor is set to 3, then you'd need to set CPL to 3 in CS and SS.

Bascially, CS on the stack should be 0x23 and SS on the stack should be 0x2B (and they must both point to segment descriptors that have DPL=3).


Cheers,

Brendan

Re:GPF on jum to less privalged code

Posted: Mon May 22, 2006 2:39 pm
by mystran
Also, I'm not quite sure you should use EFLAGS=0 while it's unlike to have anything much having to do with this.

Basicly, bit 1 (counting from 0) should always be set to 1. IIRC it's hardwired anyway, but it's good idea to set it in any case, if for nothing else, then future compability. You can't know what it's going to mean tomorrow.

I'd also set IF and AC, but that naturally depends.

Anyway, if you want clear set of flags, then use EFLAGS=2, so it conforms with what the holy manuals rule.

Re:GPF on jum to less privalged code

Posted: Tue May 23, 2006 1:04 am
by Pype.Clicker
The whole processor is organized so that you only jump to higher privilege levels. Task switch may of course bypass this, but not "non-conforming code segments". The purpose of non-conforming segments is to be able to have a part of code (presumably a library) that can be invoked from any CPL with a single segment. E.g. if you're at DPL2, the new segment will run with privilege level 2 ... if you're at CPL1, it will run with privilege 1, etc.
This has virtually no use nowadays and i wonder whether it has ever been used ...

maybe prior the addition of paging ...

What people usually do when they want their kernel to start user-level code is to push a "return state" on the kernel stack and then do an IRET, so that the CPU believes you're returning to user code that previously called your kernel. That does work.

Re:GPF on jum to less privalged code

Posted: Tue May 23, 2006 10:31 am
by Ryu
When I looked at the little doodles in the Intel manuals, I was wondering where they ever apply a non-conforming segment to the real world. I'm actually curious to know if anyone made use of it.

Re:GPF on jum to less privalged code

Posted: Tue May 23, 2006 10:42 am
by paulbarker
The only place I could see a use for this is a core library which contains functions like abs(), etc. which can run unchanged in user mode or kernel mode. I might make use of this in my OS, I might not... I'll cross that bridge when I get to it.

Re:GPF on jum to less privalged code

Posted: Tue May 23, 2006 1:35 pm
by ed_tait
hello again.

i think i've made progress, i've added a tss for switches to ring 3.

am i supposed to execute the ltr instruction just before the switch or in the initiation phase?

should i use the sellector for the tss in the gdt like so:

Code: Select all

ltr [30h] ; tss selector
if so i get a gpf:

fetch_raw_descriptor: GDT: index (ff57)1fea > limit (37)

my gdt entry for the tss looks like so:

Code: Select all

 gdt_tss:                     
        dw 104 ; 104 bytes
        dw 0
        db 10000b
        db 01011001b
        db 00001001b
        db 0b
;
; the tss itself looks like this:
;

       backlink  dw 0
        __blh dw 0
       
       esp0 dd   0
       ss0 dw   0
       __ss0h dw 0
       
       esp1 dd 0   
       ss1   dw 0   
       __ss1h dw 0
       
       esp2 dd   0 
       ss2 dw   0
       __ss2h dw 0 
      
      _cr3  dd 0   
      eip dd  0   
      eflags dd 0   
      _eax dd    0
      _ecx dd    0
      _edx dd    0
      _ebx dd    0
      _esp dd    0 
      _ebp dd    0
      _esi dd    0
      _edi dd    0
      _es dw    0
      __esh dw  0
      _cs dw     0
       __csh dw 0
      _ss dw     0
       __ssh dw 0
      _ds dw     0
      __dsh dw  0
      _fs dw     0
       __fsh dw 0
      _gs dw     0
      __gsh dw  0
      ldt dw      0
       __ldth dw 0
      trace dw   0
       bitmap dw 0
thank you.

Re:GPF on jum to less privalged code

Posted: Tue May 23, 2006 2:13 pm
by Ryu
paulbarker wrote: The only place I could see a use for this is a core library which contains functions like abs(), etc. which can run unchanged in user mode or kernel mode. I might make use of this in my OS, I might not... I'll cross that bridge when I get to it.
My growing standard C runtime library (about 16 routines so far) all follow DS=ES,SS,CS = nominal base rule, this allows me to use the library in windows developement too. :)

I think I have uses for non-conforming segments because I have high hopes for my kernel to run non-paging privilege level 0 programs and keeping it stable.

Re:GPF on jum to less privalged code

Posted: Tue May 23, 2006 6:12 pm
by mystran
Where you load your TR depends on how you do thread swithing. If you do it in software, you can load it once when you init TSS (I do it like that) and then just change the esp0 in it, since that's pretty much all you ever need, although ss0 needs a sane (constant) value too.

As for hardware switching, somebody that uses it can tell, I've forgot all about it (on purpose, really).

Re:GPF on jum to less privalged code

Posted: Wed May 24, 2006 1:01 am
by ed_tait
if you use sack based task switching do you need a tss or can you store esp0 in another way?

Re:GPF on jum to less privalged code

Posted: Wed May 24, 2006 2:32 am
by mystran
You need one TSS per processor to hold whatever is the current esp0. For system calls there's another way with sufficiently new processors, but you still need to handle interrupts and exceptions, so you still need TSS anyway.

Re:GPF on jum to less privalged code

Posted: Wed May 24, 2006 4:21 am
by paulbarker
The correct places to use TSS's in stack based switching are 1 per processor holding esp0/ss0, one for a double fault handler and one for an NMI handler.

I know this is going off topic but if I have 1 double fault handler for the entire system, what would happen if a processor tried to doube fault at the same time as another (so the busy flag for the TSS is set)? Would this cause a triple fault (reset) or would the 2nd processor wait for the first to finish?