revert between 56095 -> 55830 in arch
[AROS.git] / arch / all-mingw32 / kernel / host_intr.c
blob04e2038a2eb067a7a04e3987fd518d78a8e57ca1
1 /*
2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #define __CRT_INLINE __attribute__ ((__always_inline__))
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <windows.h>
12 /* PRINT_CPUCONTEXT() macro uses bug() because it can be useful on AROS side too. */
13 #define bug printf
15 #include "host_intern.h"
16 #include "kernel_arch.h"
17 #include "kernel_cpu.h"
20 * Console output in Windows seems to be protected via semaphores. SuspendThread() at the moment
21 * of writing something to console leaves it in locked state. Attempt to write something to it from
22 * within task scheduler may halt the system. Because of this only init debug actually works and
23 * is safe to use.
25 #define D(x) /* Init debug */
26 #define DS(x) /* Task switcher debug */
27 #define DINT(x) /* Interrupts debug */
28 #define DTRAP(x) /* Traps debug */
30 HANDLE MainThread;
31 DWORD MainThreadId;
32 unsigned short Ints_Num;
33 HANDLE IntObjects[256];
34 unsigned char PendingInts[256];
35 unsigned char AllocatedInts[256];
37 /* Virtual CPU control registers */
38 /* Note: variables need to have values set, otherwise they don't get exported from DLL */
39 __declspec(dllexport) int (*TrapVector)(unsigned int num, ULONG_PTR *args, CONTEXT *regs) = NULL;
40 __declspec(dllexport) int (*IRQVector)(unsigned char *irqs, CONTEXT *regs) = NULL;
41 __declspec(dllexport) int NonMaskableInt = -1;
42 volatile __declspec(dllexport) int Ints_Enabled = 0;
43 volatile __declspec(dllexport) int Supervisor = 0;
44 volatile __declspec(dllexport) unsigned char Sleep_Mode = 0;
45 volatile __declspec(dllexport) DWORD * LastErrorPtr = 0;
48 * This can't be placed on stack because noone knows
49 * what happens to it upon returning from Windows exception.
50 * Luckily our trap handler is guaranteed to be single-threaded,
51 * so we can safely declare this structure static.
53 static struct LeaveInterruptContext leavecontext;
55 LONG WINAPI exceptionHandler(EXCEPTION_POINTERS *exptr)
57 DWORD ExceptionCode = exptr->ExceptionRecord->ExceptionCode;
58 CONTEXT *ContextRecord = exptr->ContextRecord;
59 DWORD ThreadId;
60 int intstate;
63 * We are already in interrupt and we must not be preempted by task switcher.
64 * Note that up to this point we still can be preempted by task switcher, in
65 * fact it's not good, but this will happen only upon CPU exception. core_raise()
66 * disables interrupts before raising an exception, so i really hope AROS will fail
67 * only in very rare cases and only upon software failure.
69 Ints_Enabled = INT_DISABLE;
71 /* Exception in other thread, probably in virtual hardware. Die. */
72 ThreadId = GetCurrentThreadId();
73 if (ThreadId != MainThreadId)
75 printf("[KRN] Service thread 0x%lu, exception 0x%08lX\n", ThreadId, ExceptionCode);
76 PRINT_CPUCONTEXT(ContextRecord);
78 return EXCEPTION_CONTINUE_SEARCH;
81 /* Enter supervisor mode. We can already be in supervisor (crashed inside
82 IRQ handler), so we increment in order to retain previous state. */
83 Supervisor++;
85 /* Call trap handler */
86 DTRAP(bug("[KRN] Trap 0x%08lX\n", ExceptionCode));
88 intstate = TrapVector(ExceptionCode, exptr->ExceptionRecord->ExceptionInformation, ContextRecord);
90 if (intstate == INT_HALT)
92 printf("[KRN] **UNHANDLED EXCEPTION 0x%08lX** stopping here...\n", ExceptionCode);
94 return EXCEPTION_CONTINUE_SEARCH;
97 DTRAP(printf("[KRN] Leaving trap, Supervisor %d, intstate %d, PC 0x%p\n", Supervisor, intstate, (void *)PC(ContextRecord)));
99 /* Exit supervisor */
100 if (--Supervisor == 0)
102 /* If we are leaving to user mode, we may need to enable interrupts */
103 if (intstate)
106 * We must enable interrupts only after return from Windows
107 * exception. Otherwise supervisor thread may preempt us
108 * between enabling interrupts and actual exit, and this will
109 * cause process abort. We use core_LeaveInterrupt() routine
110 * written in asm to solve this task. We cause the task to jump
111 * to it upon return, the routine enables interrupts and then
112 * jumps to real task's PC, which is passed to it inside
113 * struct LeaveInterruptContext.
114 * The helper clobbers R0 register, so we also save it.
116 leavecontext.pc = PC(ContextRecord);
117 leavecontext.r0 = R0(ContextRecord);
118 R0(ContextRecord) = (UINT_PTR)&leavecontext;
119 PC(ContextRecord) = (UINT_PTR)core_LeaveInterrupt;
121 * If this is a newly created context, it may contain no integer registers.
122 * Here we use R0, so explicitly turn on CONTEXT_INTEGER flag.
124 ContextRecord->ContextFlags |= CONTEXT_INTEGER;
128 return EXCEPTION_CONTINUE_EXECUTION;
131 #ifdef __x86_64__
133 * Magic: on x86-64 we can't preempt within a certain location. Not good,
134 * but i can't offer something better. See leaveinterrupt_x86_64.s.
136 #define INT_SAFE(ctx) ((ctx.Rip < (DWORD64)core_LeaveInterrupt) || (ctx.Rip >= (DWORD64)&core_LeaveInt_End))
137 #else
138 #define INT_SAFE(ctx) TRUE
139 #endif
141 DWORD WINAPI TaskSwitcher()
143 DWORD obj;
144 CONTEXT MainCtx;
146 for (;;)
148 obj = WaitForMultipleObjects(Ints_Num, IntObjects, FALSE, INFINITE);
149 PendingInts[obj] = 1;
150 DINT(printf("[Task switcher] Object %lu signalled, interrupt enable %d\n", obj, Ints_Enabled));
152 /* Stop main thread if it's not sleeping */
153 if (Sleep_Mode != SLEEP_MODE_ON)
155 SuspendThread(MainThread);
157 * People say that on SMP systems thread is not stopped immediately by SuspendThread().
158 * So we have to do our best to ensure that is is really stopped. I hope GetThreadContext()
159 * guarantees it.
161 CONTEXT_INIT_FLAGS(&MainCtx);
162 GetThreadContext(MainThread, &MainCtx);
165 /* Process interrupts if we are allowed to */
166 if ((Ints_Enabled && INT_SAFE(MainCtx)) || (obj == NonMaskableInt))
168 Supervisor = 1;
170 * We get and store the complete CPU context, but set only part of it
171 * because changing some registers causes Windows to immediately shut down
172 * our process. This can be a useful aid for future AROS debuggers.
174 DS(printf("[Task switcher] original CPU context: ****\n"));
175 DS(PRINT_CPUCONTEXT(&MainCtx));
178 * Call IRQ handlers for all pending interrupts.
179 * This means that deferred interrupts may be processed with
180 * delay, meximum of one timer period, but anyway, who told
181 * that Windows is a realtime OS?
183 Ints_Enabled = IRQVector(PendingInts, &MainCtx);
184 /* All IRQs have been processed */
185 ZeroMemory(PendingInts, sizeof(PendingInts));
186 /* Leave supervisor mode. Interrupt state is already updated by IRQVector(). */
187 Supervisor = 0;
189 /* Resume main thread if AROS is not sleeping */
190 if (Sleep_Mode == SLEEP_MODE_OFF)
192 DS(printf("[Task switcher] new CPU context: ****\n"));
193 DS(PRINT_CPUCONTEXT(&MainCtx));
194 SetThreadContext(MainThread, &MainCtx);
195 ResumeThread(MainThread);
197 else
199 /* We've entered sleep mode. Main thread is kept suspended. */
200 Sleep_Mode = SLEEP_MODE_ON;
203 else
206 * Interrupts are disabled here. Do not commit sleep mode, or we
207 * end up in sleeping main thread before it enables interrupt.
208 * This will cause a deadlock.
210 if (Sleep_Mode != SLEEP_MODE_ON)
211 ResumeThread(MainThread);
214 return 0;
217 /* ****** Interface functions ****** */
219 void __declspec(dllexport) __aros core_raise(DWORD code, const ULONG_PTR n)
222 * This ensures that we are never preempted inside RaiseException().
223 * Upon exit from the syscall interrupt state will be restored by
224 * core_LeaveInterrupt().
226 Ints_Enabled = INT_DISABLE;
228 DTRAP(printf("[KRN] Raising exception 0x%08lX, SP 0x%p\n", code, stack));
229 RaiseException(code, 0, 1, &n);
231 /* If after RaiseException we are still here, but Sleep_Mode != 0, this likely means
232 we've just called SC_SCHEDULE, SC_SWITCH or SC_DISPATCH, and it is putting us to sleep.
233 Sleep mode will be committed as soon as timer IRQ happens */
234 while (Sleep_Mode);
237 unsigned long __declspec(dllexport) __aros StartClock(unsigned int irq, unsigned int TimerPeriod)
239 LARGE_INTEGER TimerValue;
241 TimerPeriod = 1000 / TimerPeriod;
242 TimerValue.QuadPart = -10000 * (LONGLONG)TimerPeriod;
244 return SetWaitableTimer(IntObjects[irq], &TimerValue, TimerPeriod, NULL, NULL, 0);
248 * Start up virtual machine.
249 * Initializes IRQ engine, runs virtual supervisor thread and starts up main system timer.
250 * Trap and IRQ vectors must be already set up, we don't check them against NULLs.
252 int __declspec(dllexport) __aros core_init(unsigned int TimerPeriod)
254 HANDLE ThisProcess;
255 HANDLE SwitcherThread;
256 void *MainTEB;
257 int i;
258 DWORD SwitcherId;
260 D(printf("[KRN] Setting up interrupts\n"));
261 Ints_Enabled = INT_DISABLE;
262 Supervisor = 0;
263 Sleep_Mode = SLEEP_MODE_OFF;
264 for (i = 1; i < 256; i++)
266 IntObjects[i] = NULL;
267 AllocatedInts[i] = 0;
270 /* Set up traps */
271 MainThreadId = GetCurrentThreadId();
272 #ifdef __x86_64__
273 AddVectoredExceptionHandler(TRUE, exceptionHandler);
274 #else
275 SetUnhandledExceptionFilter(exceptionHandler);
276 #endif
278 /* Set up debug I/O */
279 conin = GetStdHandle(STD_INPUT_HANDLE);
280 conout = GetStdHandle(STD_OUTPUT_HANDLE);
282 /* Statically allocate main system timer */
283 for (i = 0; i < 2; i++)
285 IntObjects[i] = CreateWaitableTimer(NULL, FALSE, NULL);
286 if (!IntObjects[i])
288 D(printf("[KRN] Failed to create timer %u\n", i));
289 return 0;
291 AllocatedInts[i] = 1;
292 PendingInts[i] = 0;
294 Ints_Num = 2;
296 ThisProcess = GetCurrentProcess();
297 if (DuplicateHandle(ThisProcess, GetCurrentThread(), ThisProcess, &MainThread, 0, TRUE, DUPLICATE_SAME_ACCESS))
299 #ifdef __x86_64__
300 #define LastErrOffset 0x68
301 #else
302 /* On 32-bit x86 we have to figure out the offset depending on Windows version */
303 OSVERSIONINFO osver = {0};
304 ULONG LastErrOffset = 0;
306 osver.dwOSVersionInfoSize = sizeof(osver);
307 GetVersionEx(&osver);
310 * LastError value is part of our context. In order to manipulate it we have to hack
311 * into Windows TEB (thread environment block).
312 * Since this structure is private, error code offset changes from version to version.
313 * The following offsets are known:
314 * - Windows 95 and 98 - 0x60
315 * - Windows Me - 0x74
316 * - Windows NT (all family, fixed at last) - 0x34
318 switch(osver.dwPlatformId)
320 case VER_PLATFORM_WIN32_WINDOWS:
321 if (osver.dwMajorVersion == 4)
323 if (osver.dwMinorVersion > 10)
324 LastErrOffset = 0x74;
325 else
326 LastErrOffset = 0x60;
328 break;
330 case VER_PLATFORM_WIN32_NT:
331 LastErrOffset = 0x34;
332 break;
335 if (!LastErrOffset)
337 printf("Unsupported Windows version %lu.%lu, platform ID %lu\n", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwPlatformId);
338 return 0;
340 #endif
342 #if (0)
343 MainTEB = NtCurrentTeb();
344 #endif
345 LastErrorPtr = MainTEB + LastErrOffset;
347 SwitcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TaskSwitcher, NULL, 0, &SwitcherId);
348 if (SwitcherThread)
350 SYSTEM_INFO info;
352 D(printf("[KRN] Task switcher started, ID %lu\n", SwitcherId));
354 /* Start timer 0 */
355 if (!StartClock(0, TimerPeriod))
356 return 0;
358 /* Return system page size */
359 GetSystemInfo(&info);
360 return info.dwPageSize;
362 D(else printf("[KRN] Failed to run task switcher thread\n");)
364 D(else printf("[KRN] failed to get thread handle\n");)
366 CloseHandle(IntObjects[IRQ_TIMER]);
367 return 0;
371 * The following is host-side IRQ API.
373 * It is used by virtual hadrware implemented as asynchronous host operating
374 * system threads.
378 int __declspec(dllexport) __aros KrnAllocSystemIRQ(void)
380 int irq;
382 for (irq = 0; irq < 256; irq++)
384 if (!AllocatedInts[irq])
386 if (!IntObjects[irq])
388 IntObjects[irq] = CreateEvent(NULL, FALSE, FALSE, NULL);
390 if (!IntObjects[irq])
391 return -1;
393 PendingInts[irq] = 0;
394 AllocatedInts[irq] = 1;
396 if (irq == Ints_Num)
397 Ints_Num++;
399 return irq;
402 return -1;
406 void __declspec(dllexport) __aros KrnFreeSystemIRQ(unsigned char irq)
408 AllocatedInts[irq] = 0;
410 while (!AllocatedInts[Ints_Num - 1])
411 Ints_Num--;
414 void *__declspec(dllexport) __aros KrnGetSystemIRQObject(unsigned char irq)
416 return IntObjects[irq];
419 unsigned long __declspec(dllexport) __aros KrnCauseSystemIRQ(unsigned char irq)
421 unsigned long res;
423 D(printf("[kernel IRQ] Causing IRQ %u\n", irq));
424 res = SetEvent(IntObjects[irq]);
425 D(printf("[kernel IRQ] Result: %ld\n", res));
426 return res;