Longmode Ring0->Ring3 Issues
Posted: Fri Dec 03, 2010 6:01 pm
I have got basic multitasking working now on my OS, and i can create/delete threads from the taskmanager, this works 100% in long mode ring0! However, now i am attempting to get to userland, and am having a somewhat frustrating issue.
here is how i am trying to do the task switch to ring3
Set up the TSS and load the TSS into the 64bit TSS Descriptor in the GDT (with the DPL in the descriptor as 3)
Load the TSS with the LTR instruction, which does not throw any exceptions
(the TSS is empty except for the RSP0 field which does have a correct address)
then i push SS = 0
push RSP = allocated and user priv read/write
push EFLAGS = 0x200 (enable ints)
push CS = 0x23 (yes that is the correct GDT entry for DPL = 3 long mode)
push RIP = this does have the correct address
iretq
Now what happens is no matter what value is put in SS it throws a GPF 0xD with the selector index as the error code, 0x3 = 0, 0 = 0, 0x23 throws an error code 0x20 and so on. I know its definitely an issue with the value in SS because if i try and make CS 0x22, it will except with CS = 0x20 as the error code. (and i was under the impression SS had to be null which according to the AMD manual forces a CPL switch to the CS RPL as if it had interrupted from ring3 in the first place? Or, I read wrong)
SO! What should SS really be? or am i doing something wrong elsewhere?
Some ideas could be handy.
Ok so here is what i have found myself:
What i fail to understand, is, there is no data segments in long mode, so what the heck am i putting in SS before executing iret? an index to a long mode segment with rpl = 0 or rpl = 3???
Cheers,
iLewis
here is how i am trying to do the task switch to ring3
Set up the TSS and load the TSS into the 64bit TSS Descriptor in the GDT (with the DPL in the descriptor as 3)
Load the TSS with the LTR instruction, which does not throw any exceptions
(the TSS is empty except for the RSP0 field which does have a correct address)
then i push SS = 0
push RSP = allocated and user priv read/write
push EFLAGS = 0x200 (enable ints)
push CS = 0x23 (yes that is the correct GDT entry for DPL = 3 long mode)
push RIP = this does have the correct address
iretq
Now what happens is no matter what value is put in SS it throws a GPF 0xD with the selector index as the error code, 0x3 = 0, 0 = 0, 0x23 throws an error code 0x20 and so on. I know its definitely an issue with the value in SS because if i try and make CS 0x22, it will except with CS = 0x20 as the error code. (and i was under the impression SS had to be null which according to the AMD manual forces a CPL switch to the CS RPL as if it had interrupted from ring3 in the first place? Or, I read wrong)
SO! What should SS really be? or am i doing something wrong elsewhere?
Some ideas could be handy.
Ok so here is what i have found myself:
Ok... so this means if i am switching to ring3 i must load SS with a non null value...In IA-32e mode, IRET is allowed to load a NULL SS under certain conditions. If the
target mode is 64-bit mode and the target CPL <> 3, IRET allows SS to be loaded
with a NULL selector.
Um.. So wait.. current SS before i execute the IRET should be null with a RPL = 0 for CPL = ring0, and, SS on the iret stack should be... ??? when returning to a higher CPL? *head explodes*When stacks are switched as
part of a 64-bit mode privilege-level change (resulting from an interrupt), a new SS
descriptor is not loaded. IA-32e mode loads only an inner-level RSP from the TSS.
The new SS selector is forced to NULL and the SS selector’s RPL field is set to the new
CPL. The new SS is set to NULL in order to handle nested far transfers (CALLF, INT,
interrupts and exceptions). The old SS and RSP are saved on the new stack
(Figure 6-8). On the subsequent IRET, the old SS is popped from the stack and
loaded into the SS register.
What i fail to understand, is, there is no data segments in long mode, so what the heck am i putting in SS before executing iret? an index to a long mode segment with rpl = 0 or rpl = 3???
Cheers,
iLewis