[SeaBIOS] [PATCH 11/12] Fully restore 16bit state during call16_sloppy()

Kevin O'Connor kevin at koconnor.net
Tue Sep 30 20:38:45 CEST 2014


When transitioning back to 16bit mode from within call32(), restore
the full state (cmos index, gdt, fs/gs) in addition to restoring the
original stack.

Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
---
 src/stacks.c | 93 ++++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 63 insertions(+), 30 deletions(-)

diff --git a/src/stacks.c b/src/stacks.c
index 2a7be06..eee658f 100644
--- a/src/stacks.c
+++ b/src/stacks.c
@@ -21,29 +21,64 @@
  * 16bit / 32bit calling
  ****************************************************************/
 
-u16 StackSeg VARLOW;
-u8 Call32Method VARLOW;
+struct {
+    u8 method;
+    u8 cmosindex;
+    u16 ss, fs, gs;
+    struct descloc_s gdt;
+} Call32Data VARLOW;
+
 #define C32_SLOPPY 1
 
-// Call a C function in 32bit mode.  This clobbers the 16bit segment
-// selector registers.
-static u32
-call32_sloppy(void *func, u32 eax)
+// Backup state in preparation for call32_sloppy()
+static void
+call32_sloppy_prep(void)
 {
-    ASSERT16();
     // Backup cmos index register and disable nmi
     u8 cmosindex = inb(PORT_CMOS_INDEX);
     outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
     inb(PORT_CMOS_DATA);
+    SET_LOW(Call32Data.cmosindex, cmosindex);
 
-    // Backup fs/gs and gdt
-    u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
+    // Backup ss/fs/gs and gdt
+    SET_LOW(Call32Data.ss, GET_SEG(SS));
+    SET_LOW(Call32Data.fs, GET_SEG(FS));
+    SET_LOW(Call32Data.gs, GET_SEG(GS));
     struct descloc_s gdt;
     sgdt(&gdt);
+    SET_LOW(Call32Data.gdt.length, gdt.length);
+    SET_LOW(Call32Data.gdt.addr, gdt.addr);
+
+    SET_LOW(Call32Data.method, C32_SLOPPY);
+}
 
-    u16 oldstackseg = GET_LOW(StackSeg);
-    SET_LOW(StackSeg, GET_SEG(SS));
-    SET_LOW(Call32Method, C32_SLOPPY);
+// Restore state backed up during call32_sloppy()
+static void
+call32_sloppy_post(void)
+{
+    SET_LOW(Call32Data.method, 0);
+    SET_LOW(Call32Data.ss, 0);
+
+    // Restore gdt and fs/gs
+    struct descloc_s gdt;
+    gdt.length = GET_LOW(Call32Data.gdt.length);
+    gdt.addr = GET_LOW(Call32Data.gdt.addr);
+    lgdt(&gdt);
+    SET_SEG(FS, GET_LOW(Call32Data.fs));
+    SET_SEG(GS, GET_LOW(Call32Data.gs));
+
+    // Restore cmos index register
+    outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX);
+    inb(PORT_CMOS_DATA);
+}
+
+// Call a C function in 32bit mode.  This clobbers the 16bit segment
+// selector registers.
+static u32
+call32_sloppy(void *func, u32 eax)
+{
+    ASSERT16();
+    call32_sloppy_prep();
     u32 bkup_ss, bkup_esp;
     asm volatile(
         // Backup ss/esp / set esp to flat stack location
@@ -69,21 +104,20 @@ call32_sloppy(void *func, u32 eax)
         : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
         : "r" (func)
         : "ecx", "edx", "cc", "memory");
-
-    SET_LOW(Call32Method, 0);
-    SET_LOW(StackSeg, oldstackseg);
-
-    // Restore gdt and fs/gs
-    lgdt(&gdt);
-    SET_SEG(FS, fs);
-    SET_SEG(GS, gs);
-
-    // Restore cmos index register
-    outb(cmosindex, PORT_CMOS_INDEX);
-    inb(PORT_CMOS_DATA);
+    call32_sloppy_post();
     return eax;
 }
 
+// 16bit handler code called from call16_sloppy()
+u32 VISIBLE16
+call16_sloppy_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx))
+{
+    call32_sloppy_post();
+    u32 ret = func(eax, edx);
+    call32_sloppy_prep();
+    return ret;
+}
+
 // Jump back to 16bit mode while in 32bit mode from call32_sloppy()
 u32 FUNCFSEG
 call16_sloppy(u32 eax, u32 edx, void *func)
@@ -92,8 +126,7 @@ call16_sloppy(u32 eax, u32 edx, void *func)
     if (getesp() > MAIN_STACK_MAX)
         panic("call16_sloppy with invalid stack\n");
     func -= BUILD_BIOS_ADDR;
-    Call32Method = 0;
-    u32 stackseg = GET_LOW(StackSeg);
+    u32 stackseg = Call32Data.ss;
     asm volatile(
         // Transition to 16bit mode
         "  movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n"
@@ -106,7 +139,8 @@ call16_sloppy(u32 eax, u32 edx, void *func)
         "  subl %3, %%esp\n"
         "  movw %%cx, %%ds\n"
         "  movl %2, %%edx\n"
-        "  calll *%1\n"
+        "  movl %1, %%ecx\n"
+        "  calll _cfunc16_call16_sloppy_helper\n"
         // Return to 32bit and restore esp
         "  movl $2f, %%edx\n"
         "  jmp transition32\n"
@@ -115,7 +149,6 @@ call16_sloppy(u32 eax, u32 edx, void *func)
         : "+a" (eax)
         : "r" (func), "r" (edx), "r" (stackseg)
         : "edx", "ecx", "cc", "memory");
-    Call32Method = C32_SLOPPY;
     return eax;
 }
 
@@ -190,7 +223,7 @@ static u32
 call16_back(u32 eax, u32 edx, void *func)
 {
     ASSERT32FLAT();
-    if (Call32Method == C32_SLOPPY)
+    if (Call32Data.method == C32_SLOPPY)
         return call16_sloppy(eax, edx, func);
     return call16(eax, edx, func);
 }
@@ -322,7 +355,7 @@ __call16_int(struct bregs *callregs, u16 offset)
     callregs->code.offset = offset;
     if (!MODESEGMENT) {
         callregs->code.seg = SEG_BIOS;
-        _farcall16((void*)callregs - StackSeg * 16, StackSeg);
+        _farcall16((void*)callregs - Call32Data.ss * 16, Call32Data.ss);
         return;
     }
     callregs->code.seg = GET_SEG(CS);
-- 
1.9.3




More information about the SeaBIOS mailing list