remove erroneous line
[AROS.git] / arch / all-mingw32 / kernel / host_intr.c
blobed56acf889f40637bae2c545b9c02b2ff2669cc9
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <windows.h>
10 /* PRINT_CPUCONTEXT() macro uses bug() because it can be useful on AROS side too. */
11 #define bug printf
13 #include "host_intern.h"
14 #include "kernel_arch.h"
15 #include "kernel_cpu.h"
18 * Console output in Windows seems to be protected via semaphores. SuspendThread() at the moment
19 * of writing something to console leaves it in locked state. Attempt to write something to it from
20 * within task scheduler may halt the system. Because of this only init debug actually works and
21 * is safe to use.
23 #define D(x) /* Init debug */
24 #define DS(x) /* Task switcher debug */
25 #define DINT(x) /* Interrupts debug */
26 #define DTRAP(x) /* Traps debug */
28 HANDLE MainThread;
29 DWORD MainThreadId;
30 unsigned short Ints_Num;
31 HANDLE IntObjects[256];
32 unsigned char PendingInts[256];
33 unsigned char AllocatedInts[256];
35 /* Virtual CPU control registers */
36 /* Note: variables need to have values set, otherwise they don't get exported from DLL */
37 __declspec(dllexport) int (*TrapVector)(unsigned int num, ULONG_PTR *args, CONTEXT *regs) = NULL;
38 __declspec(dllexport) int (*IRQVector)(unsigned char *irqs, CONTEXT *regs) = NULL;
39 __declspec(dllexport) int NonMaskableInt = -1;
40 volatile __declspec(dllexport) int Ints_Enabled = 0;
41 volatile __declspec(dllexport) int Supervisor = 0;
42 volatile __declspec(dllexport) unsigned char Sleep_Mode = 0;
43 volatile __declspec(dllexport) DWORD * LastErrorPtr = 0;
46 * This can't be placed on stack because noone knows
47 * what happens to it upon returning from Windows exception.
48 * Luckily our trap handler is guaranteed to be single-threaded,
49 * so we can safely declare this structure static.
51 static struct LeaveInterruptContext leavecontext;
53 LONG WINAPI exceptionHandler(EXCEPTION_POINTERS *exptr)
55 DWORD ExceptionCode = exptr->ExceptionRecord->ExceptionCode;
56 CONTEXT *ContextRecord = exptr->ContextRecord;
57 DWORD ThreadId;
58 int intstate;
61 * We are already in interrupt and we must not be preempted by task switcher.
62 * Note that up to this point we still can be preempted by task switcher, in
63 * fact it's not good, but this will happen only upon CPU exception. core_raise()
64 * disables interrupts before raising an exception, so i really hope AROS will fail
65 * only in very rare cases and only upon software failure.
67 Ints_Enabled = INT_DISABLE;
69 /* Exception in other thread, probably in virtual hardware. Die. */
70 ThreadId = GetCurrentThreadId();
71 if (ThreadId != MainThreadId)
73 printf("[KRN] Service thread 0x%lu, exception 0x%08lX\n", ThreadId, ExceptionCode);
74 PRINT_CPUCONTEXT(ContextRecord);
76 return EXCEPTION_CONTINUE_SEARCH;
79 /* Enter supervisor mode. We can already be in supervisor (crashed inside
80 IRQ handler), so we increment in order to retain previous state. */
81 Supervisor++;
83 /* Call trap handler */
84 DTRAP(bug("[KRN] Trap 0x%08lX\n", ExceptionCode));
86 intstate = TrapVector(ExceptionCode, exptr->ExceptionRecord->ExceptionInformation, ContextRecord);
88 if (intstate == INT_HALT)
90 printf("[KRN] **UNHANDLED EXCEPTION 0x%08lX** stopping here...\n", ExceptionCode);
92 return EXCEPTION_CONTINUE_SEARCH;
95 DTRAP(printf("[KRN] Leaving trap, Supervisor %d, intstate %d, PC 0x%p\n", Supervisor, intstate, (void *)PC(ContextRecord)));
97 /* Exit supervisor */
98 if (--Supervisor == 0)
100 /* If we are leaving to user mode, we may need to enable interrupts */
101 if (intstate)
104 * We must enable interrupts only after return from Windows
105 * exception. Otherwise supervisor thread may preempt us
106 * between enabling interrupts and actual exit, and this will
107 * cause process abort. We use core_LeaveInterrupt() routine
108 * written in asm to solve this task. We cause the task to jump
109 * to it upon return, the routine enables interrupts and then
110 * jumps to real task's PC, which is passed to it inside
111 * struct LeaveInterruptContext.
112 * The helper clobbers R0 register, so we also save it.
114 leavecontext.pc = PC(ContextRecord);
115 leavecontext.r0 = R0(ContextRecord);
116 R0(ContextRecord) = (UINT_PTR)&leavecontext;
117 PC(ContextRecord) = (UINT_PTR)core_LeaveInterrupt;
119 * If this is a newly created context, it may contain no integer registers.
120 * Here we use R0, so explicitly turn on CONTEXT_INTEGER flag.
122 ContextRecord->ContextFlags |= CONTEXT_INTEGER;
126 return EXCEPTION_CONTINUE_EXECUTION;
129 #ifdef __x86_64__
131 * Magic: on x86-64 we can't preempt within a certain location. Not good,
132 * but i can't offer something better. See leaveinterrupt_x86_64.s.
134 #define INT_SAFE(ctx) ((ctx.Rip < (DWORD64)core_LeaveInterrupt) || (ctx.Rip >= (DWORD64)&core_LeaveInt_End))
135 #else
136 #define INT_SAFE(ctx) TRUE
137 #endif
139 DWORD WINAPI TaskSwitcher()
141 DWORD obj;
142 CONTEXT MainCtx;
144 for (;;)
146 obj = WaitForMultipleObjects(Ints_Num, IntObjects, FALSE, INFINITE);
147 PendingInts[obj] = 1;
148 DINT(printf("[Task switcher] Object %lu signalled, interrupt enable %d\n", obj, Ints_Enabled));
150 /* Stop main thread if it's not sleeping */
151 if (Sleep_Mode != SLEEP_MODE_ON)
153 SuspendThread(MainThread);
155 * People say that on SMP systems thread is not stopped immediately by SuspendThread().
156 * So we have to do our best to ensure that is is really stopped. I hope GetThreadContext()
157 * guarantees it.
159 CONTEXT_INIT_FLAGS(&MainCtx);
160 GetThreadContext(MainThread, &MainCtx);
163 /* Process interrupts if we are allowed to */
164 if ((Ints_Enabled && INT_SAFE(MainCtx)) || (obj == NonMaskableInt))
166 Supervisor = 1;
168 * We get and store the complete CPU context, but set only part of it
169 * because changing some registers causes Windows to immediately shut down
170 * our process. This can be a useful aid for future AROS debuggers.
172 DS(printf("[Task switcher] original CPU context: ****\n"));
173 DS(PRINT_CPUCONTEXT(&MainCtx));
176 * Call IRQ handlers for all pending interrupts.
177 * This means that deferred interrupts may be processed with
178 * delay, meximum of one timer period, but anyway, who told
179 * that Windows is a realtime OS?
181 Ints_Enabled = IRQVector(PendingInts, &MainCtx);
182 /* All IRQs have been processed */
183 ZeroMemory(PendingInts, sizeof(PendingInts));
184 /* Leave supervisor mode. Interrupt state is already updated by IRQVector(). */
185 Supervisor = 0;
187 /* Resume main thread if AROS is not sleeping */
188 if (Sleep_Mode == SLEEP_MODE_OFF)
190 DS(printf("[Task switcher] new CPU context: ****\n"));
191 DS(PRINT_CPUCONTEXT(&MainCtx));
192 SetThreadContext(MainThread, &MainCtx);
193 ResumeThread(MainThread);
195 else
197 /* We've entered sleep mode. Main thread is kept suspended. */
198 Sleep_Mode = SLEEP_MODE_ON;
201 else
204 * Interrupts are disabled here. Do not commit sleep mode, or we
205 * end up in sleeping main thread before it enables interrupt.
206 * This will cause a deadlock.
208 if (Sleep_Mode != SLEEP_MODE_ON)
209 ResumeThread(MainThread);
212 return 0;
215 /* ****** Interface functions ****** */
217 void __declspec(dllexport) __aros core_raise(DWORD code, const ULONG_PTR n)
220 * This ensures that we are never preempted inside RaiseException().
221 * Upon exit from the syscall interrupt state will be restored by
222 * core_LeaveInterrupt().
224 Ints_Enabled = INT_DISABLE;
226 DTRAP(printf("[KRN] Raising exception 0x%08lX, SP 0x%p\n", code, stack));
227 RaiseException(code, 0, 1, &n);
229 /* If after RaiseException we are still here, but Sleep_Mode != 0, this likely means
230 we've just called SC_SCHEDULE, SC_SWITCH or SC_DISPATCH, and it is putting us to sleep.
231 Sleep mode will be committed as soon as timer IRQ happens */
232 while (Sleep_Mode);
235 unsigned long __declspec(dllexport) __aros StartClock(unsigned int irq, unsigned int TimerPeriod)
237 LARGE_INTEGER TimerValue;
239 TimerPeriod = 1000 / TimerPeriod;
240 TimerValue.QuadPart = -10000 * (LONGLONG)TimerPeriod;
242 return SetWaitableTimer(IntObjects[irq], &TimerValue, TimerPeriod, NULL, NULL, 0);
246 * Start up virtual machine.
247 * Initializes IRQ engine, runs virtual supervisor thread and starts up main system timer.
248 * Trap and IRQ vectors must be already set up, we don't check them against NULLs.
250 int __declspec(dllexport) __aros core_init(unsigned int TimerPeriod)
252 HANDLE ThisProcess;
253 HANDLE SwitcherThread;
254 void *MainTEB;
255 int i;
256 DWORD SwitcherId;
258 D(printf("[KRN] Setting up interrupts\n"));
259 Ints_Enabled = INT_DISABLE;
260 Supervisor = 0;
261 Sleep_Mode = SLEEP_MODE_OFF;
262 for (i = 1; i < 256; i++)
264 IntObjects[i] = NULL;
265 AllocatedInts[i] = 0;
268 /* Set up traps */
269 MainThreadId = GetCurrentThreadId();
270 #ifdef __x86_64__
271 AddVectoredExceptionHandler(TRUE, exceptionHandler);
272 #else
273 SetUnhandledExceptionFilter(exceptionHandler);
274 #endif
276 /* Set up debug I/O */
277 conin = GetStdHandle(STD_INPUT_HANDLE);
278 conout = GetStdHandle(STD_OUTPUT_HANDLE);
280 /* Statically allocate main system timer */
281 for (i = 0; i < 2; i++)
283 IntObjects[i] = CreateWaitableTimer(NULL, FALSE, NULL);
284 if (!IntObjects[i])
286 D(printf("[KRN] Failed to create timer %u\n", i));
287 return 0;
289 AllocatedInts[i] = 1;
290 PendingInts[i] = 0;
292 Ints_Num = 2;
294 ThisProcess = GetCurrentProcess();
295 if (DuplicateHandle(ThisProcess, GetCurrentThread(), ThisProcess, &MainThread, 0, TRUE, DUPLICATE_SAME_ACCESS))
297 #ifdef __x86_64__
298 #define LastErrOffset 0x68
299 #else
300 /* On 32-bit x86 we have to figure out the offset depending on Windows version */
301 OSVERSIONINFO osver = {0};
302 ULONG LastErrOffset = 0;
304 osver.dwOSVersionInfoSize = sizeof(osver);
305 GetVersionEx(&osver);
308 * LastError value is part of our context. In order to manipulate it we have to hack
309 * into Windows TEB (thread environment block).
310 * Since this structure is private, error code offset changes from version to version.
311 * The following offsets are known:
312 * - Windows 95 and 98 - 0x60
313 * - Windows Me - 0x74
314 * - Windows NT (all family, fixed at last) - 0x34
316 switch(osver.dwPlatformId)
318 case VER_PLATFORM_WIN32_WINDOWS:
319 if (osver.dwMajorVersion == 4)
321 if (osver.dwMinorVersion > 10)
322 LastErrOffset = 0x74;
323 else
324 LastErrOffset = 0x60;
326 break;
328 case VER_PLATFORM_WIN32_NT:
329 LastErrOffset = 0x34;
330 break;
333 if (!LastErrOffset)
335 printf("Unsupported Windows version %lu.%lu, platform ID %lu\n", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwPlatformId);
336 return 0;
338 #endif
340 MainTEB = NtCurrentTeb();
341 LastErrorPtr = MainTEB + LastErrOffset;
343 SwitcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TaskSwitcher, NULL, 0, &SwitcherId);
344 if (SwitcherThread)
346 SYSTEM_INFO info;
348 D(printf("[KRN] Task switcher started, ID %lu\n", SwitcherId));
350 /* Start timer 0 */
351 if (!StartClock(0, TimerPeriod))
352 return 0;
354 /* Return system page size */
355 GetSystemInfo(&info);
356 return info.dwPageSize;
358 D(else printf("[KRN] Failed to run task switcher thread\n");)
360 D(else printf("[KRN] failed to get thread handle\n");)
362 CloseHandle(IntObjects[IRQ_TIMER]);
363 return 0;
367 * The following is host-side IRQ API.
369 * It is used by virtual hadrware implemented as asynchronous host operating
370 * system threads.
374 int __declspec(dllexport) __aros KrnAllocIRQ(void)
376 int irq;
378 for (irq = 0; irq < 256; irq++)
380 if (!AllocatedInts[irq])
382 if (!IntObjects[irq])
384 IntObjects[irq] = CreateEvent(NULL, FALSE, FALSE, NULL);
386 if (!IntObjects[irq])
387 return -1;
389 PendingInts[irq] = 0;
390 AllocatedInts[irq] = 1;
392 if (irq == Ints_Num)
393 Ints_Num++;
395 return irq;
398 return -1;
402 void __declspec(dllexport) __aros KrnFreeIRQ(unsigned char irq)
404 AllocatedInts[irq] = 0;
406 while (!AllocatedInts[Ints_Num - 1])
407 Ints_Num--;
410 void *__declspec(dllexport) __aros KrnGetIRQObject(unsigned char irq)
412 return IntObjects[irq];
415 unsigned long __declspec(dllexport) __aros KrnCauseIRQ(unsigned char irq)
417 unsigned long res;
419 D(printf("[kernel IRQ] Causing IRQ %u\n", irq));
420 res = SetEvent(IntObjects[irq]);
421 D(printf("[kernel IRQ] Result: %ld\n", res));
422 return res;