~ubuntu-branches/ubuntu/oneiric/seabios/oneiric

« back to all changes in this revision

Viewing changes to .pc/debian-changes-0.6.0-0ubuntu2/src/stacks.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2010-10-22 11:04:31 UTC
  • Revision ID: james.westby@ubuntu.com-20101022110431-fnfj73ra6xkq623n
Tags: 0.6.0-0ubuntu2
Add all patches which were included in qemu-0.13.0-rc2 (per
commit on Jul 13, 2010).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Code for manipulating stack locations.
 
2
//
 
3
// Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
 
4
//
 
5
// This file may be distributed under the terms of the GNU LGPLv3 license.
 
6
 
 
7
#include "biosvar.h" // get_ebda_seg
 
8
#include "util.h" // dprintf
 
9
#include "bregs.h" // CR0_PE
 
10
 
 
11
// Thread info - stored at bottom of each thread stack - don't change
 
12
// without also updating the inline assembler below.
 
13
struct thread_info {
 
14
    struct thread_info *next;
 
15
    void *stackpos;
 
16
    struct thread_info **pprev;
 
17
};
 
18
struct thread_info VAR16VISIBLE MainThread;
 
19
 
 
20
 
 
21
/****************************************************************
 
22
 * Low level helpers
 
23
 ****************************************************************/
 
24
 
 
25
static inline u32 getcr0(void) {
 
26
    u32 cr0;
 
27
    asm("movl %%cr0, %0" : "=r"(cr0));
 
28
    return cr0;
 
29
}
 
30
static inline void sgdt(struct descloc_s *desc) {
 
31
    asm("sgdtl %0" : "=m"(*desc));
 
32
}
 
33
static inline void lgdt(struct descloc_s *desc) {
 
34
    asm("lgdtl %0" : : "m"(*desc) : "memory");
 
35
}
 
36
 
 
37
// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
 
38
static inline int
 
39
call32(void *func)
 
40
{
 
41
    ASSERT16();
 
42
    u32 cr0 = getcr0();
 
43
    if (cr0 & CR0_PE)
 
44
        // Called in 16bit protected mode?!
 
45
        return -1;
 
46
 
 
47
    // Backup cmos index register and disable nmi
 
48
    u8 cmosindex = inb(PORT_CMOS_INDEX);
 
49
    outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
 
50
    inb(PORT_CMOS_DATA);
 
51
 
 
52
    // Backup fs/gs and gdt
 
53
    u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
 
54
    struct descloc_s gdt;
 
55
    sgdt(&gdt);
 
56
 
 
57
    u32 bkup_ss, bkup_esp;
 
58
    asm volatile(
 
59
        // Backup ss/esp / set esp to flat stack location
 
60
        "  movl %%ss, %0\n"
 
61
        "  movl %%esp, %1\n"
 
62
        "  shll $4, %0\n"
 
63
        "  addl %0, %%esp\n"
 
64
        "  movl %%ss, %0\n"
 
65
 
 
66
        // Transition to 32bit mode, call func, return to 16bit
 
67
        "  pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
 
68
        "  jmp transition32\n"
 
69
        "  .code32\n"
 
70
        "1:calll *%2\n"
 
71
        "  pushl $2f\n"
 
72
        "  jmp transition16big\n"
 
73
 
 
74
        // Restore ds/ss/esp
 
75
        "  .code16gcc\n"
 
76
        "2:movl %0, %%ds\n"
 
77
        "  movl %0, %%ss\n"
 
78
        "  movl %1, %%esp\n"
 
79
        : "=&r" (bkup_ss), "=&r" (bkup_esp)
 
80
        : "r" (func)
 
81
        : "eax", "ecx", "edx", "cc", "memory");
 
82
 
 
83
    // Restore gdt and fs/gs
 
84
    lgdt(&gdt);
 
85
    SET_SEG(FS, fs);
 
86
    SET_SEG(GS, gs);
 
87
 
 
88
    // Restore cmos index register
 
89
    outb(cmosindex, PORT_CMOS_INDEX);
 
90
    inb(PORT_CMOS_DATA);
 
91
    return 0;
 
92
}
 
93
 
 
94
// 16bit trampoline for enabling irqs from 32bit mode.
 
95
ASM16(
 
96
    "  .global trampoline_checkirqs\n"
 
97
    "trampoline_checkirqs:\n"
 
98
    "  rep ; nop\n"
 
99
    "  lretw"
 
100
    );
 
101
 
 
102
static void
 
103
check_irqs(void)
 
104
{
 
105
    if (MODESEGMENT) {
 
106
        asm volatile(
 
107
            "sti\n"
 
108
            "nop\n"
 
109
            "rep ; nop\n"
 
110
            "cli\n"
 
111
            "cld\n"
 
112
            : : :"memory");
 
113
        return;
 
114
    }
 
115
    extern void trampoline_checkirqs();
 
116
    struct bregs br;
 
117
    br.flags = F_IF;
 
118
    br.code.seg = SEG_BIOS;
 
119
    br.code.offset = (u32)&trampoline_checkirqs;
 
120
    call16big(&br);
 
121
}
 
122
 
 
123
// 16bit trampoline for waiting for an irq from 32bit mode.
 
124
ASM16(
 
125
    "  .global trampoline_waitirq\n"
 
126
    "trampoline_waitirq:\n"
 
127
    "  sti\n"
 
128
    "  hlt\n"
 
129
    "  lretw"
 
130
    );
 
131
 
 
132
// Wait for next irq to occur.
 
133
void
 
134
wait_irq(void)
 
135
{
 
136
    if (MODESEGMENT) {
 
137
        asm volatile("sti ; hlt ; cli ; cld": : :"memory");
 
138
        return;
 
139
    }
 
140
    if (CONFIG_THREADS && MainThread.next != &MainThread) {
 
141
        // Threads still active - do a yield instead.
 
142
        yield();
 
143
        return;
 
144
    }
 
145
    extern void trampoline_waitirq();
 
146
    struct bregs br;
 
147
    br.flags = 0;
 
148
    br.code.seg = SEG_BIOS;
 
149
    br.code.offset = (u32)&trampoline_waitirq;
 
150
    call16big(&br);
 
151
}
 
152
 
 
153
 
 
154
/****************************************************************
 
155
 * Stack in EBDA
 
156
 ****************************************************************/
 
157
 
 
158
// Switch to the extra stack in ebda and call a function.
 
159
inline u32
 
160
stack_hop(u32 eax, u32 edx, void *func)
 
161
{
 
162
    ASSERT16();
 
163
    u16 ebda_seg = get_ebda_seg(), bkup_ss;
 
164
    u32 bkup_esp;
 
165
    asm volatile(
 
166
        // Backup current %ss/%esp values.
 
167
        "movw %%ss, %w3\n"
 
168
        "movl %%esp, %4\n"
 
169
        // Copy ebda seg to %ds/%ss and set %esp
 
170
        "movw %w6, %%ds\n"
 
171
        "movw %w6, %%ss\n"
 
172
        "movl %5, %%esp\n"
 
173
        // Call func
 
174
        "calll *%2\n"
 
175
        // Restore segments and stack
 
176
        "movw %w3, %%ds\n"
 
177
        "movw %w3, %%ss\n"
 
178
        "movl %4, %%esp"
 
179
        : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
 
180
        : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
 
181
        : "cc", "memory");
 
182
    return eax;
 
183
}
 
184
 
 
185
 
 
186
/****************************************************************
 
187
 * Threads
 
188
 ****************************************************************/
 
189
 
 
190
#define THREADSTACKSIZE 4096
 
191
int VAR16VISIBLE CanPreempt;
 
192
 
 
193
void
 
194
thread_setup(void)
 
195
{
 
196
    MainThread.next = &MainThread;
 
197
    MainThread.pprev = &MainThread.next;
 
198
    MainThread.stackpos = NULL;
 
199
    CanPreempt = 0;
 
200
}
 
201
 
 
202
// Return the 'struct thread_info' for the currently running thread.
 
203
struct thread_info *
 
204
getCurThread(void)
 
205
{
 
206
    u32 esp = getesp();
 
207
    if (esp <= BUILD_STACK_ADDR)
 
208
        return &MainThread;
 
209
    return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
 
210
}
 
211
 
 
212
// Switch to next thread stack.
 
213
static void
 
214
switch_next(struct thread_info *cur)
 
215
{
 
216
    struct thread_info *next = cur->next;
 
217
    if (cur == next)
 
218
        // Nothing to do.
 
219
        return;
 
220
    asm volatile(
 
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
 
227
        "1:\n"
 
228
        : "+a"(cur), "+c"(next)
 
229
        :
 
230
        : "ebx", "edx", "esi", "edi", "cc", "memory");
 
231
}
 
232
 
 
233
// Briefly permit irqs to occur.
 
234
void
 
235
yield(void)
 
236
{
 
237
    if (MODESEGMENT || !CONFIG_THREADS) {
 
238
        // Just directly check irqs.
 
239
        check_irqs();
 
240
        return;
 
241
    }
 
242
    struct thread_info *cur = getCurThread();
 
243
    if (cur == &MainThread)
 
244
        // Permit irqs to fire
 
245
        check_irqs();
 
246
 
 
247
    // Switch to the next thread
 
248
    switch_next(cur);
 
249
}
 
250
 
 
251
// Last thing called from a thread (called on "next" stack).
 
252
static void
 
253
__end_thread(struct thread_info *old)
 
254
{
 
255
    old->next->pprev = old->pprev;
 
256
    *old->pprev = old->next;
 
257
    free(old);
 
258
    dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
 
259
    if (MainThread.next == &MainThread)
 
260
        dprintf(1, "All threads complete.\n");
 
261
}
 
262
 
 
263
// Create a new thread and start executing 'func' in it.
 
264
void
 
265
run_thread(void (*func)(void*), void *data)
 
266
{
 
267
    ASSERT32FLAT();
 
268
    if (! CONFIG_THREADS)
 
269
        goto fail;
 
270
    struct thread_info *thread;
 
271
    thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
 
272
    if (!thread)
 
273
        goto fail;
 
274
 
 
275
    thread->stackpos = (void*)thread + THREADSTACKSIZE;
 
276
    struct thread_info *cur = getCurThread();
 
277
    thread->next = cur;
 
278
    thread->pprev = cur->pprev;
 
279
    cur->pprev = &thread->next;
 
280
    *thread->pprev = thread;
 
281
 
 
282
    dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
 
283
    asm volatile(
 
284
        // Start 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
 
290
 
 
291
        // End thread
 
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
 
298
        "1:\n"
 
299
        : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
 
300
        : "m"(*(u8*)__end_thread)
 
301
        : "esi", "edi", "cc", "memory");
 
302
    return;
 
303
 
 
304
fail:
 
305
    func(data);
 
306
}
 
307
 
 
308
// Wait for all threads (other than the main thread) to complete.
 
309
void
 
310
wait_threads(void)
 
311
{
 
312
    ASSERT32FLAT();
 
313
    if (! CONFIG_THREADS)
 
314
        return;
 
315
    while (MainThread.next != &MainThread)
 
316
        yield();
 
317
}
 
318
 
 
319
void
 
320
mutex_lock(struct mutex_s *mutex)
 
321
{
 
322
    ASSERT32FLAT();
 
323
    if (! CONFIG_THREADS)
 
324
        return;
 
325
    while (mutex->isLocked)
 
326
        yield();
 
327
    mutex->isLocked = 1;
 
328
}
 
329
 
 
330
void
 
331
mutex_unlock(struct mutex_s *mutex)
 
332
{
 
333
    ASSERT32FLAT();
 
334
    if (! CONFIG_THREADS)
 
335
        return;
 
336
    mutex->isLocked = 0;
 
337
}
 
338
 
 
339
 
 
340
/****************************************************************
 
341
 * Thread preemption
 
342
 ****************************************************************/
 
343
 
 
344
static u32 PreemptCount;
 
345
 
 
346
// Turn on RTC irqs and arrange for them to check the 32bit threads.
 
347
void
 
348
start_preempt(void)
 
349
{
 
350
    if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
 
351
        return;
 
352
    CanPreempt = 1;
 
353
    PreemptCount = 0;
 
354
    useRTC();
 
355
}
 
356
 
 
357
// Turn off RTC irqs / stop checking for thread execution.
 
358
void
 
359
finish_preempt(void)
 
360
{
 
361
    if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
 
362
        yield();
 
363
        return;
 
364
    }
 
365
    CanPreempt = 0;
 
366
    releaseRTC();
 
367
    dprintf(9, "Done preempt - %d checks\n", PreemptCount);
 
368
    yield();
 
369
}
 
370
 
 
371
// Check if preemption is on, and wait for it to complete if so.
 
372
int
 
373
wait_preempt(void)
 
374
{
 
375
    if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
 
376
        || !CanPreempt)
 
377
        return 0;
 
378
    while (CanPreempt)
 
379
        yield();
 
380
    return 1;
 
381
}
 
382
 
 
383
extern void yield_preempt(void);
 
384
#if MODESEGMENT == 0
 
385
// Try to execute 32bit threads.
 
386
void VISIBLE32FLAT
 
387
yield_preempt(void)
 
388
{
 
389
    PreemptCount++;
 
390
    switch_next(&MainThread);
 
391
}
 
392
#endif
 
393
 
 
394
// 16bit code that checks if threads are pending and executes them if so.
 
395
void
 
396
check_preempt(void)
 
397
{
 
398
    if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
 
399
        || !GET_GLOBAL(CanPreempt)
 
400
        || GET_GLOBAL(MainThread.next) == &MainThread)
 
401
        return;
 
402
 
 
403
    call32(yield_preempt);
 
404
}