Added support for compiling C++ files. It isn't included for all
[AROS.git] / arch / all-mingw32 / kernel / host_intr.c
blob0d5d7a1ad2cf09e6f4f46f3183418c39967eab7b
1 #include <stddef.h>
2 #include <stdio.h>
3 #include <windows.h>
5 /* PRINT_CPUCONTEXT() macro uses bug() because it can be useful on AROS side too. */
6 #define bug printf
8 #include "host_intern.h"
9 #include "kernel_arch.h"
10 #include "kernel_cpu.h"
13 * Console output in Windows seems to be protected via semaphores. SuspendThread() at the moment
14 * of writing something to console leaves it in locked state. Attempt to write something to it from
15 * within task scheduler may halt the system. Because of this only init debug actually works and
16 * is safe to use.
18 #define D(x) /* Init debug */
19 #define DS(x) /* Task switcher debug */
20 #define DINT(x) /* Interrupts debug */
21 #define DTRAP(x) /* Traps debug */
23 HANDLE MainThread;
24 DWORD MainThreadId;
25 unsigned short Ints_Num;
26 HANDLE IntObjects[256];
27 unsigned char PendingInts[256];
28 unsigned char AllocatedInts[256];
30 /* Virtual CPU control registers */
31 int __declspec(dllexport) __aros (*TrapVector)(unsigned int num, ULONG_PTR *args, CONTEXT *regs);
32 int __declspec(dllexport) __aros (*IRQVector)(unsigned char *irqs, CONTEXT *regs);
33 volatile int __declspec(dllexport) Ints_Enabled;
34 volatile int __declspec(dllexport) Supervisor;
35 volatile unsigned char __declspec(dllexport) Sleep_Mode;
36 volatile DWORD * __declspec(dllexport) LastErrorPtr;
39 * This can't be placed on stack because noone knows
40 * what happens to it upon returning from Windows exception.
41 * Luckily our trap handler is guaranteed to be single-threaded,
42 * so we can safely declare this structure static.
44 static struct LeaveInterruptContext leavecontext;
46 LONG WINAPI exceptionHandler(EXCEPTION_POINTERS *exptr)
48 DWORD ExceptionCode = exptr->ExceptionRecord->ExceptionCode;
49 CONTEXT *ContextRecord = exptr->ContextRecord;
50 DWORD ThreadId;
51 int intstate;
54 * We are already in interrupt and we must not be preempted by task switcher.
55 * Note that up to this point we still can be preempted by task switcher, in
56 * fact it's not good, but this will happen only upon CPU exception. core_raise()
57 * disables interrupts before raising an exception, so i really hope AROS will fail
58 * only in very rare cases and only upon software failure.
60 Ints_Enabled = INT_DISABLE;
62 /* Exception in other thread, probably in virtual hardware. Die. */
63 ThreadId = GetCurrentThreadId();
64 if (ThreadId != MainThreadId)
66 printf("[KRN] Service thread 0x%lu, exception 0x%08lX\n", ThreadId, ExceptionCode);
67 PRINT_CPUCONTEXT(ContextRecord);
69 return EXCEPTION_CONTINUE_SEARCH;
72 /* Enter supervisor mode. We can already be in supervisor (crashed inside
73 IRQ handler), so we increment in order to retain previous state. */
74 Supervisor++;
76 /* Call trap handler */
77 DTRAP(bug("[KRN] Trap 0x%08lX\n", ExceptionCode));
79 intstate = TrapVector(ExceptionCode, exptr->ExceptionRecord->ExceptionInformation, ContextRecord);
81 if (intstate == INT_HALT)
83 printf("[KRN] **UNHANDLED EXCEPTION 0x%08lX** stopping here...\n", ExceptionCode);
85 return EXCEPTION_CONTINUE_SEARCH;
88 DTRAP(printf("[KRN] Leaving trap, Supervisor %d, intstate %d, PC 0x%p\n", Supervisor, intstate, (void *)PC(ContextRecord)));
90 /* Exit supervisor */
91 if (--Supervisor == 0)
93 /* If we are leaving to user mode, we may need to enable interrupts */
94 if (intstate)
97 * We must enable interrupts only after return from Windows
98 * exception. Otherwise supervisor thread may preempt us
99 * between enabling interrupts and actual exit, and this will
100 * cause process abort. We use core_LeaveInterrupt() routine
101 * written in asm to solve this task. We cause the task to jump
102 * to it upon return, the routine enables interrupts and then
103 * jumps to real task's PC, which is passed to it inside
104 * struct LeaveInterruptContext.
105 * The helper clobbers R0 register, so we also save it.
107 leavecontext.pc = PC(ContextRecord);
108 leavecontext.r0 = R0(ContextRecord);
109 R0(ContextRecord) = (UINT_PTR)&leavecontext;
110 PC(ContextRecord) = (UINT_PTR)core_LeaveInterrupt;
112 * If this is a newly created context, it may contain no integer registers.
113 * Here we use R0, so explicitly turn on CONTEXT_INTEGER flag.
115 ContextRecord->ContextFlags |= CONTEXT_INTEGER;
119 return EXCEPTION_CONTINUE_EXECUTION;
122 #ifdef __x86_64__
124 * Magic: on x86-64 we can't preempt within a certain location. Not good,
125 * but i can't offer something better. See leaveinterrupt_x86_64.s.
127 #define INT_SAFE(ctx) ((ctx.Rip < (DWORD64)core_LeaveInterrupt) || (ctx.Rip >= (DWORD64)&core_LeaveInt_End))
128 #else
129 #define INT_SAFE(ctx) TRUE
130 #endif
132 DWORD WINAPI TaskSwitcher()
134 DWORD obj;
135 CONTEXT MainCtx;
137 for (;;)
139 obj = WaitForMultipleObjects(Ints_Num, IntObjects, FALSE, INFINITE);
140 PendingInts[obj] = 1;
141 DINT(printf("[Task switcher] Object %lu signalled, interrupt enable %d\n", obj, Ints_Enabled));
143 /* Stop main thread if it's not sleeping */
144 if (Sleep_Mode != SLEEP_MODE_ON)
146 SuspendThread(MainThread);
148 * People say that on SMP systems thread is not stopped immediately by SuspendThread().
149 * So we have to do our best to ensure that is is really stopped. I hope GetThreadContext()
150 * guarantees it.
152 CONTEXT_INIT_FLAGS(&MainCtx);
153 GetThreadContext(MainThread, &MainCtx);
156 /* Process interrupts if we are allowed to */
157 if (Ints_Enabled && INT_SAFE(MainCtx))
159 Supervisor = 1;
161 * We get and store the complete CPU context, but set only part of it
162 * because changing some registers causes Windows to immediately shut down
163 * our process. This can be a useful aid for future AROS debuggers.
165 DS(printf("[Task switcher] original CPU context: ****\n"));
166 DS(PRINT_CPUCONTEXT(&MainCtx));
169 * Call IRQ handlers for all pending interrupts.
170 * This means that deferred interrupts may be processed with
171 * delay, meximum of one timer period, but anyway, who told
172 * that Windows is a realtime OS?
174 Ints_Enabled = IRQVector(PendingInts, &MainCtx);
175 /* All IRQs have been processed */
176 ZeroMemory(PendingInts, sizeof(PendingInts));
178 /* If AROS is not going to sleep, set new CPU context */
179 if (Sleep_Mode == SLEEP_MODE_OFF)
181 DS(printf("[Task switcher] new CPU context: ****\n"));
182 DS(PRINT_CPUCONTEXT(&MainCtx));
183 SetThreadContext(MainThread, &MainCtx);
186 /* Leave supervisor mode. Interrupt state is already updated by IRQVector(). */
187 Supervisor = 0;
190 /* Resuming main thread if AROS is not sleeping */
191 if (Sleep_Mode == SLEEP_MODE_OFF)
193 DS(printf("[Task switcher] Resuming main thread\n"));
194 ResumeThread(MainThread);
196 else
197 /* We've entered sleep mode */
198 Sleep_Mode = SLEEP_MODE_ON;
200 return 0;
203 /* ****** Interface functions ****** */
205 void __declspec(dllexport) __aros core_raise(DWORD code, const ULONG_PTR n)
208 * This ensures that we are never preempted inside RaiseException().
209 * Upon exit from the syscall interrupt state will be restored by
210 * core_LeaveInterrupt().
212 Ints_Enabled = INT_DISABLE;
214 DTRAP(printf("[KRN] Raising exception 0x%08lX, SP 0x%p\n", code, stack));
215 RaiseException(code, 0, 1, &n);
217 /* If after RaiseException we are still here, but Sleep_Mode != 0, this likely means
218 we've just called SC_SCHEDULE, SC_SWITCH or SC_DISPATCH, and it is putting us to sleep.
219 Sleep mode will be committed as soon as timer IRQ happens */
220 while (Sleep_Mode);
223 unsigned long __declspec(dllexport) __aros StartClock(unsigned int irq, unsigned int TimerPeriod)
225 LARGE_INTEGER TimerValue;
227 TimerPeriod = 1000 / TimerPeriod;
228 TimerValue.QuadPart = -10000 * (LONGLONG)TimerPeriod;
230 return SetWaitableTimer(IntObjects[irq], &TimerValue, TimerPeriod, NULL, NULL, 0);
234 * Start up virtual machine.
235 * Initializes IRQ engine, runs virtual supervisor thread and starts up main system timer.
236 * Trap and IRQ vectors must be already set up, we don't check them against NULLs.
238 int __declspec(dllexport) __aros core_init(unsigned int TimerPeriod)
240 HANDLE ThisProcess;
241 HANDLE SwitcherThread;
242 void *MainTEB;
243 int i;
244 DWORD SwitcherId;
246 D(printf("[KRN] Setting up interrupts\n"));
247 Ints_Enabled = INT_DISABLE;
248 Supervisor = 0;
249 Sleep_Mode = SLEEP_MODE_OFF;
250 for (i = 1; i < 256; i++)
252 IntObjects[i] = NULL;
253 AllocatedInts[i] = 0;
256 /* Set up traps */
257 MainThreadId = GetCurrentThreadId();
258 #ifdef __x86_64__
259 AddVectoredExceptionHandler(TRUE, exceptionHandler);
260 #else
261 SetUnhandledExceptionFilter(exceptionHandler);
262 #endif
264 /* Set up debug I/O */
265 conin = GetStdHandle(STD_INPUT_HANDLE);
266 conout = GetStdHandle(STD_OUTPUT_HANDLE);
268 /* Statically allocate main system timer */
269 for (i = 0; i < 2; i++)
271 IntObjects[i] = CreateWaitableTimer(NULL, FALSE, NULL);
272 if (!IntObjects[i])
274 D(printf("[KRN] Failed to create timer %u\n", i));
275 return 0;
277 AllocatedInts[i] = 1;
278 PendingInts[i] = 0;
280 Ints_Num = 2;
282 ThisProcess = GetCurrentProcess();
283 if (DuplicateHandle(ThisProcess, GetCurrentThread(), ThisProcess, &MainThread, 0, TRUE, DUPLICATE_SAME_ACCESS))
285 #ifdef __x86_64__
286 #define LastErrOffset 0x68
287 #else
288 /* On 32-bit x86 we have to figure out the offset depending on Windows version */
289 OSVERSIONINFO osver = {0};
290 ULONG LastErrOffset = 0;
292 osver.dwOSVersionInfoSize = sizeof(osver);
293 GetVersionEx(&osver);
296 * LastError value is part of our context. In order to manipulate it we have to hack
297 * into Windows TEB (thread environment block).
298 * Since this structure is private, error code offset changes from version to version.
299 * The following offsets are known:
300 * - Windows 95 and 98 - 0x60
301 * - Windows Me - 0x74
302 * - Windows NT (all family, fixed at last) - 0x34
304 switch(osver.dwPlatformId)
306 case VER_PLATFORM_WIN32_WINDOWS:
307 if (osver.dwMajorVersion == 4)
309 if (osver.dwMinorVersion > 10)
310 LastErrOffset = 0x74;
311 else
312 LastErrOffset = 0x60;
314 break;
316 case VER_PLATFORM_WIN32_NT:
317 LastErrOffset = 0x34;
318 break;
321 if (!LastErrOffset)
323 printf("Unsupported Windows version %lu.%lu, platform ID %lu\n", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwPlatformId);
324 return 0;
326 #endif
328 MainTEB = NtCurrentTeb();
329 LastErrorPtr = MainTEB + LastErrOffset;
331 SwitcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TaskSwitcher, NULL, 0, &SwitcherId);
332 if (SwitcherThread)
334 SYSTEM_INFO info;
336 D(printf("[KRN] Task switcher started, ID %lu\n", SwitcherId));
338 /* Start timer 0 */
339 if (!StartClock(0, TimerPeriod))
340 return 0;
342 /* Return system page size */
343 GetSystemInfo(&info);
344 return info.dwPageSize;
346 D(else printf("[KRN] Failed to run task switcher thread\n");)
348 D(else printf("[KRN] failed to get thread handle\n");)
350 CloseHandle(IntObjects[IRQ_TIMER]);
351 return 0;
355 * The following is host-side IRQ API.
357 * It is used by virtual hadrware implemented as asynchronous host operating
358 * system threads.
362 int __declspec(dllexport) KrnAllocIRQ(void)
364 int irq;
366 for (irq = 0; irq < 256; irq++)
368 if (!AllocatedInts[irq])
370 if (!IntObjects[irq])
372 IntObjects[irq] = CreateEvent(NULL, FALSE, FALSE, NULL);
374 if (!IntObjects[irq])
375 return -1;
377 PendingInts[irq] = 0;
378 AllocatedInts[irq] = 1;
380 if (irq == Ints_Num)
381 Ints_Num++;
383 return irq;
386 return -1;
389 void __declspec(dllexport) KrnFreeIRQ(unsigned char irq)
391 AllocatedInts[irq] = 0;
393 while (!AllocatedInts[Ints_Num - 1])
394 Ints_Num--;
397 void *__declspec(dllexport) KrnGetIRQObject(unsigned char irq)
399 return IntObjects[irq];
402 unsigned long __declspec(dllexport) KrnCauseIRQ(unsigned char irq)
404 unsigned long res;
406 D(printf("[kernel IRQ] Causing IRQ %u\n", irq));
407 res = SetEvent(IntObjects[irq]);
408 D(printf("[kernel IRQ] Result: %ld\n", res));
409 return res;