XCHG wrote:urxae wrote:
Also note that iretd works differently when switching between privilege levels (the "esp" and "ss" fields are only used when the "cs" field indicates a switch to a less-privileged ring).
That was exactly what I needed to know. I don't remember reading that anywhere in the Intel Manuals even in their Instruction Manuals!
It's in there. In my copy it's in 5.12.1 (Interrupt and Exception Handling/Exception and Interrupt Handling/Exception- or Interrupt-Handler procedures), both in the text and figure 5-4.
So what is the SS and ESP for? Should that be set to kernel's stack? Do we have to put anything in the kernel's stack before switching to the user's stack? Could you be more specific?
Thank you in advance.
No, those fields of the structure should contain the user-mode stack pointer and segment. Like I said,
urxae wrote:When that's the next thing on the stack, you can just execute an iretd instruction to jump to your ring 3 process, simultaneously loading above fields into the registers of the same name.
so the values of those fields will be the values of the respective registers after the iretd instruction. The code will be executing at
cs:eip, with stack at
ss:esp and flags set to
eflags
For example if you have the desired values of those registers in some variables, one way to switch to ring 3 code would be:
Code: Select all
push [user_ss]
push [user_esp]
push [user_eflags]
push [user_cs]
push [user_eip]
iretd
This technique basically "fakes" the structures set up by an inter-privilege level interrupt and then "returns" from it.
Above code is not exactly how I do it, by the way.
I only use something akin to the above for creating new processes. And even then, I use regular memory accesses to set it up, and also recreate the stack layout of my stack switching function below that, with the return address set to a stub that clears some registers and performs the iretd.
That way I can add it to my scheduler queue and later use my regular context-switch function to switch to it.
After that, the only way that thread ever gets to a switchable state is by switching to another thread from an interrupt or exception handler, using the same context-switch function.
That context-switch function just pushes a lot of registers, switches to another kernel stack, pops the same registers (in reverse order, of course) and returns (using the return address on the
new stack).
That return address will be whatever function last called the context-switch function to go to another thread (for "old" threads) or above-mentioned iretd-ing stub function.
("old" threads then typically continue returning down to the handler for whatever interrupt last occured in that thread and return from it, getting back to whatever code that thread was executing)