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