On Tue, Dec 07, 2010 at 05:14:11PM -0800, H. Peter Anvin wrote:
First of all, sorry for the very late reply, however, I thought I really ought to offer my perspective on this:
On 11/25/2010 06:24 AM, Kevin O'Connor wrote:
It's difficult to have a uniform view of the stack when transition modes, so pass the return address in a register. As a result, the transition functions only access memory via the %cs selector now.
I think this assertion is rather unfortunate, because my own experience with thunking is that it is actually a very useful thing to have access to the real-mode stack.
This is simply accomplished by computing a 32-bit register containing the value (ss << 4) + sp, for example:
movzwl %sp, %eax movl %ss, %ecx shrl $4, %ecx addl %ecx, %eax
This is basically what the stacks.c:call32() code does.
This is particularly handy if there is a push/pop of the 16-bit register set in the entry/exit sequence. Furthermore, pushing the target address onto the stack rather than stuffing it into a register allows a 32-bit routine to have full access to the 16-bit register image, whereas burning a register means that that register is going to have to be handled differently.
In Syslinux I have this formalized so that the sequence:
pushl $func32 callw _pm_call
This is similar to what SeaBIOS used to do - it had: "pushl $func32; jmp transition32" and "pushl $func16; jmp transition16".
The problem with this is that I can't use "popl" to get the destination address in transition16 because a popl in 16bit mode only looks at %sp and not %esp. So, if %esp==0x90000 and I do "pushl $func16; transition16", then when transition16 does a "retl" (or "popl") then it ends up pulling the address at 0x0000 instead of 0x90000.
You might ask why transition16 doesn't restore %ss/%sp - but that's what the caller (stacks.c:call32) does. So, the real mode stack is available, it's just not the task of transition16.
[...]
typedef struct { uint16_t gs; /* Offset 0 */ uint16_t fs; /* Offset 2 */
[...]
reg32_t eflags; /* Offset 40 */
} com32sys_t;
That's basically the same thing as SeaBIOS's "struct bregs" in bregs.h. The 16bit entry points back up the register state, pass it into the C code (which can then modify the registers if needed), and then restore the register state on return.
[...]
_pm_call:
[...]
mov ebx,.pm mov ds,ax jmp enter_pm
This looks very similar to what SeaBIOS has now - _pm_call is the equivalent of the inline asm in call32() and enter_pm is the equivalent of transtion32. See:
http://git.linuxtogo.org/?p=kevin/seabios.git;a=blob;f=src/stacks.c;h=c77ba1...
and:
http://git.linuxtogo.org/?p=kevin/seabios.git;a=blob;f=src/romlayout.S;h=d83...
-Kevin