1 /*
2  * PROJECT:     ReactOS kernel-mode tests
3  * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4  * PURPOSE:     Kernel-Mode Test Suite user-mode support routines
5  * COPYRIGHT:   Copyright 2011-2020 Thomas Faber <thomas.faber@reactos.org>
6  *              Copyright 2013 Nikolay Borisov <nib9@aber.ac.uk>
7  *              Copyright 2018 Serge Gautherie <reactos-git_serge_171003@gautherie.fr>
8  */
9 
10 #include <kmt_test.h>
11 
12 #include "kmtest.h"
13 #include <kmt_public.h>
14 
15 #include <assert.h>
16 #include <debug.h>
17 
18 extern HANDLE KmtestHandle;
19 
20 /**
21  * @name KmtUserCallbackThread
22  *
23  * Thread routine which awaits callback requests from kernel-mode
24  *
25  * @return Win32 error code
26  */
27 DWORD
28 WINAPI
29 KmtUserCallbackThread(
30     PVOID Parameter)
31 {
32     DWORD Error = ERROR_SUCCESS;
33     /* TODO: RequestPacket? */
34     KMT_CALLBACK_REQUEST_PACKET RequestPacket;
35     KMT_RESPONSE Response;
36     DWORD BytesReturned;
37     HANDLE LocalKmtHandle;
38 
39     UNREFERENCED_PARAMETER(Parameter);
40 
41     /* concurrent IoCtls on the same (non-overlapped) handle aren't possible,
42      * so open a separate one.
43      * For more info http://www.osronline.com/showthread.cfm?link=230782 */
44     LocalKmtHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
45     if (LocalKmtHandle == INVALID_HANDLE_VALUE)
46         error_goto(Error, cleanup);
47 
48     while (1)
49     {
50         if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_AWAIT_REQ, NULL, 0, &RequestPacket, sizeof(RequestPacket), &BytesReturned, NULL))
51             error_goto(Error, cleanup);
52         ASSERT(BytesReturned == sizeof(RequestPacket));
53 
54         switch (RequestPacket.OperationClass)
55         {
56             case QueryVirtualMemory:
57             {
58                 SIZE_T InfoBufferSize = VirtualQuery(RequestPacket.Parameters, &Response.MemInfo, sizeof(Response.MemInfo));
59                 /* FIXME: an error is a valid result. That should go as a response to kernel mode instead of terminating the thread */
60                 if (InfoBufferSize == 0)
61                     error_goto(Error, cleanup);
62 
63                 if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE, &RequestPacket.RequestId, sizeof(RequestPacket.RequestId), &Response, sizeof(Response), &BytesReturned, NULL))
64                     error_goto(Error, cleanup);
65                 ASSERT(BytesReturned == 0);
66 
67                 break;
68             }
69             default:
70                 DPRINT1("Unrecognized user-mode callback request\n");
71                 break;
72         }
73     }
74 
75 cleanup:
76     if (LocalKmtHandle != INVALID_HANDLE_VALUE)
77         CloseHandle(LocalKmtHandle);
78 
79     DPRINT("Callback handler dying! Error code %lu", Error);
80     return Error;
81 }
82 
83 
84 /**
85  * @name KmtRunKernelTest
86  *
87  * Run the specified kernel-mode test part
88  *
89  * @param TestName
90  *        Name of the test to run
91  *
92  * @return Win32 error code as returned by DeviceIoControl
93  */
94 DWORD
95 KmtRunKernelTest(
96     IN PCSTR TestName)
97 {
98     HANDLE CallbackThread;
99     DWORD Error = ERROR_SUCCESS;
100     DWORD BytesRead;
101 
102     CallbackThread = CreateThread(NULL, 0, KmtUserCallbackThread, NULL, 0, NULL);
103 
104     if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, (DWORD)strlen(TestName), NULL, 0, &BytesRead, NULL))
105         error(Error);
106 
107     if (CallbackThread != NULL)
108          CloseHandle(CallbackThread);
109 
110     return Error;
111 }
112 
113 static WCHAR TestServiceName[MAX_PATH];
114 static SC_HANDLE TestServiceHandle;
115 static HANDLE TestDeviceHandle;
116 
117 /**
118  * @name KmtLoadDriver
119  *
120  * Load the specified special-purpose driver (create/start the service)
121  *
122  * @param ServiceName
123  *        Name of the driver service (Kmtest- prefix will be added automatically)
124  * @param RestartIfRunning
125  *        TRUE to stop and restart the service if it is already running
126  */
127 DWORD
128 KmtLoadDriver(
129     IN PCWSTR ServiceName,
130     IN BOOLEAN RestartIfRunning)
131 {
132     WCHAR ServicePath[MAX_PATH];
133 
134     StringCbCopyW(ServicePath, sizeof(ServicePath), ServiceName);
135     StringCbCatW(ServicePath, sizeof(ServicePath), L"_drv.sys");
136 
137     StringCbCopyW(TestServiceName, sizeof(TestServiceName), L"Kmtest-");
138     StringCbCatW(TestServiceName, sizeof(TestServiceName), ServiceName);
139 
140     return KmtCreateAndStartService(TestServiceName, ServicePath, NULL, &TestServiceHandle, RestartIfRunning);
141 }
142 
143 /**
144  * @name KmtUnloadDriverKeepService
145  *
146  * Unload special-purpose driver (stop the service only)
147  */
148 VOID
149 KmtUnloadDriverKeepService(VOID)
150 {
151     DWORD Error;
152 
153     Error = KmtStopService(TestServiceName, &TestServiceHandle);
154 
155     if (Error)
156     {
157         fprintf(stderr, "Failed to stop %ls service with error 0x%lx\n", TestServiceName, Error);
158     }
159 }
160 
161 /**
162  * @name KmtUnloadDriver
163  *
164  * Unload special-purpose driver (stop and delete the service)
165  */
166 VOID
167 KmtUnloadDriver(VOID)
168 {
169     DWORD Error;
170 
171     Error = KmtStopService(TestServiceName, &TestServiceHandle);
172 
173     if (Error)
174     {
175         fprintf(stderr, "Failed to stop %ls service with error 0x%lx\n", TestServiceName, Error);
176     }
177 
178     Error = KmtDeleteService(TestServiceName, &TestServiceHandle);
179 
180     if (Error)
181     {
182         fprintf(stderr, "Failed to delete %ls service with error 0x%lx\n", TestServiceName, Error);
183     }
184 }
185 
186 /**
187  * @name KmtOpenDriver
188  *
189  * Open special-purpose driver (acquire a device handle)
190  */
191 DWORD
192 KmtOpenDriver(VOID)
193 {
194     DWORD Error = ERROR_SUCCESS;
195     WCHAR DevicePath[MAX_PATH];
196 
197     StringCbCopyW(DevicePath, sizeof(DevicePath), L"\\\\.\\Global\\GLOBALROOT\\Device\\");
198     StringCbCatW(DevicePath, sizeof(DevicePath), TestServiceName);
199 
200     TestDeviceHandle = CreateFile(DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
201     if (TestDeviceHandle == INVALID_HANDLE_VALUE)
202         error(Error);
203 
204     return Error;
205 }
206 
207 /**
208  * @name KmtOpenDriver
209  *
210  * Load and open special-purpose driver (acquire a device handle)
211  */
212 DWORD
213 KmtLoadAndOpenDriver(
214     IN PCWSTR ServiceName,
215     IN BOOLEAN RestartIfRunning)
216 {
217     DWORD Error;
218 
219     Error = KmtLoadDriver(ServiceName, RestartIfRunning);
220     if (Error)
221         return Error;
222 
223     Error = KmtOpenDriver();
224     if (Error)
225         return Error;
226 
227     return ERROR_SUCCESS;
228 }
229 
230 /**
231  * @name KmtCloseDriver
232  *
233  * Close special-purpose driver (close device handle)
234  */
235 VOID
236 KmtCloseDriver(VOID)
237 {
238     DWORD Error = ERROR_SUCCESS;
239 
240     if (TestDeviceHandle && !CloseHandle(TestDeviceHandle))
241         error(Error);
242 
243     if (Error)
244     {
245         DPRINT1("CloseHandle failed: 0x%lx\n", Error);
246     }
247 }
248 
249 /**
250  * @name KmtSendToDriver
251  *
252  * Send an I/O control message with no arguments to the driver opened with KmtOpenDriver
253  *
254  * @param ControlCode
255  *
256  * @return Win32 error code as returned by DeviceIoControl
257  */
258 DWORD
259 KmtSendToDriver(
260     IN DWORD ControlCode)
261 {
262     DWORD BytesRead;
263 
264     assert(ControlCode < 0x400);
265 
266     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), NULL, 0, NULL, 0, &BytesRead, NULL))
267         return GetLastError();
268 
269     return ERROR_SUCCESS;
270 }
271 
272 /**
273  * @name KmtSendStringToDriver
274  *
275  * Send an I/O control message with a string argument to the driver opened with KmtOpenDriver
276  *
277  * @param ControlCode
278  * @param String
279  *
280  * @return Win32 error code as returned by DeviceIoControl
281  */
282 DWORD
283 KmtSendStringToDriver(
284     IN DWORD ControlCode,
285     IN PCSTR String)
286 {
287     DWORD BytesRead;
288 
289     assert(ControlCode < 0x400);
290 
291     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, (DWORD)strlen(String), NULL, 0, &BytesRead, NULL))
292         return GetLastError();
293 
294     return ERROR_SUCCESS;
295 }
296 
297 /**
298  * @name KmtSendWStringToDriver
299  *
300  * Send an I/O control message with a wide string argument to the driver opened with KmtOpenDriver
301  *
302  * @param ControlCode
303  * @param String
304  *
305  * @return Win32 error code as returned by DeviceIoControl
306  */
307 DWORD
308 KmtSendWStringToDriver(
309     IN DWORD ControlCode,
310     IN PCWSTR String)
311 {
312     DWORD BytesRead;
313 
314     assert(ControlCode < 0x400);
315 
316     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, (DWORD)wcslen(String) * sizeof(WCHAR), NULL, 0, &BytesRead, NULL))
317         return GetLastError();
318 
319     return ERROR_SUCCESS;
320 }
321 
322 /**
323  * @name KmtSendUlongToDriver
324  *
325  * Send an I/O control message with an integer argument to the driver opened with KmtOpenDriver
326  *
327  * @param ControlCode
328  * @param Value
329  *
330  * @return Win32 error code as returned by DeviceIoControl
331  */
332 DWORD
333 KmtSendUlongToDriver(
334     IN DWORD ControlCode,
335     IN DWORD Value)
336 {
337     DWORD BytesRead;
338 
339     assert(ControlCode < 0x400);
340 
341     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), &Value, sizeof(Value), NULL, 0, &BytesRead, NULL))
342         return GetLastError();
343 
344     return ERROR_SUCCESS;
345 }
346 
347 /**
348  * @name KmtSendBufferToDriver
349  *
350  * Send an I/O control message with the specified arguments to the driver opened with KmtOpenDriver
351  *
352  * @param ControlCode
353  * @param Buffer
354  * @param InLength
355  * @param OutLength
356  *
357  * @return Win32 error code as returned by DeviceIoControl
358  */
359 DWORD
360 KmtSendBufferToDriver(
361     IN DWORD ControlCode,
362     IN OUT PVOID Buffer OPTIONAL,
363     IN DWORD InLength,
364     IN OUT PDWORD OutLength)
365 {
366     assert(OutLength);
367     assert(Buffer || (!InLength && !*OutLength));
368     assert(ControlCode < 0x400);
369 
370     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), Buffer, InLength, Buffer, *OutLength, OutLength, NULL))
371         return GetLastError();
372 
373     return ERROR_SUCCESS;
374 }
375