These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / seabios / src / stacks.c
index 1dbdfe9..ef6a707 100644 (file)
@@ -1,6 +1,6 @@
 // Code for manipulating stack locations.
 //
-// Copyright (C) 2009-2014  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2009-2015  Kevin O'Connor <kevin@koconnor.net>
 //
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 
@@ -13,6 +13,7 @@
 #include "output.h" // dprintf
 #include "romfile.h" // romfile_loadint
 #include "stacks.h" // struct mutex_s
+#include "string.h" // memset
 #include "util.h" // useRTC
 
 #define MAIN_STACK_MAX (1024*1024)
@@ -27,40 +28,108 @@ struct {
     u8 cmosindex;
     u8 a20;
     u16 ss, fs, gs;
+    u32 cr0;
     struct descloc_s gdt;
-} Call32Data VARLOW;
+} Call16Data VARLOW;
 
-#define C32_SLOPPY 1
-#define C32_SMM    2
+#define C16_BIG 1
+#define C16_SMM 2
 
 int HaveSmmCall32 VARFSEG;
 
-// Backup state in preparation for call32_smm()
-static void
-call32_smm_prep(void)
-{
+// Backup state in preparation for call32
+static int
+call32_prep(u8 method)
+{
+    if (!CONFIG_CALL32_SMM || method != C16_SMM) {
+        // Backup cr0
+        u32 cr0 = cr0_read();
+        if (cr0 & CR0_PE)
+            // Called in 16bit protected mode?!
+            return -1;
+        SET_LOW(Call16Data.cr0, cr0);
+
+        // Backup fs/gs and gdt
+        SET_LOW(Call16Data.fs, GET_SEG(FS));
+        SET_LOW(Call16Data.gs, GET_SEG(GS));
+        struct descloc_s gdt;
+        sgdt(&gdt);
+        SET_LOW(Call16Data.gdt.length, gdt.length);
+        SET_LOW(Call16Data.gdt.addr, gdt.addr);
+
+        // Enable a20 and backup its previous state
+        SET_LOW(Call16Data.a20, set_a20(1));
+    }
+
+    // Backup ss
+    SET_LOW(Call16Data.ss, GET_SEG(SS));
+
     // 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 ss
-    SET_LOW(Call32Data.ss, GET_SEG(SS));
+    SET_LOW(Call16Data.cmosindex, cmosindex);
 
-    SET_LOW(Call32Data.method, C32_SMM);
+    SET_LOW(Call16Data.method, method);
+    return 0;
 }
 
-// Restore state backed up during call32_smm()
-static void
-call32_smm_post(void)
+// Restore state backed up during call32
+static u8
+call32_post(void)
 {
-    SET_LOW(Call32Data.method, 0);
-    SET_LOW(Call32Data.ss, 0);
+    u8 method = GET_LOW(Call16Data.method);
+    SET_LOW(Call16Data.method, 0);
+    SET_LOW(Call16Data.ss, 0);
+
+    if (!CONFIG_CALL32_SMM || method != C16_SMM) {
+        // Restore a20
+        set_a20(GET_LOW(Call16Data.a20));
+
+        // Restore gdt and fs/gs
+        struct descloc_s gdt;
+        gdt.length = GET_LOW(Call16Data.gdt.length);
+        gdt.addr = GET_LOW(Call16Data.gdt.addr);
+        lgdt(&gdt);
+        SET_SEG(FS, GET_LOW(Call16Data.fs));
+        SET_SEG(GS, GET_LOW(Call16Data.gs));
+
+        // Restore cr0
+        u32 cr0_caching = GET_LOW(Call16Data.cr0) & (CR0_CD|CR0_NW);
+        if (cr0_caching)
+            cr0_mask(CR0_CD|CR0_NW, cr0_caching);
+    }
 
     // Restore cmos index register
-    outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX);
+    outb(GET_LOW(Call16Data.cmosindex), PORT_CMOS_INDEX);
     inb(PORT_CMOS_DATA);
+    return method;
+}
+
+// Force next call16() to restore to a pristine cpu environment state
+static void
+call16_override(int big)
+{
+    ASSERT32FLAT();
+    if (getesp() > BUILD_STACK_ADDR)
+        panic("call16_override with invalid stack\n");
+    memset(&Call16Data, 0, sizeof(Call16Data));
+    if (big) {
+        Call16Data.method = C16_BIG;
+        Call16Data.a20 = 1;
+    } else {
+        Call16Data.a20 = !CONFIG_DISABLE_A20;
+    }
+}
+
+// 16bit handler code called from call16() / call16_smm()
+u32 VISIBLE16
+call16_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx))
+{
+    u8 method = call32_post();
+    u32 ret = func(eax, edx);
+    call32_prep(method);
+    return ret;
 }
 
 #define ASM32_SWITCH16 "  .pushsection .text.32fseg." UNIQSEC "\n  .code16\n"
@@ -74,7 +143,7 @@ call32_smm(void *func, u32 eax)
 {
     ASSERT16();
     dprintf(9, "call32_smm %p %x\n", func, eax);
-    call32_smm_prep();
+    call32_prep(C16_SMM);
     u32 bkup_esp;
     asm volatile(
         // Backup esp / set esp to flat stack location
@@ -109,24 +178,12 @@ call32_smm(void *func, u32 eax)
         : "=&r" (bkup_esp), "+r" (eax)
         : "r" (func)
         : "eax", "ecx", "edx", "ebx", "cc", "memory");
-    call32_smm_post();
+    call32_post();
 
     dprintf(9, "call32_smm done %p %x\n", func, eax);
     return eax;
 }
 
-// 16bit handler code called from call16_smm()
-u32 VISIBLE16
-call16_smm_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx))
-{
-    if (!CONFIG_CALL32_SMM)
-        return eax;
-    call32_smm_post();
-    u32 ret = func(eax, edx);
-    call32_smm_prep();
-    return ret;
-}
-
 static u32
 call16_smm(u32 eax, u32 edx, void *func)
 {
@@ -135,7 +192,7 @@ call16_smm(u32 eax, u32 edx, void *func)
         return eax;
     func -= BUILD_BIOS_ADDR;
     dprintf(9, "call16_smm %p %x %x\n", func, eax, edx);
-    u32 stackoffset = Call32Data.ss << 4;
+    u32 stackoffset = Call16Data.ss << 4;
     asm volatile(
         // Restore esp
         "  subl %0, %%esp\n"
@@ -151,7 +208,7 @@ call16_smm(u32 eax, u32 edx, void *func)
         ASM32_SWITCH16
         "1:movl %1, %%eax\n"
         "  movl %3, %%ecx\n"
-        "  calll _cfunc16_call16_smm_helper\n"
+        "  calll _cfunc16_call16_helper\n"
         "  movl %%eax, %1\n"
 
         "  movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n"
@@ -170,61 +227,18 @@ call16_smm(u32 eax, u32 edx, void *func)
     return eax;
 }
 
-// Backup state in preparation for call32_sloppy()
-static void
-call32_sloppy_prep(void)
-{
-    // 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);
-
-    // Enable a20 and backup it's previous state
-    SET_LOW(Call32Data.a20, set_a20(1));
-
-    // 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);
-}
-
-// 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 a20
-    set_a20(GET_LOW(Call32Data.a20));
-
-    // 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)
+// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
+u32 VISIBLE16
+__call32(void *func, u32 eax, u32 errret)
 {
     ASSERT16();
-    call32_sloppy_prep();
+    if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32))
+        return call32_smm(func, eax);
+    // Jump direclty to 32bit mode - this clobbers the 16bit segment
+    // selector registers.
+    int ret = call32_prep(C16_BIG);
+    if (ret)
+        return errret;
     u32 bkup_ss, bkup_esp;
     asm volatile(
         // Backup ss/esp / set esp to flat stack location
@@ -236,7 +250,7 @@ call32_sloppy(void *func, u32 eax)
 
         // Transition to 32bit mode, call func, return to 16bit
         "  movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
-        "  jmp transition32\n"
+        "  jmp transition32_nmi_off\n"
         ASM16_SWITCH32
         "1:calll *%3\n"
         "  movl $2f, %%edx\n"
@@ -250,136 +264,52 @@ call32_sloppy(void *func, u32 eax)
         : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
         : "r" (func)
         : "ecx", "edx", "cc", "memory");
-    call32_sloppy_post();
+    call32_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()
+// Call a 16bit SeaBIOS function, restoring the mode from last call32().
 static u32
-call16_sloppy(u32 eax, u32 edx, void *func)
+call16(u32 eax, u32 edx, void *func)
 {
     ASSERT32FLAT();
     if (getesp() > MAIN_STACK_MAX)
-        panic("call16_sloppy with invalid stack\n");
+        panic("call16 with invalid stack\n");
+    if (CONFIG_CALL32_SMM && Call16Data.method == C16_SMM)
+        return call16_smm(eax, edx, func);
+
+    extern void transition16big(void);
+    extern void transition16(void);
+    void *thunk = transition16;
+    if (Call16Data.method == C16_BIG || in_post())
+        thunk = transition16big;
     func -= BUILD_BIOS_ADDR;
-    u32 stackseg = Call32Data.ss;
+    u32 stackseg = Call16Data.ss;
     asm volatile(
         // Transition to 16bit mode
         "  movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n"
-        "  jmp transition16big\n"
+        "  jmp *%%ecx\n"
         // Setup ss/esp and call func
         ASM32_SWITCH16
-        "1:movl %3, %%ecx\n"
-        "  shll $4, %3\n"
+        "1:movl %2, %%ecx\n"
+        "  shll $4, %2\n"
         "  movw %%cx, %%ss\n"
-        "  subl %3, %%esp\n"
+        "  subl %2, %%esp\n"
         "  movw %%cx, %%ds\n"
-        "  movl %2, %%edx\n"
-        "  movl %1, %%ecx\n"
-        "  calll _cfunc16_call16_sloppy_helper\n"
+        "  movl %4, %%edx\n"
+        "  movl %3, %%ecx\n"
+        "  calll _cfunc16_call16_helper\n"
         // Return to 32bit and restore esp
         "  movl $2f, %%edx\n"
-        "  jmp transition32\n"
-        ASM32_BACK32
-        "2:addl %3, %%esp\n"
-        : "+a" (eax)
-        : "r" (func), "r" (edx), "r" (stackseg)
-        : "edx", "ecx", "cc", "memory");
-    return eax;
-}
-
-// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
-u32 VISIBLE16
-call32(void *func, u32 eax, u32 errret)
-{
-    ASSERT16();
-    if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32))
-        return call32_smm(func, eax);
-    u32 cr0 = getcr0();
-    if (cr0 & CR0_PE)
-        // Called in 16bit protected mode?!
-        return errret;
-    return call32_sloppy(func, eax);
-}
-
-// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function.
-static u32
-call16(u32 eax, u32 edx, void *func)
-{
-    ASSERT32FLAT();
-    if (getesp() > BUILD_STACK_ADDR)
-        panic("call16 with invalid stack\n");
-    func -= BUILD_BIOS_ADDR;
-    asm volatile(
-        // Transition to 16bit mode
-        "  movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n"
-        "  jmp transition16\n"
-        // Call func
-        ASM32_SWITCH16
-        "1:movl %2, %%edx\n"
-        "  calll *%1\n"
-        // Return to 32bit
-        "  movl $2f, %%edx\n"
-        "  jmp transition32\n"
-        ASM32_BACK32
-        "2:\n"
-        : "+a" (eax)
-        : "r" (func), "r" (edx)
-        : "edx", "ecx", "cc", "memory");
-    return eax;
-}
-
-// Call a 16bit SeaBIOS function in "big real" mode.
-static u32
-call16big(u32 eax, u32 edx, void *func)
-{
-    ASSERT32FLAT();
-    if (getesp() > BUILD_STACK_ADDR)
-        panic("call16big with invalid stack\n");
-    func -= BUILD_BIOS_ADDR;
-    asm volatile(
-        // Transition to 16bit mode
-        "  movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n"
-        "  jmp transition16big\n"
-        // Call func
-        ASM32_SWITCH16
-        "1:movl %2, %%edx\n"
-        "  calll *%1\n"
-        // Return to 32bit
-        "  movl $2f, %%edx\n"
-        "  jmp transition32\n"
+        "  jmp transition32_nmi_off\n"
         ASM32_BACK32
-        "2:\n"
-        : "+a" (eax)
+        "2:addl %2, %%esp\n"
+        : "+a" (eax), "+c"(thunk), "+r"(stackseg)
         : "r" (func), "r" (edx)
-        : "edx", "ecx", "cc", "memory");
+        : "edx", "cc", "memory");
     return eax;
 }
 
-// Call a 16bit SeaBIOS function, restoring the mode from last call32().
-static u32
-call16_back(u32 eax, u32 edx, void *func)
-{
-    ASSERT32FLAT();
-    if (CONFIG_CALL32_SMM && Call32Data.method == C32_SMM)
-        return call16_smm(eax, edx, func);
-    if (Call32Data.method == C32_SLOPPY)
-        return call16_sloppy(eax, edx, func);
-    if (in_post())
-        return call16big(eax, edx, func);
-    return call16(eax, edx, func);
-}
-
 
 /****************************************************************
  * Extra 16bit stack
@@ -398,7 +328,7 @@ on_extra_stack(void)
 
 // Switch to the extra stack and call a function.
 u32
-stack_hop(u32 eax, u32 edx, void *func)
+__stack_hop(u32 eax, u32 edx, void *func)
 {
     if (on_extra_stack())
         return ((u32 (*)(u32, u32))func)(eax, edx);
@@ -431,10 +361,10 @@ stack_hop(u32 eax, u32 edx, void *func)
 
 // Switch back to original caller's stack and call a function.
 u32
-stack_hop_back(u32 eax, u32 edx, void *func)
+__stack_hop_back(u32 eax, u32 edx, void *func)
 {
     if (!MODESEGMENT)
-        return call16_back(eax, edx, func);
+        return call16(eax, edx, func);
     if (!MODE16 || !on_extra_stack())
         return ((u32 (*)(u32, u32))func)(eax, edx);
     ASSERT16();
@@ -474,8 +404,7 @@ void VISIBLE16
 _farcall16(struct bregs *callregs, u16 callregseg)
 {
     if (need_hop_back()) {
-        extern void _cfunc16__farcall16(void);
-        stack_hop_back((u32)callregs, callregseg, _cfunc16__farcall16);
+        stack_hop_back(_farcall16, callregs, callregseg);
         return;
     }
     ASSERT16();
@@ -486,18 +415,20 @@ _farcall16(struct bregs *callregs, u16 callregseg)
         : "ebx", "ecx", "esi", "edi", "cc", "memory");
 }
 
+// Invoke external 16bit code.
 void
 farcall16(struct bregs *callregs)
 {
-    extern void _cfunc16__farcall16(void);
-    call16((u32)callregs, 0, _cfunc16__farcall16);
+    call16_override(0);
+    _farcall16(callregs, 0);
 }
 
+// Invoke external 16bit code in "big real" mode.
 void
 farcall16big(struct bregs *callregs)
 {
-    extern void _cfunc16__farcall16(void);
-    call16big((u32)callregs, 0, _cfunc16__farcall16);
+    call16_override(1);
+    _farcall16(callregs, 0);
 }
 
 // Invoke a 16bit software interrupt.
@@ -507,7 +438,7 @@ __call16_int(struct bregs *callregs, u16 offset)
     callregs->code.offset = offset;
     if (!MODESEGMENT) {
         callregs->code.seg = SEG_BIOS;
-        _farcall16((void*)callregs - Call32Data.ss * 16, Call32Data.ss);
+        _farcall16((void*)callregs - Call16Data.ss * 16, Call16Data.ss);
         return;
     }
     callregs->code.seg = GET_SEG(CS);
@@ -520,7 +451,7 @@ reset(void)
 {
     extern void reset_vector(void) __noreturn;
     if (!MODE16)
-        call16_back(0, 0, reset_vector);
+        call16(0, 0, reset_vector);
     reset_vector();
 }
 
@@ -558,12 +489,13 @@ getCurThread(void)
     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
 }
 
-static int ThreadControl;
+static u8 CanInterrupt, ThreadControl;
 
 // Initialize the support for internal threads.
 void
-thread_init(void)
+thread_setup(void)
 {
+    CanInterrupt = 1;
     if (! CONFIG_THREADS)
         return;
     ThreadControl = romfile_loadint("etc/threads", 1);
@@ -573,7 +505,7 @@ thread_init(void)
 int
 threads_during_optionroms(void)
 {
-    return CONFIG_THREADS && ThreadControl == 2 && in_post();
+    return CONFIG_THREADS && CONFIG_RTC_TIMER && ThreadControl == 2 && in_post();
 }
 
 // Switch to next thread stack.
@@ -660,11 +592,17 @@ fail:
 void VISIBLE16
 check_irqs(void)
 {
+    if (!MODESEGMENT && !CanInterrupt) {
+        // Can't enable interrupts (PIC and/or IVT not yet setup)
+        cpu_relax();
+        return;
+    }
     if (need_hop_back()) {
-        extern void _cfunc16_check_irqs(void);
-        stack_hop_back(0, 0, _cfunc16_check_irqs);
+        stack_hop_back(check_irqs, 0, 0);
         return;
     }
+    if (MODE16)
+        clock_poll_irq();
     asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory");
 }
 
@@ -689,8 +627,7 @@ void VISIBLE16
 wait_irq(void)
 {
     if (need_hop_back()) {
-        extern void _cfunc16_wait_irq(void);
-        stack_hop_back(0, 0, _cfunc16_wait_irq);
+        stack_hop_back(wait_irq, 0, 0);
         return;
     }
     asm volatile("sti ; hlt ; cli ; cld": : :"memory");
@@ -700,8 +637,9 @@ wait_irq(void)
 void
 yield_toirq(void)
 {
-    if (!MODESEGMENT && have_threads()) {
-        // Threads still active - do a yield instead.
+    if (!CONFIG_HARDWARE_IRQ
+        || (!MODESEGMENT && (have_threads() || !CanInterrupt))) {
+        // Threads still active or irqs not available - do a yield instead.
         yield();
         return;
     }
@@ -794,9 +732,8 @@ yield_preempt(void)
 void
 check_preempt(void)
 {
-    extern void _cfunc32flat_yield_preempt(void);
     if (CONFIG_THREADS && GET_GLOBAL(CanPreempt) && have_threads())
-        call32(_cfunc32flat_yield_preempt, 0, 0);
+        call32(yield_preempt, 0, 0);
 }
 
 
@@ -817,11 +754,10 @@ call32_params_helper(struct call32_params_s *params)
 }
 
 u32
-call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret)
+__call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret)
 {
     ASSERT16();
     struct call32_params_s params = {func, eax, edx, ecx};
-    extern void _cfunc32flat_call32_params_helper(void);
-    return call32(_cfunc32flat_call32_params_helper
-                  , (u32)MAKE_FLATPTR(GET_SEG(SS), &params), errret);
+    return call32(call32_params_helper, MAKE_FLATPTR(GET_SEG(SS), &params)
+                  , errret);
 }