Greetings all -
I'm trying to task-switch from kernel to a user app. I loaded and ran the app in ring 0 and it seems to work fine. When I go and change it to ring 3 it fails. I tracked the problem down to where the task-switching code is loading the app's DPL3 stack segment into SS, which for some reason causes a GPF. Note that this worked fine when it was a DPL0 stack segment.
Since ring 0 is more privileged than ring 3 I think that what its doing should be valid. I go to change my kernel's stack segment to DPL3, and it also yields a GPF.
Is there a reason why the kernel wouldn't able to use a DPL3 stack segment?
Kernel utilizing a DPL3 stack segment?
Re:Kernel utilizing a DPL3 stack segment?
Yes, because it's DPL3. CPL0 code must use a DPL0 stack.
Re:Kernel utilizing a DPL3 stack segment?
How can the task switcher (CPL0) switch to a ring 3 app then? Chicken and egg problem: It needs to load the PL3 stack in order to pop off the ring 3 code segment... (?)
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Kernel utilizing a DPL3 stack segment?
the ring3 app has a dpl0 stack that is used when it calls (or is interrupted by) the kernel. Switches occurs between those 'kernal stacks', and when the switch is complete, the kernel returns to the normal APP that called it whith IRET.
Re:Kernel utilizing a DPL3 stack segment?
Specifically, the kernel loads the ring 0 SS and ESP from the TSS when an interrupt from ring 3 to ring 0 occurs.
In general, an interrupt from ring n to ring m, where n > m (n is less privileged than m), causes the CPU to load SSm and ESPm. The CPU does not update SSm and ESPm on IRET.
In general, an interrupt from ring n to ring m, where n > m (n is less privileged than m), causes the CPU to load SSm and ESPm. The CPU does not update SSm and ESPm on IRET.
Re:Kernel utilizing a DPL3 stack segment?
Thanks for the info about a ring3->ring0 switch, I didn't know that before... but I was originally asking about a ring0->ring3 switch.
Since ring0 code cannot load a ring3 SS, how do I perform a software task switch? It seems like I need to load both the ring3 CS and SS at the same time which is impossible.
Or am I missing something?
Since ring0 code cannot load a ring3 SS, how do I perform a software task switch? It seems like I need to load both the ring3 CS and SS at the same time which is impossible.
Or am I missing something?
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Kernel utilizing a DPL3 stack segment?
Let's say you're loading a new user process. It will have a DPL0 stack (for IRQ, EXC and SYSCALLs) and a DPL3 stack (for the user process)
You will set your task object so that it claims the active stack is SS0 and active code segment is CS0, *but* you will set up the SS0 stack content so that it will return to the user stack when a switch occur.
For instance, let's say you have
Initially, the task's DPL0 stack will have zeroes for the "popa" instruction and will have the address of "end_of_interrupt" for the "ret" instruction.
Once you returned to end_of_interrupt, the DPL0 stack will also have values of segment registers that will fit the expectations of the user process, and IRET will find the user-level code segment as well as the user program entry point and the segment & offset of the user-level stack.
So there is no real DPL0->DPL3 switch: all the task switches are DPL0->DPL0 and then the DPL0 code returns to the interrupted process.
If the user process was never interrupted, well ... you don't care and make the processor believe it was
You will set your task object so that it claims the active stack is SS0 and active code segment is CS0, *but* you will set up the SS0 stack content so that it will return to the user stack when a switch occur.
For instance, let's say you have
Code: Select all
;; from task in eax, target task in edx
task_switch:
pusha
mov [eax+4],ss
mov [eax],esp
lss esp,[edx]
popa
ret
end_of_interrupt:
pop fs
pop gs
pop es
pop ds
iret
Once you returned to end_of_interrupt, the DPL0 stack will also have values of segment registers that will fit the expectations of the user process, and IRET will find the user-level code segment as well as the user program entry point and the segment & offset of the user-level stack.
So there is no real DPL0->DPL3 switch: all the task switches are DPL0->DPL0 and then the DPL0 code returns to the interrupted process.
If the user process was never interrupted, well ... you don't care and make the processor believe it was
Re:Kernel utilizing a DPL3 stack segment?
Ah... ingenius! Thank you for explaining.
There's just one thing: I thought IRET only pops off EFLAGS and CS:EIP. So if the method you describe indeed works, I assume that when there is a privilege change it pops the following in order:
EIP
CS
EFLAGS
SS
ESP
Is this correct?
There's just one thing: I thought IRET only pops off EFLAGS and CS:EIP. So if the method you describe indeed works, I assume that when there is a privilege change it pops the following in order:
EIP
CS
EFLAGS
SS
ESP
Is this correct?
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Kernel utilizing a DPL3 stack segment?
better check the intel manuals (and i don't have 'm here), but i wonder if SS isn't the last thing popped.
What happens is that the CPU can detect the "return back to user mode" because it tries to restore a CS value which has a DPL value different from the current DPL.
What happens is that the CPU can detect the "return back to user mode" because it tries to restore a CS value which has a DPL value different from the current DPL.