2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
6 #define __CRT_INLINE __attribute__ ((__always_inline__))
12 /* PRINT_CPUCONTEXT() macro uses bug() because it can be useful on AROS side too. */
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
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 */
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
;
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. */
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
)));
100 if (--Supervisor
== 0)
102 /* If we are leaving to user mode, we may need to enable interrupts */
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
;
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))
138 #define INT_SAFE(ctx) TRUE
141 DWORD WINAPI
TaskSwitcher()
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()
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
))
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(). */
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
);
199 /* We've entered sleep mode. Main thread is kept suspended. */
200 Sleep_Mode
= SLEEP_MODE_ON
;
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
);
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 */
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
)
255 HANDLE SwitcherThread
;
260 D(printf("[KRN] Setting up interrupts\n"));
261 Ints_Enabled
= INT_DISABLE
;
263 Sleep_Mode
= SLEEP_MODE_OFF
;
264 for (i
= 1; i
< 256; i
++)
266 IntObjects
[i
] = NULL
;
267 AllocatedInts
[i
] = 0;
271 MainThreadId
= GetCurrentThreadId();
273 AddVectoredExceptionHandler(TRUE
, exceptionHandler
);
275 SetUnhandledExceptionFilter(exceptionHandler
);
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
);
288 D(printf("[KRN] Failed to create timer %u\n", i
));
291 AllocatedInts
[i
] = 1;
296 ThisProcess
= GetCurrentProcess();
297 if (DuplicateHandle(ThisProcess
, GetCurrentThread(), ThisProcess
, &MainThread
, 0, TRUE
, DUPLICATE_SAME_ACCESS
))
300 #define LastErrOffset 0x68
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;
326 LastErrOffset
= 0x60;
330 case VER_PLATFORM_WIN32_NT
:
331 LastErrOffset
= 0x34;
337 printf("Unsupported Windows version %lu.%lu, platform ID %lu\n", osver
.dwMajorVersion
, osver
.dwMinorVersion
, osver
.dwPlatformId
);
343 MainTEB
= NtCurrentTeb();
345 LastErrorPtr
= MainTEB
+ LastErrOffset
;
347 SwitcherThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)TaskSwitcher
, NULL
, 0, &SwitcherId
);
352 D(printf("[KRN] Task switcher started, ID %lu\n", SwitcherId
));
355 if (!StartClock(0, TimerPeriod
))
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
]);
371 * The following is host-side IRQ API.
373 * It is used by virtual hadrware implemented as asynchronous host operating
378 int __declspec(dllexport
) __aros
KrnAllocSystemIRQ(void)
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
])
393 PendingInts
[irq
] = 0;
394 AllocatedInts
[irq
] = 1;
406 void __declspec(dllexport
) __aros
KrnFreeSystemIRQ(unsigned char irq
)
408 AllocatedInts
[irq
] = 0;
410 while (!AllocatedInts
[Ints_Num
- 1])
414 void *__declspec(dllexport
) __aros
KrnGetSystemIRQObject(unsigned char irq
)
416 return IntObjects
[irq
];
419 unsigned long __declspec(dllexport
) __aros
KrnCauseSystemIRQ(unsigned char irq
)
423 D(printf("[kernel IRQ] Causing IRQ %u\n", irq
));
424 res
= SetEvent(IntObjects
[irq
]);
425 D(printf("[kernel IRQ] Result: %ld\n", res
));