// Code for manipulating stack locations.
//
-// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2009-2010 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "util.h" // dprintf
#include "bregs.h" // CR0_PE
+// Thread info - stored at bottom of each thread stack - don't change
+// without also updating the inline assembler below.
+struct thread_info {
+ struct thread_info *next;
+ void *stackpos;
+ struct thread_info **pprev;
+};
+struct thread_info VAR32FLATVISIBLE MainThread = {
+ &MainThread, NULL, &MainThread.next
+};
+
+
+/****************************************************************
+ * Low level helpers
+ ****************************************************************/
+
+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");
+}
+
+// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
+u32 VISIBLE16
+call32(void *func, u32 eax, u32 errret)
+{
+ ASSERT16();
+ u32 cr0 = getcr0();
+ if (cr0 & CR0_PE)
+ // Called in 16bit protected mode?!
+ return errret;
+
+ // 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"
+ " shrl $4, %0\n"
+
+ // Transition to 32bit mode, call func, return to 16bit
+ " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
+ " jmp transition32\n"
+ " .code32\n"
+ "1:calll *%3\n"
+ " movl $2f, %%edx\n"
+ " jmp transition16big\n"
+
+ // Restore ds/ss/esp
+ " .code16gcc\n"
+ "2:movl %0, %%ds\n"
+ " movl %0, %%ss\n"
+ " movl %1, %%esp\n"
+ : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
+ : "r" (func)
+ : "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);
+ return eax;
+}
+
+// 16bit trampoline for enabling irqs from 32bit mode.
+ASM16(
+ " .global trampoline_checkirqs\n"
+ "trampoline_checkirqs:\n"
+ " rep ; nop\n"
+ " lretw"
+ );
+
+static void
+check_irqs(void)
+{
+ if (MODESEGMENT) {
+ asm volatile(
+ "sti\n"
+ "nop\n"
+ "rep ; nop\n"
+ "cli\n"
+ "cld\n"
+ : : :"memory");
+ return;
+ }
+ extern void trampoline_checkirqs();
+ struct bregs br;
+ br.flags = F_IF;
+ br.code.seg = SEG_BIOS;
+ br.code.offset = (u32)&trampoline_checkirqs;
+ call16big(&br);
+}
+
+// 16bit trampoline for waiting for an irq from 32bit mode.
+ASM16(
+ " .global trampoline_waitirq\n"
+ "trampoline_waitirq:\n"
+ " sti\n"
+ " hlt\n"
+ " lretw"
+ );
+
+// Wait for next irq to occur.
+void
+wait_irq(void)
+{
+ if (MODESEGMENT) {
+ asm volatile("sti ; hlt ; cli ; cld": : :"memory");
+ return;
+ }
+ if (CONFIG_THREADS && MainThread.next != &MainThread) {
+ // Threads still active - do a yield instead.
+ yield();
+ return;
+ }
+ extern void trampoline_waitirq();
+ struct bregs br;
+ br.flags = 0;
+ br.code.seg = SEG_BIOS;
+ br.code.offset = (u32)&trampoline_waitirq;
+ call16big(&br);
+}
+
/****************************************************************
* Stack in EBDA
// Switch to the extra stack in ebda and call a function.
inline u32
-stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
+stack_hop(u32 eax, u32 edx, void *func)
{
ASSERT16();
u16 ebda_seg = get_ebda_seg(), bkup_ss;
"movw %w6, %%ss\n"
"movl %5, %%esp\n"
// Call func
- "calll %7\n"
+ "calll *%2\n"
// Restore segments and stack
"movw %w3, %%ds\n"
"movw %w3, %%ss\n"
"movl %4, %%esp"
- : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
- : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
+ : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
+ : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
: "cc", "memory");
return eax;
}
****************************************************************/
#define THREADSTACKSIZE 4096
-
-struct thread_info {
- struct thread_info *next;
- void *stackpos;
-};
-
-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()
+getCurThread(void)
{
u32 esp = getesp();
if (esp <= BUILD_STACK_ADDR)
switch_next(struct thread_info *cur)
{
struct thread_info *next = cur->next;
+ if (cur == next)
+ // Nothing to do.
+ return;
asm volatile(
" pushl $1f\n" // store return pc
" pushl %%ebp\n" // backup %ebp
// Briefly permit irqs to occur.
void
-yield()
+yield(void)
{
- if (MODE16 || !CONFIG_THREADS) {
+ if (MODESEGMENT || !CONFIG_THREADS) {
// Just directly check irqs.
check_irqs();
return;
static void
__end_thread(struct thread_info *old)
{
- struct thread_info *pos = &MainThread;
- while (pos->next != old)
- pos = pos->next;
- pos->next = old->next;
+ old->next->pprev = old->pprev;
+ *old->pprev = old->next;
free(old);
dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
+ if (MainThread.next == &MainThread)
+ dprintf(1, "All threads complete.\n");
}
// Create a new thread and start executing 'func' in it.
void
run_thread(void (*func)(void*), void *data)
{
- ASSERT32();
+ ASSERT32FLAT();
if (! CONFIG_THREADS)
goto fail;
struct thread_info *thread;
thread->stackpos = (void*)thread + THREADSTACKSIZE;
struct thread_info *cur = getCurThread();
- thread->next = cur->next;
- cur->next = thread;
+ thread->next = cur;
+ thread->pprev = cur->pprev;
+ cur->pprev = &thread->next;
+ *thread->pprev = thread;
dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
asm volatile(
// Wait for all threads (other than the main thread) to complete.
void
-wait_threads()
+wait_threads(void)
{
- ASSERT32();
+ ASSERT32FLAT();
if (! CONFIG_THREADS)
return;
while (MainThread.next != &MainThread)
yield();
}
+void
+mutex_lock(struct mutex_s *mutex)
+{
+ ASSERT32FLAT();
+ if (! CONFIG_THREADS)
+ return;
+ while (mutex->isLocked)
+ yield();
+ mutex->isLocked = 1;
+}
+
+void
+mutex_unlock(struct mutex_s *mutex)
+{
+ ASSERT32FLAT();
+ if (! CONFIG_THREADS)
+ return;
+ mutex->isLocked = 0;
+}
+
/****************************************************************
* Thread preemption
// Turn on RTC irqs and arrange for them to check the 32bit threads.
void
-start_preempt()
+start_preempt(void)
{
if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
return;
// Turn off RTC irqs / stop checking for thread execution.
void
-finish_preempt()
+finish_preempt(void)
{
- if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
+ if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
+ yield();
return;
+ }
CanPreempt = 0;
releaseRTC();
- dprintf(1, "Done preempt - %d checks\n", PreemptCount);
+ dprintf(9, "Done preempt - %d checks\n", PreemptCount);
+ yield();
}
-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");
+// Check if preemption is on, and wait for it to complete if so.
+int
+wait_preempt(void)
+{
+ if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
+ || !CanPreempt)
+ return 0;
+ while (CanPreempt)
+ yield();
+ return 1;
}
-#if !MODE16
// Try to execute 32bit threads.
-void VISIBLE32
-yield_preempt()
+void VISIBLE32INIT
+yield_preempt(void)
{
PreemptCount++;
switch_next(&MainThread);
}
-#endif
// 16bit code that checks if threads are pending and executes them if so.
void
-check_preempt()
+check_preempt(void)
{
- ASSERT16();
if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
|| !GET_GLOBAL(CanPreempt)
- || GET_GLOBAL(MainThread.next) == &MainThread)
+ || GET_FLATPTR(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);
+ extern void _cfunc32flat_yield_preempt(void);
+ call32(_cfunc32flat_yield_preempt, 0, 0);
}