Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 1 | // Code for manipulating stack locations. |
| 2 | // |
Kevin O'Connor | d1b4f96 | 2010-09-15 21:38:16 -0400 | [diff] [blame] | 3 | // Copyright (C) 2009-2010 Kevin O'Connor <kevin@koconnor.net> |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 4 | // |
| 5 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
| 6 | |
Kevin O'Connor | 46b8262 | 2012-05-13 12:10:30 -0400 | [diff] [blame] | 7 | #include "biosvar.h" // GET_GLOBAL |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 8 | #include "util.h" // dprintf |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 9 | #include "bregs.h" // CR0_PE |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 10 | |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 11 | |
| 12 | /**************************************************************** |
| 13 | * Extra 16bit stack |
| 14 | ****************************************************************/ |
| 15 | |
| 16 | // Space for a stack for 16bit code. |
| 17 | u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8); |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 18 | u8 *StackPos VARLOW; |
| 19 | |
| 20 | // Test if currently on the extra stack |
| 21 | static inline int |
| 22 | on_extra_stack(void) |
| 23 | { |
| 24 | return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack; |
| 25 | } |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 26 | |
| 27 | // Switch to the extra stack and call a function. |
Kevin O'Connor | ecdc655 | 2012-05-28 14:25:15 -0400 | [diff] [blame] | 28 | u32 |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 29 | stack_hop(u32 eax, u32 edx, void *func) |
| 30 | { |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 31 | if (on_extra_stack()) |
| 32 | return ((u32 (*)(u32, u32))func)(eax, edx); |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 33 | ASSERT16(); |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 34 | u16 stack_seg = SEG_LOW; |
| 35 | u32 bkup_ss, bkup_esp; |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 36 | asm volatile( |
| 37 | // Backup current %ss/%esp values. |
| 38 | "movw %%ss, %w3\n" |
| 39 | "movl %%esp, %4\n" |
| 40 | // Copy stack seg to %ds/%ss and set %esp |
| 41 | "movw %w6, %%ds\n" |
| 42 | "movw %w6, %%ss\n" |
| 43 | "movl %5, %%esp\n" |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 44 | "pushl %3\n" |
| 45 | "pushl %4\n" |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 46 | // Call func |
| 47 | "calll *%2\n" |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 48 | "popl %4\n" |
| 49 | "popl %3\n" |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 50 | // Restore segments and stack |
| 51 | "movw %w3, %%ds\n" |
| 52 | "movw %w3, %%ss\n" |
| 53 | "movl %4, %%esp" |
| 54 | : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp) |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 55 | : "m" (StackPos), "r" (stack_seg) |
| 56 | : "cc", "memory"); |
| 57 | return eax; |
| 58 | } |
| 59 | |
| 60 | // Switch back to original caller's stack and call a function. |
Kevin O'Connor | ecdc655 | 2012-05-28 14:25:15 -0400 | [diff] [blame] | 61 | u32 |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 62 | stack_hop_back(u32 eax, u32 edx, void *func) |
| 63 | { |
| 64 | if (!on_extra_stack()) |
| 65 | return ((u32 (*)(u32, u32))func)(eax, edx); |
| 66 | ASSERT16(); |
| 67 | u16 bkup_ss; |
| 68 | u32 bkup_stack_pos, temp; |
| 69 | asm volatile( |
| 70 | // Backup stack_pos and current %ss/%esp |
| 71 | "movl %6, %4\n" |
| 72 | "movw %%ss, %w3\n" |
| 73 | "movl %%esp, %6\n" |
| 74 | // Restore original callers' %ss/%esp |
| 75 | "movl -4(%4), %5\n" |
| 76 | "movl %5, %%ss\n" |
Kevin O'Connor | 1297e5d | 2012-06-02 20:30:58 -0400 | [diff] [blame] | 77 | "movw %%ds:-8(%4), %%sp\n" |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 78 | "movl %5, %%ds\n" |
| 79 | // Call func |
| 80 | "calll *%2\n" |
| 81 | // Restore %ss/%esp and stack_pos |
| 82 | "movw %w3, %%ds\n" |
| 83 | "movw %w3, %%ss\n" |
| 84 | "movl %6, %%esp\n" |
| 85 | "movl %4, %6" |
| 86 | : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss) |
| 87 | , "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos) |
| 88 | : |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 89 | : "cc", "memory"); |
| 90 | return eax; |
| 91 | } |
Kevin O'Connor | 9c447c3 | 2010-05-23 10:24:22 -0400 | [diff] [blame] | 92 | |
| 93 | |
| 94 | /**************************************************************** |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 95 | * 16bit / 32bit calling |
Kevin O'Connor | 9c447c3 | 2010-05-23 10:24:22 -0400 | [diff] [blame] | 96 | ****************************************************************/ |
| 97 | |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 98 | static inline void sgdt(struct descloc_s *desc) { |
| 99 | asm("sgdtl %0" : "=m"(*desc)); |
| 100 | } |
| 101 | static inline void lgdt(struct descloc_s *desc) { |
| 102 | asm("lgdtl %0" : : "m"(*desc) : "memory"); |
| 103 | } |
| 104 | |
| 105 | // Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. |
Kevin O'Connor | 533b628 | 2011-07-16 13:13:12 -0400 | [diff] [blame] | 106 | u32 VISIBLE16 |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 107 | call32(void *func, u32 eax, u32 errret) |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 108 | { |
| 109 | ASSERT16(); |
| 110 | u32 cr0 = getcr0(); |
| 111 | if (cr0 & CR0_PE) |
| 112 | // Called in 16bit protected mode?! |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 113 | return errret; |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 114 | |
| 115 | // Backup cmos index register and disable nmi |
| 116 | u8 cmosindex = inb(PORT_CMOS_INDEX); |
| 117 | outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); |
| 118 | inb(PORT_CMOS_DATA); |
| 119 | |
| 120 | // Backup fs/gs and gdt |
| 121 | u16 fs = GET_SEG(FS), gs = GET_SEG(GS); |
| 122 | struct descloc_s gdt; |
| 123 | sgdt(&gdt); |
| 124 | |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 125 | u32 bkup_ss, bkup_esp; |
| 126 | asm volatile( |
| 127 | // Backup ss/esp / set esp to flat stack location |
| 128 | " movl %%ss, %0\n" |
| 129 | " movl %%esp, %1\n" |
| 130 | " shll $4, %0\n" |
| 131 | " addl %0, %%esp\n" |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 132 | " shrl $4, %0\n" |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 133 | |
Kevin O'Connor | fb214dc | 2009-12-20 13:11:17 -0500 | [diff] [blame] | 134 | // Transition to 32bit mode, call func, return to 16bit |
Kevin O'Connor | 4057f98 | 2010-11-25 08:52:50 -0500 | [diff] [blame] | 135 | " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n" |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 136 | " jmp transition32\n" |
| 137 | " .code32\n" |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 138 | "1:calll *%3\n" |
Kevin O'Connor | 4057f98 | 2010-11-25 08:52:50 -0500 | [diff] [blame] | 139 | " movl $2f, %%edx\n" |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 140 | " jmp transition16big\n" |
| 141 | |
| 142 | // Restore ds/ss/esp |
| 143 | " .code16gcc\n" |
| 144 | "2:movl %0, %%ds\n" |
| 145 | " movl %0, %%ss\n" |
| 146 | " movl %1, %%esp\n" |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 147 | : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax) |
Kevin O'Connor | bca0736 | 2010-03-20 20:41:38 -0400 | [diff] [blame] | 148 | : "r" (func) |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 149 | : "ecx", "edx", "cc", "memory"); |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 150 | |
| 151 | // Restore gdt and fs/gs |
| 152 | lgdt(&gdt); |
| 153 | SET_SEG(FS, fs); |
| 154 | SET_SEG(GS, gs); |
| 155 | |
| 156 | // Restore cmos index register |
| 157 | outb(cmosindex, PORT_CMOS_INDEX); |
| 158 | inb(PORT_CMOS_DATA); |
Kevin O'Connor | af9629b | 2010-11-25 09:17:31 -0500 | [diff] [blame] | 159 | return eax; |
Kevin O'Connor | a7fc5de | 2009-12-13 11:48:18 -0500 | [diff] [blame] | 160 | } |
| 161 | |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 162 | // Call a 16bit SeaBIOS function from a 32bit SeaBIOS function. |
| 163 | static inline u32 |
| 164 | call16(u32 eax, void *func) |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 165 | { |
| 166 | ASSERT32FLAT(); |
| 167 | if (getesp() > BUILD_STACK_ADDR) |
| 168 | panic("call16 with invalid stack\n"); |
| 169 | asm volatile( |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 170 | "calll __call16" |
| 171 | : "+a" (eax) |
| 172 | : "b" ((u32)func - BUILD_BIOS_ADDR) |
| 173 | : "ecx", "edx", "cc", "memory"); |
| 174 | return eax; |
| 175 | } |
| 176 | |
| 177 | static inline u32 |
| 178 | call16big(u32 eax, void *func) |
| 179 | { |
| 180 | ASSERT32FLAT(); |
| 181 | if (getesp() > BUILD_STACK_ADDR) |
| 182 | panic("call16big with invalid stack\n"); |
| 183 | asm volatile( |
| 184 | "calll __call16big" |
| 185 | : "+a" (eax) |
| 186 | : "b" ((u32)func - BUILD_BIOS_ADDR) |
| 187 | : "ecx", "edx", "cc", "memory"); |
| 188 | return eax; |
| 189 | } |
| 190 | |
| 191 | // Far call 16bit code with a specified register state. |
| 192 | void VISIBLE16 |
| 193 | _farcall16(struct bregs *callregs) |
| 194 | { |
| 195 | ASSERT16(); |
| 196 | asm volatile( |
| 197 | "calll __farcall16\n" |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 198 | : "+a" (callregs), "+m" (*callregs) |
Kevin O'Connor | beeabd6 | 2012-05-28 13:59:07 -0400 | [diff] [blame] | 199 | : "m" (__segment_ES) |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 200 | : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory"); |
| 201 | } |
| 202 | |
| 203 | inline void |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 204 | farcall16(struct bregs *callregs) |
| 205 | { |
| 206 | if (MODE16) { |
Kevin O'Connor | beeabd6 | 2012-05-28 13:59:07 -0400 | [diff] [blame] | 207 | SET_SEG(ES, GET_SEG(SS)); |
| 208 | stack_hop_back((u32)callregs, 0, _farcall16); |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 209 | return; |
| 210 | } |
| 211 | extern void _cfunc16__farcall16(void); |
| 212 | call16((u32)callregs, _cfunc16__farcall16); |
| 213 | } |
| 214 | |
| 215 | inline void |
| 216 | farcall16big(struct bregs *callregs) |
| 217 | { |
| 218 | extern void _cfunc16__farcall16(void); |
| 219 | call16big((u32)callregs, _cfunc16__farcall16); |
| 220 | } |
| 221 | |
| 222 | // Invoke a 16bit software interrupt. |
| 223 | inline void |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 224 | __call16_int(struct bregs *callregs, u16 offset) |
| 225 | { |
| 226 | if (MODESEGMENT) |
| 227 | callregs->code.seg = GET_SEG(CS); |
| 228 | else |
| 229 | callregs->code.seg = SEG_BIOS; |
| 230 | callregs->code.offset = offset; |
| 231 | farcall16(callregs); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | |
| 235 | /**************************************************************** |
| 236 | * Threads |
| 237 | ****************************************************************/ |
| 238 | |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 239 | // Thread info - stored at bottom of each thread stack - don't change |
| 240 | // without also updating the inline assembler below. |
| 241 | struct thread_info { |
| 242 | struct thread_info *next; |
| 243 | void *stackpos; |
| 244 | struct thread_info **pprev; |
| 245 | }; |
| 246 | struct thread_info VAR32FLATVISIBLE MainThread = { |
| 247 | &MainThread, NULL, &MainThread.next |
| 248 | }; |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 249 | #define THREADSTACKSIZE 4096 |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 250 | |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 251 | // Return the 'struct thread_info' for the currently running thread. |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 252 | struct thread_info * |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 253 | getCurThread(void) |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 254 | { |
| 255 | u32 esp = getesp(); |
| 256 | if (esp <= BUILD_STACK_ADDR) |
| 257 | return &MainThread; |
| 258 | return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE); |
| 259 | } |
| 260 | |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 261 | // Switch to next thread stack. |
| 262 | static void |
| 263 | switch_next(struct thread_info *cur) |
| 264 | { |
| 265 | struct thread_info *next = cur->next; |
Kevin O'Connor | fb214dc | 2009-12-20 13:11:17 -0500 | [diff] [blame] | 266 | if (cur == next) |
| 267 | // Nothing to do. |
| 268 | return; |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 269 | asm volatile( |
| 270 | " pushl $1f\n" // store return pc |
| 271 | " pushl %%ebp\n" // backup %ebp |
| 272 | " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp |
| 273 | " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos |
| 274 | " popl %%ebp\n" // restore %ebp |
| 275 | " retl\n" // restore pc |
| 276 | "1:\n" |
| 277 | : "+a"(cur), "+c"(next) |
| 278 | : |
| 279 | : "ebx", "edx", "esi", "edi", "cc", "memory"); |
| 280 | } |
| 281 | |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 282 | // Low-level irq enable. |
| 283 | void VISIBLE16 |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 284 | check_irqs(void) |
| 285 | { |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 286 | asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory"); |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 287 | } |
| 288 | |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 289 | // Briefly permit irqs to occur. |
| 290 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 291 | yield(void) |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 292 | { |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 293 | if (MODESEGMENT) { |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 294 | stack_hop_back(0, 0, check_irqs); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 295 | return; |
| 296 | } |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 297 | extern void _cfunc16_check_irqs(void); |
| 298 | if (!CONFIG_THREADS) { |
| 299 | call16big(0, _cfunc16_check_irqs); |
| 300 | return; |
| 301 | } |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 302 | struct thread_info *cur = getCurThread(); |
| 303 | if (cur == &MainThread) |
| 304 | // Permit irqs to fire |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 305 | call16big(0, _cfunc16_check_irqs); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 306 | |
| 307 | // Switch to the next thread |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 308 | switch_next(cur); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 309 | } |
| 310 | |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 311 | void VISIBLE16 |
| 312 | wait_irq(void) |
| 313 | { |
| 314 | asm volatile("sti ; hlt ; cli ; cld": : :"memory"); |
| 315 | } |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 316 | |
| 317 | // Wait for next irq to occur. |
| 318 | void |
| 319 | yield_toirq(void) |
| 320 | { |
| 321 | if (MODESEGMENT) { |
Kevin O'Connor | bf2e8c2 | 2012-05-28 12:59:58 -0400 | [diff] [blame] | 322 | stack_hop_back(0, 0, wait_irq); |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 323 | return; |
| 324 | } |
| 325 | if (CONFIG_THREADS && MainThread.next != &MainThread) { |
| 326 | // Threads still active - do a yield instead. |
| 327 | yield(); |
| 328 | return; |
| 329 | } |
Kevin O'Connor | 2f898d5 | 2012-05-28 10:56:20 -0400 | [diff] [blame] | 330 | extern void _cfunc16_wait_irq(void); |
| 331 | call16big(0, _cfunc16_wait_irq); |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 332 | } |
| 333 | |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 334 | // Last thing called from a thread (called on "next" stack). |
| 335 | static void |
| 336 | __end_thread(struct thread_info *old) |
| 337 | { |
Kevin O'Connor | a7eb8fc | 2010-04-02 13:13:23 -0400 | [diff] [blame] | 338 | old->next->pprev = old->pprev; |
| 339 | *old->pprev = old->next; |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 340 | free(old); |
| 341 | dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old); |
Kevin O'Connor | e438b0c | 2010-05-01 12:20:33 -0400 | [diff] [blame] | 342 | if (MainThread.next == &MainThread) |
| 343 | dprintf(1, "All threads complete.\n"); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 344 | } |
| 345 | |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 346 | // Create a new thread and start executing 'func' in it. |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 347 | void |
| 348 | run_thread(void (*func)(void*), void *data) |
| 349 | { |
Kevin O'Connor | 52a300f | 2009-12-26 23:32:57 -0500 | [diff] [blame] | 350 | ASSERT32FLAT(); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 351 | if (! CONFIG_THREADS) |
| 352 | goto fail; |
| 353 | struct thread_info *thread; |
| 354 | thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE); |
| 355 | if (!thread) |
| 356 | goto fail; |
| 357 | |
| 358 | thread->stackpos = (void*)thread + THREADSTACKSIZE; |
| 359 | struct thread_info *cur = getCurThread(); |
Kevin O'Connor | a7eb8fc | 2010-04-02 13:13:23 -0400 | [diff] [blame] | 360 | thread->next = cur; |
| 361 | thread->pprev = cur->pprev; |
| 362 | cur->pprev = &thread->next; |
| 363 | *thread->pprev = thread; |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 364 | |
| 365 | dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread); |
| 366 | asm volatile( |
| 367 | // Start thread |
| 368 | " pushl $1f\n" // store return pc |
| 369 | " pushl %%ebp\n" // backup %ebp |
| 370 | " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp |
| 371 | " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos |
| 372 | " calll *%%ecx\n" // Call func |
| 373 | |
| 374 | // End thread |
| 375 | " movl (%%ebx), %%ecx\n" // %ecx = thread->next |
| 376 | " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos |
| 377 | " movl %%ebx, %%eax\n" |
| 378 | " calll %4\n" // call __end_thread(thread) |
| 379 | " popl %%ebp\n" // restore %ebp |
| 380 | " retl\n" // restore pc |
| 381 | "1:\n" |
| 382 | : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur) |
| 383 | : "m"(*(u8*)__end_thread) |
| 384 | : "esi", "edi", "cc", "memory"); |
| 385 | return; |
| 386 | |
| 387 | fail: |
| 388 | func(data); |
| 389 | } |
| 390 | |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 391 | // Wait for all threads (other than the main thread) to complete. |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 392 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 393 | wait_threads(void) |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 394 | { |
Kevin O'Connor | 52a300f | 2009-12-26 23:32:57 -0500 | [diff] [blame] | 395 | ASSERT32FLAT(); |
Kevin O'Connor | 7cefbfa | 2009-12-10 21:35:49 -0500 | [diff] [blame] | 396 | if (! CONFIG_THREADS) |
| 397 | return; |
| 398 | while (MainThread.next != &MainThread) |
| 399 | yield(); |
| 400 | } |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 401 | |
Kevin O'Connor | e908665 | 2010-02-27 13:49:47 -0500 | [diff] [blame] | 402 | void |
| 403 | mutex_lock(struct mutex_s *mutex) |
| 404 | { |
| 405 | ASSERT32FLAT(); |
| 406 | if (! CONFIG_THREADS) |
| 407 | return; |
| 408 | while (mutex->isLocked) |
| 409 | yield(); |
| 410 | mutex->isLocked = 1; |
| 411 | } |
| 412 | |
| 413 | void |
| 414 | mutex_unlock(struct mutex_s *mutex) |
| 415 | { |
| 416 | ASSERT32FLAT(); |
| 417 | if (! CONFIG_THREADS) |
| 418 | return; |
| 419 | mutex->isLocked = 0; |
| 420 | } |
| 421 | |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 422 | |
| 423 | /**************************************************************** |
| 424 | * Thread preemption |
| 425 | ****************************************************************/ |
| 426 | |
Kevin O'Connor | e77c705 | 2012-05-28 22:06:42 -0400 | [diff] [blame] | 427 | int VAR16VISIBLE CanPreempt; |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 428 | static u32 PreemptCount; |
| 429 | |
| 430 | // Turn on RTC irqs and arrange for them to check the 32bit threads. |
| 431 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 432 | start_preempt(void) |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 433 | { |
Kevin O'Connor | b5c7da6 | 2013-02-08 00:45:44 -0500 | [diff] [blame^] | 434 | if (! CONFIG_THREAD_OPTIONROMS) |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 435 | return; |
| 436 | CanPreempt = 1; |
| 437 | PreemptCount = 0; |
| 438 | useRTC(); |
| 439 | } |
| 440 | |
| 441 | // Turn off RTC irqs / stop checking for thread execution. |
| 442 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 443 | finish_preempt(void) |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 444 | { |
Kevin O'Connor | b5c7da6 | 2013-02-08 00:45:44 -0500 | [diff] [blame^] | 445 | if (! CONFIG_THREAD_OPTIONROMS) { |
Kevin O'Connor | a7eb8fc | 2010-04-02 13:13:23 -0400 | [diff] [blame] | 446 | yield(); |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 447 | return; |
Kevin O'Connor | a7eb8fc | 2010-04-02 13:13:23 -0400 | [diff] [blame] | 448 | } |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 449 | CanPreempt = 0; |
| 450 | releaseRTC(); |
Kevin O'Connor | e438b0c | 2010-05-01 12:20:33 -0400 | [diff] [blame] | 451 | dprintf(9, "Done preempt - %d checks\n", PreemptCount); |
Kevin O'Connor | a7eb8fc | 2010-04-02 13:13:23 -0400 | [diff] [blame] | 452 | yield(); |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 453 | } |
| 454 | |
Kevin O'Connor | d7eb27e | 2010-03-20 18:17:19 -0400 | [diff] [blame] | 455 | // Check if preemption is on, and wait for it to complete if so. |
| 456 | int |
| 457 | wait_preempt(void) |
| 458 | { |
Kevin O'Connor | b5c7da6 | 2013-02-08 00:45:44 -0500 | [diff] [blame^] | 459 | if (MODESEGMENT || !CONFIG_THREAD_OPTIONROMS || !CanPreempt) |
Kevin O'Connor | d7eb27e | 2010-03-20 18:17:19 -0400 | [diff] [blame] | 460 | return 0; |
| 461 | while (CanPreempt) |
| 462 | yield(); |
| 463 | return 1; |
| 464 | } |
| 465 | |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 466 | // Try to execute 32bit threads. |
Kevin O'Connor | d1b4f96 | 2010-09-15 21:38:16 -0400 | [diff] [blame] | 467 | void VISIBLE32INIT |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 468 | yield_preempt(void) |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 469 | { |
| 470 | PreemptCount++; |
| 471 | switch_next(&MainThread); |
| 472 | } |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 473 | |
| 474 | // 16bit code that checks if threads are pending and executes them if so. |
| 475 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 476 | check_preempt(void) |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 477 | { |
Kevin O'Connor | b5c7da6 | 2013-02-08 00:45:44 -0500 | [diff] [blame^] | 478 | if (! CONFIG_THREAD_OPTIONROMS || !GET_GLOBAL(CanPreempt) |
Kevin O'Connor | 4d96edc | 2010-09-25 14:53:15 -0400 | [diff] [blame] | 479 | || GET_FLATPTR(MainThread.next) == &MainThread) |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 480 | return; |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 481 | |
Kevin O'Connor | f3fe3aa | 2010-12-05 12:38:33 -0500 | [diff] [blame] | 482 | extern void _cfunc32flat_yield_preempt(void); |
| 483 | call32(_cfunc32flat_yield_preempt, 0, 0); |
Kevin O'Connor | ad90159 | 2009-12-13 11:25:25 -0500 | [diff] [blame] | 484 | } |