2 * This little utility is specific for Windows-hosted port. It allows you to enter
3 * SAD even on completely frozen system.
4 * It is written specifically to facilitate finding long-standing lockup bug.
5 * Additionally it can serve as a short example showing how to interact with Windows
7 * The program installs a hook on Windows console, which generates a NMI whenever the
8 * user presses Ctrl-Break in the console.
11 #include <aros/debug.h>
12 #include <proto/exec.h>
13 #include <proto/hostlib.h>
14 #include <proto/kernel.h>
19 #define __stdcall __attribute__((ms_abi))
21 #define __stdcall __attribute__((stdcall))
24 #define WAIT_TIMEOUT 0x00000102
26 struct KernelInterface
28 int (*KrnAllocIRQ
)(void);
29 void (*KrnFreeIRQ
)(unsigned char irq
);
30 void (*KrnCauseIRQ
)(unsigned char irq
);
35 struct Kernel32Interface
37 ULONG
__stdcall (*SetConsoleCtrlHandler
)(void *HandlerRoutine
, ULONG Add
);
38 APTR
__stdcall (*CreateEvent
)(void *lpEventAttributes
, ULONG bManualReset
, ULONG bInitialState
, const char *lpName
);
39 ULONG
__stdcall (*CloseHandle
)(APTR hObject
);
40 ULONG
__stdcall (*SetEvent
)(APTR hEvent
);
41 ULONG
__stdcall (*WaitForSingleObject
)(void *hHandle
, ULONG dwMilliseconds
);
46 static struct KernelInterface
*kernelIf
;
47 static struct Kernel32Interface
*winIf
;
49 static const char *symbols
[] =
59 static const char *kern32_symbols
[] =
61 "SetConsoleCtrlHandler",
65 "WaitForSingleObject",
69 static void DebugInterrupt(void *a1
, void *a2
)
72 * Acknowledge the interrupt and call SAD.
73 * Acknowledgement mechanism is needed for very heavy lockups, when interrupts
74 * become inresponsive. Our console hook will wait for 3 seconds, and if this
75 * timeout expores, we consider that our virtualized CPU is dead, and run SAD
76 * right from within console hook. Doing this under normal circumstances results
77 * in crash because of asynchronous re-entering user-mode code.
79 winIf
->SetEvent(intAck
);
84 * Be careful! This function is executed by Windows, in console's context!
85 * So no AROS calls here!!!
87 static ULONG __stdcall
ConsoleHook(ULONG event
)
89 if (event
== 1) /* CTRL_BREAK_EVENT */
93 kernelIf
->KrnCauseIRQ(debugIRQ
);
94 status
= winIf
->WaitForSingleObject(intAck
, 3000);
96 if (status
!= WAIT_TIMEOUT
)
100 * This will set supervisor flag. After this it's OK to call kprintf().
101 * We add 1 instead of just setting 1 in order to be able to know
102 * its original value.
103 * Note that here we are running outside of both AROS threads
104 * (supervisor and user). It's OK only because if we are here, interrupt
105 * thread went defunct, and this is the only thread running.
107 *kernelIf
->Supervisor
+= 1;
109 kprintf("Timeout waiting for NMI ACK, entering emergency SAD\n");
115 int __nocommandline
= 1;
122 APTR KernelBase
, HostLibBase
;
125 KernelBase
= OpenResource("kernel.resource");
128 printf("Failed to open kernel.resource ???\n");
132 HostLibBase
= OpenResource("hostlib.resource");
135 printf("hostlib.resource not found, likely native system\n");
139 kern32
= HostLib_Open("kernel32.dll", &errStr
);
142 printf("kernel32.dll: %s\n", errStr
);
143 HostLib_FreeErrorStr(errStr
);
147 winIf
= (struct Kernel32Interface
*)HostLib_GetInterface(kern32
, kern32_symbols
, &unres
);
148 if ((!winIf
) || unres
)
150 printf("Failed to obtain kernel.dll interface, %u symbols unresolved\n", unres
);
153 HostLib_DropInterface((void **)winIf
);
154 HostLib_Close(kern32
, NULL
);
158 kernel
= HostLib_Open("Libs\\Host\\kernel.dll", &errStr
);
161 printf("kernel.dll: %s\n", errStr
);
162 HostLib_FreeErrorStr(errStr
);
163 HostLib_DropInterface((void **)winIf
);
164 HostLib_Close(kern32
, NULL
);
168 kernelIf
= (struct KernelInterface
*)HostLib_GetInterface(kernel
, symbols
, &unres
);
169 if ((!kernelIf
) || unres
)
171 printf("Failed to obtain kernel.dll interface, %u symbols unresolved\n", unres
);
174 HostLib_DropInterface((void **)kernelIf
);
175 HostLib_DropInterface((void **)winIf
);
176 HostLib_Close(kernel
, NULL
);
177 HostLib_Close(kern32
, NULL
);
181 debugIRQ
= kernelIf
->KrnAllocIRQ();
184 printf("Failed to create NMI interrupt!\n");
185 HostLib_DropInterface((void **)kernelIf
);
186 HostLib_DropInterface((void **)winIf
);
187 HostLib_Close(kernel
, NULL
);
188 HostLib_Close(kern32
, NULL
);
193 intHandle
= KrnAddIRQHandler(debugIRQ
, DebugInterrupt
, NULL
, NULL
);
196 printf("Failed to add IRQ handler\n");
198 HostLib_DropInterface((void **)kernelIf
);
199 HostLib_DropInterface((void **)winIf
);
200 HostLib_Close(kernel
, NULL
);
201 HostLib_Close(kern32
, NULL
);
206 intAck
= winIf
->CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
208 unres
= winIf
->SetConsoleCtrlHandler(ConsoleHook
, TRUE
);
213 /* Yes, NonMaskable IRQ exists solely for us dirty hackers */
214 *kernelIf
->NonMaskableIRQ
= debugIRQ
; /* This turns IRQ into NMI */
216 printf("Debug NMI#%d installed, press Ctrl-C to uninstall\n", debugIRQ
);
217 Wait(SIGBREAKF_CTRL_C
);
219 printf("Done, exiting\n");
221 /* Disable NMI, or bad things may happen if someone reuses this IRQ number */
222 *kernelIf
->NonMaskableIRQ
= -1;
225 printf("Failed to install console hook\n");
229 winIf
->SetConsoleCtrlHandler(ConsoleHook
, FALSE
);
230 winIf
->CloseHandle(intAck
);
233 KrnRemIRQHandler(intHandle
);
234 kernelIf
->KrnFreeIRQ(debugIRQ
);
235 HostLib_DropInterface((void **)kernelIf
);
236 HostLib_DropInterface((void **)winIf
);
237 HostLib_Close(kernel
, NULL
);
238 HostLib_Close(kern32
, NULL
);