2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
10 /* PRINT_CPUCONTEXT() macro uses bug() because it can be useful on AROS side too. */
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
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 */
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
;
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. */
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
)));
98 if (--Supervisor
== 0)
100 /* If we are leaving to user mode, we may need to enable interrupts */
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
;
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))
136 #define INT_SAFE(ctx) TRUE
139 DWORD WINAPI
TaskSwitcher()
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()
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
))
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(). */
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
);
197 /* We've entered sleep mode. Main thread is kept suspended. */
198 Sleep_Mode
= SLEEP_MODE_ON
;
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
);
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 */
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
)
253 HANDLE SwitcherThread
;
258 D(printf("[KRN] Setting up interrupts\n"));
259 Ints_Enabled
= INT_DISABLE
;
261 Sleep_Mode
= SLEEP_MODE_OFF
;
262 for (i
= 1; i
< 256; i
++)
264 IntObjects
[i
] = NULL
;
265 AllocatedInts
[i
] = 0;
269 MainThreadId
= GetCurrentThreadId();
271 AddVectoredExceptionHandler(TRUE
, exceptionHandler
);
273 SetUnhandledExceptionFilter(exceptionHandler
);
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
);
286 D(printf("[KRN] Failed to create timer %u\n", i
));
289 AllocatedInts
[i
] = 1;
294 ThisProcess
= GetCurrentProcess();
295 if (DuplicateHandle(ThisProcess
, GetCurrentThread(), ThisProcess
, &MainThread
, 0, TRUE
, DUPLICATE_SAME_ACCESS
))
298 #define LastErrOffset 0x68
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;
324 LastErrOffset
= 0x60;
328 case VER_PLATFORM_WIN32_NT
:
329 LastErrOffset
= 0x34;
335 printf("Unsupported Windows version %lu.%lu, platform ID %lu\n", osver
.dwMajorVersion
, osver
.dwMinorVersion
, osver
.dwPlatformId
);
340 MainTEB
= NtCurrentTeb();
341 LastErrorPtr
= MainTEB
+ LastErrOffset
;
343 SwitcherThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)TaskSwitcher
, NULL
, 0, &SwitcherId
);
348 D(printf("[KRN] Task switcher started, ID %lu\n", SwitcherId
));
351 if (!StartClock(0, TimerPeriod
))
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
]);
367 * The following is host-side IRQ API.
369 * It is used by virtual hadrware implemented as asynchronous host operating
374 int __declspec(dllexport
) __aros
KrnAllocIRQ(void)
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
])
389 PendingInts
[irq
] = 0;
390 AllocatedInts
[irq
] = 1;
402 void __declspec(dllexport
) __aros
KrnFreeIRQ(unsigned char irq
)
404 AllocatedInts
[irq
] = 0;
406 while (!AllocatedInts
[Ints_Num
- 1])
410 void *__declspec(dllexport
) __aros
KrnGetIRQObject(unsigned char irq
)
412 return IntObjects
[irq
];
415 unsigned long __declspec(dllexport
) __aros
KrnCauseIRQ(unsigned char irq
)
419 D(printf("[kernel IRQ] Causing IRQ %u\n", irq
));
420 res
= SetEvent(IntObjects
[irq
]);
421 D(printf("[kernel IRQ] Result: %ld\n", res
));