1 /*
2  * PROJECT:     ReactOS Virtual DOS Machine
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Testing VDD for NTVDM
5  * COPYRIGHT:   Copyright 2015-2018 Hermes Belusca-Maito
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <stdio.h>
11 
12 #include <windows.h>
13 #include <vddsvc.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 
19 /* DEBUGGING HELPERS **********************************************************/
20 
21 // Enable this define to use DPRINT1 instead of MessageBox
22 // #define DBG_SILENT
23 
24 #ifdef DBG_SILENT
25 
26     #define VDD_DBG(...) \
27     do { \
28         DPRINT1(__VA_ARGS__); \
29         DbgPrint("\n");       \
30     } while(0)
31 
32 #else
33 
34     static VOID
35     VddDbgMsg(LPCSTR Format, ...)
36     {
37     #ifndef WIN2K_COMPLIANT
38         CHAR  StaticBuffer[256];
39         LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
40     #else
41         CHAR  Buffer[2048]; // Large enough. If not, increase it by hand.
42     #endif
43         size_t MsgLen;
44         va_list Parameters;
45 
46         va_start(Parameters, Format);
47 
48     #ifndef WIN2K_COMPLIANT
49         /*
50          * Retrieve the message length and if it is too long, allocate
51          * an auxiliary buffer; otherwise use the static buffer.
52          */
53         MsgLen = _vscprintf(Format, Parameters) + 1; // NULL-terminated
54         if (MsgLen > ARRAYSIZE(StaticBuffer))
55         {
56             Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MsgLen * sizeof(WCHAR));
57             if (Buffer == NULL)
58             {
59                 /* Allocation failed, use the static buffer and display a suitable error message */
60                 Buffer = StaticBuffer;
61                 Format = "DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
62                 MsgLen = strlen(Format);
63             }
64         }
65     #else
66         MsgLen = ARRAYSIZE(Buffer);
67     #endif
68 
69         /* Display the message */
70         _vsnprintf(Buffer, MsgLen, Format, Parameters);
71         MessageBoxA(NULL, Buffer, "Test VDD", MB_OK);
72 
73     #ifndef WIN2K_COMPLIANT
74         /* Free the buffer if needed */
75         if (Buffer != StaticBuffer) HeapFree(GetProcessHeap(), 0, Buffer);
76     #endif
77 
78         va_end(Parameters);
79     }
80 
81     #define VDD_DBG VddDbgMsg
82 #endif
83 
84 
85 /* GLOBALS ********************************************************************/
86 
87 HANDLE hVdd = NULL;
88 
89 
90 /* VDD I/O PORTS TESTING ******************************************************/
91 
92 /*
93  * Port hooks (serial ports) -- Each port range is for testing different port handlers.
94  */
95 #define NUM_PORTS 4
96 
97 VDD_IO_PORTRANGE PortDefs[NUM_PORTS] =
98 {
99     {0x3F8, 0x3FF},
100     {0x2F8, 0x2FF},
101     {0x3E8, 0x3EF},
102     {0x2E8, 0x2EF}
103 };
104 
105 VOID
106 WINAPI
107 PortInB(IN  USHORT Port,
108         OUT PUCHAR Data)
109 {
110     *Data = 0;
111     VDD_DBG("0x%08x (BYTE 0x%02x) <-- Port 0x%04x", Data, *Data, Port);
112 }
113 
114 VOID
115 WINAPI
116 PortOutB(IN USHORT Port,
117          IN UCHAR  Data)
118 {
119     VDD_DBG("(BYTE 0x%02x) --> Port 0x%04x", Data, Port);
120 }
121 
122 VOID
123 WINAPI
124 PortInW(IN  USHORT  Port,
125         OUT PUSHORT Data)
126 {
127     *Data = 0;
128     VDD_DBG("0x%08x (WORD 0x%04x) <-- Port 0x%04x", Data, *Data, Port);
129 }
130 
131 VOID
132 WINAPI
133 PortOutW(IN USHORT Port,
134          IN USHORT Data)
135 {
136     VDD_DBG("(WORD 0x%04x) --> Port 0x%04x", Data, Port);
137 }
138 
139 
140 VOID
141 WINAPI
142 PortInsB(IN  USHORT Port,
143          OUT PUCHAR Data,
144          IN  USHORT Count)
145 {
146     VDD_DBG("0x%08x (BYTESTR[%u]) <-- Port 0x%04x", Data, Count, Port);
147     while (Count--) *Data++ = 0;
148 }
149 
150 VOID
151 WINAPI
152 PortOutsB(IN USHORT Port,
153           IN PUCHAR Data,
154           IN USHORT Count)
155 {
156     VDD_DBG("0x%08x (BYTESTR[%u]) --> Port 0x%04x", Data, Count, Port);
157 }
158 
159 VOID
160 WINAPI
161 PortInsW(IN  USHORT  Port,
162          OUT PUSHORT Data,
163          IN  USHORT  Count)
164 {
165     VDD_DBG("0x%08x (WORDSTR[%u]) <-- Port 0x%04x", Data, Count, Port);
166     while (Count--) *Data++ = 0;
167 }
168 
169 VOID
170 WINAPI
171 PortOutsW(IN USHORT  Port,
172           IN PUSHORT Data,
173           IN USHORT  Count)
174 {
175     VDD_DBG("0x%08x (WORDSTR[%u]) --> Port 0x%04x", Data, Count, Port);
176 }
177 
178 
179 VDD_IO_HANDLERS PortHandlers[NUM_PORTS] =
180 {
181     {PortInB, NULL   , NULL    , NULL    , PortOutB, NULL    , NULL     , NULL     },
182     {PortInB, PortInW, NULL    , NULL    , PortOutB, PortOutW, NULL     , NULL     },
183     {PortInB, NULL   , PortInsB, NULL    , PortOutB, NULL    , PortOutsB, NULL     },
184     {PortInB, NULL   , NULL    , PortInsW, PortOutB, NULL    , NULL     , PortOutsW},
185 };
186 
187 
188 /* VDD MEMORY HOOKS TESTING ***************************************************/
189 
190 /*
191  * Everything should be page-rounded.
192  */
193 
194 #ifndef PAGE_SIZE
195 #define PAGE_SIZE   0x1000
196 #endif
197 
198 #ifndef PAGE_ROUND_DOWN
199 #define PAGE_ROUND_DOWN(x)  \
200     ( ((ULONG_PTR)(x)) & (~(PAGE_SIZE-1)) )
201 #endif
202 
203 #ifndef PAGE_ROUND_UP
204 #define PAGE_ROUND_UP(x)    \
205     ( (((ULONG_PTR)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) )
206 #endif
207 
208 #define MEM_SEG_START   0x0000
209 #define MEM_SIZE        PAGE_SIZE
210 
211 USHORT HookedSegment = 0x0000;
212 ULONG  HookedOffset  = 0x0000;
213 PVOID  HookedAddress = NULL;
214 
215 VOID
216 WINAPI
217 MemoryHandler(IN PVOID FaultAddress,
218               IN ULONG RWMode)
219 {
220     BOOLEAN Success = FALSE;
221 
222     VDD_DBG("MemoryHandler(0x%08x, %s)", FaultAddress, (RWMode == 1) ? "Write" : "Read");
223     // VDDTerminateVDM();
224 
225     Success = VDDAllocMem(hVdd, HookedAddress, MEM_SIZE);
226     if (!Success) VDD_DBG("Unable to allocate memory");
227 }
228 
229 PVOID
230 FindHookableMemory(IN  USHORT  StartSegment,
231                    IN  ULONG   StartOffset,
232                    OUT PUSHORT HookedSegment,
233                    OUT PULONG  HookedOffset)
234 {
235     BOOLEAN Success;
236     PVOID  PhysMemStart = NULL;
237     USHORT Segment = StartSegment;
238     ULONG  Offset  = PAGE_ROUND_DOWN(StartOffset);
239 
240     *HookedSegment = 0x0000;
241     *HookedOffset  = 0x0000;
242 
243     while (Segment <= 0xF000)
244     {
245         // PhysMemStart = GetVDMPointer(GetVDMAddress(Segment, Offset), MEM_SIZE, (getMSW() & MSW_PE));
246         PhysMemStart = VdmMapFlat(Segment, Offset, getMODE());
247 
248         /* Try to hook this memory area... */
249         Success = VDDInstallMemoryHook(hVdd, PhysMemStart, MEM_SIZE, MemoryHandler);
250         if (!Success)
251         {
252             /* ... it didn't work. Free PhysMemStart, increase segment/offset and try again. */
253             DPRINT1("%04lX:%08lX hooking failed, continue...\n", Segment, Offset);
254 
255             VdmUnmapFlat(Segment, Offset, PhysMemStart, getMODE());
256             // FreeVDMPointer(GetVDMAddress(Segment, Offset), MEM_SIZE, PhysMemStart, (getMSW() & MSW_PE));
257             PhysMemStart = NULL;
258 
259             Offset += MEM_SIZE;
260             if (Offset + MEM_SIZE > 0xFFFF)
261             {
262                 Segment += 0x1000;
263                 Offset = 0x0000;
264             }
265         }
266         else
267         {
268             /* ... it worked. We'll free PhysMemStart later on. */
269             DPRINT1("%04lX:%08lX hooking succeeded!\n", Segment, Offset);
270             break;
271         }
272     }
273 
274     if (PhysMemStart)
275     {
276         VDD_DBG("We hooked at %04lX:%08lX (0x%p)", Segment, Offset, PhysMemStart);
277         *HookedSegment = Segment;
278         *HookedOffset  = Offset;
279     }
280     else
281     {
282         VDD_DBG("Hooking attempt failed!");
283     }
284 
285     return PhysMemStart;
286 }
287 
288 
289 /* VDD USER HOOKS TESTING *****************************************************/
290 
291 VOID
292 WINAPI
293 Create1Handler(USHORT DosPDB)
294 {
295     VDD_DBG("Create1Handler(0x%04x)", DosPDB);
296 }
297 
298 VOID
299 WINAPI
300 Create2Handler(USHORT DosPDB)
301 {
302     VDD_DBG("Create2Handler(0x%04x)", DosPDB);
303 }
304 
305 VOID
306 WINAPI
307 Terminate1Handler(USHORT DosPDB)
308 {
309     VDD_DBG("Terminate1Handler(0x%04x)", DosPDB);
310 }
311 
312 VOID
313 WINAPI
314 Terminate2Handler(USHORT DosPDB)
315 {
316     VDD_DBG("Terminate2Handler(0x%04x)", DosPDB);
317 }
318 
319 VOID
320 WINAPI
321 Block1Handler(VOID)
322 {
323     VDD_DBG("Block1Handler");
324 }
325 
326 VOID
327 WINAPI
328 Block2Handler(VOID)
329 {
330     VDD_DBG("Block2Handler");
331 }
332 
333 VOID
334 WINAPI
335 Resume1Handler(VOID)
336 {
337     VDD_DBG("Resume1Handler");
338 }
339 
340 VOID
341 WINAPI
342 Resume2Handler(VOID)
343 {
344     VDD_DBG("Resume2Handler");
345 }
346 
347 
348 /* VDD INITIALIZATION AND REGISTRATION ****************************************/
349 
350 VOID
351 WINAPI
352 TestVDDRegister(VOID)
353 {
354     VDD_DBG("TestVDDRegister");
355 
356     /* Clear the Carry Flag: success */
357     setCF(0);
358 }
359 
360 VOID
361 WINAPI
362 TestVDDUnRegister(VOID)
363 {
364     VDD_DBG("TestVDDUnRegister");
365 
366     /* Clear the Carry Flag: success */
367     setCF(0);
368 }
369 
370 VOID
371 WINAPI
372 TestVDDDispatch(VOID)
373 {
374     VDD_DBG("TestVDDDispatch");
375 
376     /* Clear the Carry Flag: success */
377     setCF(0);
378 }
379 
380 BOOLEAN
381 RegisterVDD(BOOLEAN Register)
382 {
383     BOOLEAN Success = FALSE;
384 
385     if (Register)
386     {
387         /* Hook some IO ports */
388         VDD_DBG("VDDInstallIOHook");
389         Success = VDDInstallIOHook(hVdd, NUM_PORTS, PortDefs, PortHandlers);
390         if (!Success)
391         {
392             VDD_DBG("Unable to hook IO ports, terminate...");
393             VDDTerminateVDM();
394         }
395 
396         /* Add a memory handler */
397         VDD_DBG("FindHookableMemory");
398         HookedAddress = FindHookableMemory(MEM_SEG_START, 0x0000,
399                                            &HookedSegment, &HookedOffset);
400         if (HookedAddress == NULL)
401         {
402             VDD_DBG("Unable to install memory handler, terminate...");
403             VDDTerminateVDM();
404         }
405 
406         /* Add some user hooks -- Test order of initialization and calling */
407         VDD_DBG("VDDInstallUserHook (1)");
408         Success = VDDInstallUserHook(hVdd,
409                                      Create1Handler,
410                                      Terminate1Handler,
411                                      Block1Handler,
412                                      Resume1Handler);
413         if (!Success)
414         {
415             VDD_DBG("Unable to install user hooks (1)...");
416         }
417 
418         VDD_DBG("VDDInstallUserHook (2)");
419         Success = VDDInstallUserHook(hVdd,
420                                      Create2Handler,
421                                      Terminate2Handler,
422                                      Block2Handler,
423                                      Resume2Handler);
424         if (!Success)
425         {
426             VDD_DBG("Unable to install user hooks (2)...");
427         }
428 
429         /* We have finished! */
430         VDD_DBG("Initialization finished!");
431     }
432     else
433     {
434         /* Remove the user hooks */
435         VDD_DBG("VDDDeInstallUserHook (1)");
436         Success = VDDDeInstallUserHook(hVdd);
437         if (!Success) VDD_DBG("Unable to uninstall user hooks (1)");
438 
439         // TODO: See which hooks are still existing there...
440 
441         VDD_DBG("VDDDeInstallUserHook (2)");
442         Success = VDDDeInstallUserHook(hVdd);
443         if (!Success) VDD_DBG("Unable to uninstall user hooks (2)");
444 
445         VDD_DBG("VDDDeInstallUserHook (3)");
446         Success = VDDDeInstallUserHook(hVdd);
447         if (!Success) VDD_DBG("EXPECTED ERROR: Unable to uninstall user hooks (3)");
448         else          VDD_DBG("UNEXPECTED ERROR: Uninstalling user hooks (3) succeeded?!");
449 
450         /* Uninstall the memory handler */
451         Success = VDDFreeMem(hVdd, HookedAddress, MEM_SIZE);
452         if (!Success) VDD_DBG("Unable to free memory");
453 
454         VDD_DBG("VDDDeInstallMemoryHook");
455         Success = VDDDeInstallMemoryHook(hVdd, HookedAddress, MEM_SIZE);
456         if (!Success) VDD_DBG("Memory handler uninstall failed");
457 
458         VDD_DBG("VdmUnmapFlat");
459         Success = VdmUnmapFlat(HookedSegment, HookedOffset, HookedAddress, getMODE());
460         // FreeVDMPointer(GetVDMAddress(HookedSegment, HookedOffset), MEM_SIZE, HookedAddress, (getMSW() & MSW_PE));
461         if (!Success) VDD_DBG("VdmUnmapFlat failed!");
462 
463         /* Deregister the hooked IO ports */
464         VDD_DBG("VDDDeInstallIOHook");
465         VDDDeInstallIOHook(hVdd, NUM_PORTS, PortDefs);
466 
467         VDD_DBG("Cleanup finished!");
468         Success = TRUE;
469     }
470 
471     return Success;
472 }
473 
474 BOOL
475 WINAPI // VDDInitialize
476 DllMain(IN HINSTANCE hInstanceDll,
477         IN DWORD dwReason,
478         IN LPVOID lpReserved)
479 {
480     BOOLEAN Success;
481 
482     UNREFERENCED_PARAMETER(lpReserved);
483 
484     switch (dwReason)
485     {
486         case DLL_PROCESS_ATTACH:
487         {
488             VDD_DBG("DLL_PROCESS_ATTACH");
489 
490             /* Save our global VDD handle */
491             hVdd = hInstanceDll;
492 
493             /* Register VDD */
494             Success = RegisterVDD(TRUE);
495             if (!Success) VDD_DBG("Failed to register the VDD...");
496 
497             break;
498         }
499 
500         case DLL_PROCESS_DETACH:
501         {
502             VDD_DBG("DLL_PROCESS_DETACH");
503 
504             /* Unregister VDD */
505             Success = RegisterVDD(FALSE);
506             if (!Success) VDD_DBG("Failed to unregister the VDD...");
507 
508             break;
509         }
510 
511         case DLL_THREAD_ATTACH:
512         case DLL_THREAD_DETACH:
513         default:
514             break;
515     }
516 
517     return TRUE;
518 }
519 
520 /* EOF */
521