1
// Code for manipulating stack locations.
3
// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
5
// This file may be distributed under the terms of the GNU LGPLv3 license.
7
#include "biosvar.h" // get_ebda_seg
8
#include "util.h" // dprintf
9
#include "bregs.h" // CR0_PE
11
// Thread info - stored at bottom of each thread stack - don't change
12
// without also updating the inline assembler below.
14
struct thread_info *next;
16
struct thread_info **pprev;
18
struct thread_info VAR16VISIBLE MainThread;
21
/****************************************************************
23
****************************************************************/
25
static inline u32 getcr0(void) {
27
asm("movl %%cr0, %0" : "=r"(cr0));
30
static inline void sgdt(struct descloc_s *desc) {
31
asm("sgdtl %0" : "=m"(*desc));
33
static inline void lgdt(struct descloc_s *desc) {
34
asm("lgdtl %0" : : "m"(*desc) : "memory");
37
// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
44
// Called in 16bit protected mode?!
47
// Backup cmos index register and disable nmi
48
u8 cmosindex = inb(PORT_CMOS_INDEX);
49
outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
52
// Backup fs/gs and gdt
53
u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
57
u32 bkup_ss, bkup_esp;
59
// Backup ss/esp / set esp to flat stack location
66
// Transition to 32bit mode, call func, return to 16bit
67
" pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
72
" jmp transition16big\n"
79
: "=&r" (bkup_ss), "=&r" (bkup_esp)
81
: "eax", "ecx", "edx", "cc", "memory");
83
// Restore gdt and fs/gs
88
// Restore cmos index register
89
outb(cmosindex, PORT_CMOS_INDEX);
94
// 16bit trampoline for enabling irqs from 32bit mode.
96
" .global trampoline_checkirqs\n"
97
"trampoline_checkirqs:\n"
115
extern void trampoline_checkirqs();
118
br.code.seg = SEG_BIOS;
119
br.code.offset = (u32)&trampoline_checkirqs;
123
// 16bit trampoline for waiting for an irq from 32bit mode.
125
" .global trampoline_waitirq\n"
126
"trampoline_waitirq:\n"
132
// Wait for next irq to occur.
137
asm volatile("sti ; hlt ; cli ; cld": : :"memory");
140
if (CONFIG_THREADS && MainThread.next != &MainThread) {
141
// Threads still active - do a yield instead.
145
extern void trampoline_waitirq();
148
br.code.seg = SEG_BIOS;
149
br.code.offset = (u32)&trampoline_waitirq;
154
/****************************************************************
156
****************************************************************/
158
// Switch to the extra stack in ebda and call a function.
160
stack_hop(u32 eax, u32 edx, void *func)
163
u16 ebda_seg = get_ebda_seg(), bkup_ss;
166
// Backup current %ss/%esp values.
169
// Copy ebda seg to %ds/%ss and set %esp
175
// Restore segments and stack
179
: "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
180
: "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
186
/****************************************************************
188
****************************************************************/
190
#define THREADSTACKSIZE 4096
191
int VAR16VISIBLE CanPreempt;
196
MainThread.next = &MainThread;
197
MainThread.pprev = &MainThread.next;
198
MainThread.stackpos = NULL;
202
// Return the 'struct thread_info' for the currently running thread.
207
if (esp <= BUILD_STACK_ADDR)
209
return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
212
// Switch to next thread stack.
214
switch_next(struct thread_info *cur)
216
struct thread_info *next = cur->next;
221
" pushl $1f\n" // store return pc
222
" pushl %%ebp\n" // backup %ebp
223
" movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
224
" movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
225
" popl %%ebp\n" // restore %ebp
226
" retl\n" // restore pc
228
: "+a"(cur), "+c"(next)
230
: "ebx", "edx", "esi", "edi", "cc", "memory");
233
// Briefly permit irqs to occur.
237
if (MODESEGMENT || !CONFIG_THREADS) {
238
// Just directly check irqs.
242
struct thread_info *cur = getCurThread();
243
if (cur == &MainThread)
244
// Permit irqs to fire
247
// Switch to the next thread
251
// Last thing called from a thread (called on "next" stack).
253
__end_thread(struct thread_info *old)
255
old->next->pprev = old->pprev;
256
*old->pprev = old->next;
258
dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
259
if (MainThread.next == &MainThread)
260
dprintf(1, "All threads complete.\n");
263
// Create a new thread and start executing 'func' in it.
265
run_thread(void (*func)(void*), void *data)
268
if (! CONFIG_THREADS)
270
struct thread_info *thread;
271
thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
275
thread->stackpos = (void*)thread + THREADSTACKSIZE;
276
struct thread_info *cur = getCurThread();
278
thread->pprev = cur->pprev;
279
cur->pprev = &thread->next;
280
*thread->pprev = thread;
282
dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
285
" pushl $1f\n" // store return pc
286
" pushl %%ebp\n" // backup %ebp
287
" movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
288
" movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
289
" calll *%%ecx\n" // Call func
292
" movl (%%ebx), %%ecx\n" // %ecx = thread->next
293
" movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
294
" movl %%ebx, %%eax\n"
295
" calll %4\n" // call __end_thread(thread)
296
" popl %%ebp\n" // restore %ebp
297
" retl\n" // restore pc
299
: "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
300
: "m"(*(u8*)__end_thread)
301
: "esi", "edi", "cc", "memory");
308
// Wait for all threads (other than the main thread) to complete.
313
if (! CONFIG_THREADS)
315
while (MainThread.next != &MainThread)
320
mutex_lock(struct mutex_s *mutex)
323
if (! CONFIG_THREADS)
325
while (mutex->isLocked)
331
mutex_unlock(struct mutex_s *mutex)
334
if (! CONFIG_THREADS)
340
/****************************************************************
342
****************************************************************/
344
static u32 PreemptCount;
346
// Turn on RTC irqs and arrange for them to check the 32bit threads.
350
if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
357
// Turn off RTC irqs / stop checking for thread execution.
361
if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
367
dprintf(9, "Done preempt - %d checks\n", PreemptCount);
371
// Check if preemption is on, and wait for it to complete if so.
375
if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
383
extern void yield_preempt(void);
385
// Try to execute 32bit threads.
390
switch_next(&MainThread);
394
// 16bit code that checks if threads are pending and executes them if so.
398
if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
399
|| !GET_GLOBAL(CanPreempt)
400
|| GET_GLOBAL(MainThread.next) == &MainThread)
403
call32(yield_preempt);