#include "biosvar.h" // get_ebda_seg
#include "util.h" // dprintf
+#include "bregs.h" // CR0_PE
/****************************************************************
void *stackpos;
};
-struct thread_info MainThread;
+struct thread_info VAR16VISIBLE MainThread;
+int VAR16VISIBLE CanPreempt;
void
thread_setup()
{
MainThread.next = &MainThread;
MainThread.stackpos = NULL;
+ CanPreempt = 0;
}
+// Return the 'struct thread_info' for the currently running thread.
struct thread_info *
getCurThread()
{
return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
}
+// Switch to next thread stack.
+static void
+switch_next(struct thread_info *cur)
+{
+ struct thread_info *next = cur->next;
+ asm volatile(
+ " pushl $1f\n" // store return pc
+ " pushl %%ebp\n" // backup %ebp
+ " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
+ " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
+ " popl %%ebp\n" // restore %ebp
+ " retl\n" // restore pc
+ "1:\n"
+ : "+a"(cur), "+c"(next)
+ :
+ : "ebx", "edx", "esi", "edi", "cc", "memory");
+}
+
// Briefly permit irqs to occur.
void
yield()
check_irqs();
// Switch to the next thread
- struct thread_info *next = cur->next;
- asm volatile(
- " pushl $1f\n" // store return pc
- " pushl %%ebp\n" // backup %ebp
- " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
- " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
- " popl %%ebp\n" // restore %ebp
- " retl\n" // restore pc
- "1:\n"
- : "+a"(cur), "+c"(next)
- :
- : "ebx", "edx", "esi", "edi", "cc", "memory");
+ switch_next(cur);
}
// Last thing called from a thread (called on "next" stack).
dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
}
+// Create a new thread and start executing 'func' in it.
void
run_thread(void (*func)(void*), void *data)
{
func(data);
}
+// Wait for all threads (other than the main thread) to complete.
void
wait_threads()
{
while (MainThread.next != &MainThread)
yield();
}
+
+
+/****************************************************************
+ * Thread preemption
+ ****************************************************************/
+
+static u32 PreemptCount;
+
+// Turn on RTC irqs and arrange for them to check the 32bit threads.
+void
+start_preempt()
+{
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
+ return;
+ CanPreempt = 1;
+ PreemptCount = 0;
+ useRTC();
+}
+
+// Turn off RTC irqs / stop checking for thread execution.
+void
+finish_preempt()
+{
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
+ return;
+ CanPreempt = 0;
+ releaseRTC();
+ dprintf(1, "Done preempt - %d checks\n", PreemptCount);
+}
+
+static inline u32 getcr0() {
+ u32 cr0;
+ asm("movl %%cr0, %0" : "=r"(cr0));
+ return cr0;
+}
+static inline void sgdt(struct descloc_s *desc) {
+ asm("sgdtl %0" : "=m"(*desc));
+}
+static inline void lgdt(struct descloc_s *desc) {
+ asm("lgdtl %0" : : "m"(*desc) : "memory");
+}
+
+#if !MODE16
+// Try to execute 32bit threads.
+void VISIBLE32
+yield_preempt()
+{
+ PreemptCount++;
+ switch_next(&MainThread);
+}
+#endif
+
+// 16bit code that checks if threads are pending and executes them if so.
+void
+check_preempt()
+{
+ ASSERT16();
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
+ || !GET_GLOBAL(CanPreempt)
+ || GET_GLOBAL(MainThread.next) == &MainThread)
+ return;
+ u32 cr0 = getcr0();
+ if (cr0 & CR0_PE)
+ // Called in 16bit protected mode?!
+ return;
+
+ // 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);
+
+ // Backup fs/gs and gdt
+ u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
+ struct descloc_s gdt;
+ sgdt(&gdt);
+
+ u32 bkup_ss, bkup_esp;
+ asm volatile(
+ // Backup ss/esp / set esp to flat stack location
+ " movl %%ss, %0\n"
+ " movl %%esp, %1\n"
+ " shll $4, %0\n"
+ " addl %0, %%esp\n"
+ " movl %%ss, %0\n"
+
+ // Transition to 32bit mode, call yield_preempt, return to 16bit
+ " pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
+ " jmp transition32\n"
+ " .code32\n"
+ "1:calll (yield_preempt - " __stringify(BUILD_BIOS_ADDR) ")\n"
+ " pushl $2f\n"
+ " jmp transition16big\n"
+ " .code16gcc\n"
+ "2:\n"
+
+ // Restore ds/ss/esp
+ " movl %0, %%ds\n"
+ " movl %0, %%ss\n"
+ " movl %1, %%esp\n"
+ : "=&r" (bkup_ss), "=&r" (bkup_esp)
+ : : "eax", "ecx", "edx", "cc", "memory");
+
+ // 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);
+}