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 VOID
128 KmtLoadDriver(
129     IN PCWSTR ServiceName,
130     IN BOOLEAN RestartIfRunning)
131 {
132     DWORD Error = ERROR_SUCCESS;
133     WCHAR ServicePath[MAX_PATH];
134 
135     StringCbCopyW(ServicePath, sizeof(ServicePath), ServiceName);
136     StringCbCatW(ServicePath, sizeof(ServicePath), L"_drv.sys");
137 
138     StringCbCopyW(TestServiceName, sizeof(TestServiceName), L"Kmtest-");
139     StringCbCatW(TestServiceName, sizeof(TestServiceName), ServiceName);
140 
141     Error = KmtCreateAndStartService(TestServiceName, ServicePath, NULL, &TestServiceHandle, RestartIfRunning);
142 
143     if (Error)
144     {
145         // TODO
146         __debugbreak();
147     }
148 }
149 
150 /**
151  * @name KmtUnloadDriver
152  *
153  * Unload special-purpose driver (stop and delete the service)
154  */
155 VOID
156 KmtUnloadDriver(VOID)
157 {
158     DWORD Error;
159 
160     Error = KmtStopService(TestServiceName, &TestServiceHandle);
161 
162     if (Error)
163     {
164         fprintf(stderr, "Failed to stop %ls service with error 0x%lx\n", TestServiceName, Error);
165     }
166 
167     Error = KmtDeleteService(TestServiceName, &TestServiceHandle);
168 
169     if (Error)
170     {
171         fprintf(stderr, "Failed to delete %ls service with error 0x%lx\n", TestServiceName, Error);
172     }
173 }
174 
175 /**
176  * @name KmtOpenDriver
177  *
178  * Open special-purpose driver (acquire a device handle)
179  */
180 VOID
181 KmtOpenDriver(VOID)
182 {
183     DWORD Error = ERROR_SUCCESS;
184     WCHAR DevicePath[MAX_PATH];
185 
186     StringCbCopyW(DevicePath, sizeof(DevicePath), L"\\\\.\\Global\\GLOBALROOT\\Device\\");
187     StringCbCatW(DevicePath, sizeof(DevicePath), TestServiceName);
188 
189     TestDeviceHandle = CreateFile(DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
190     if (TestDeviceHandle == INVALID_HANDLE_VALUE)
191         error(Error);
192 
193     if (Error)
194     {
195         // TODO
196         __debugbreak();
197     }
198 
199 }
200 
201 /**
202  * @name KmtCloseDriver
203  *
204  * Close special-purpose driver (close device handle)
205  */
206 VOID
207 KmtCloseDriver(VOID)
208 {
209     DWORD Error = ERROR_SUCCESS;
210 
211     if (TestDeviceHandle && !CloseHandle(TestDeviceHandle))
212         error(Error);
213 
214     if (Error)
215     {
216         // TODO
217         __debugbreak();
218     }
219 }
220 
221 /**
222  * @name KmtSendToDriver
223  *
224  * Send an I/O control message with no arguments to the driver opened with KmtOpenDriver
225  *
226  * @param ControlCode
227  *
228  * @return Win32 error code as returned by DeviceIoControl
229  */
230 DWORD
231 KmtSendToDriver(
232     IN DWORD ControlCode)
233 {
234     DWORD BytesRead;
235 
236     assert(ControlCode < 0x400);
237 
238     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), NULL, 0, NULL, 0, &BytesRead, NULL))
239         return GetLastError();
240 
241     return ERROR_SUCCESS;
242 }
243 
244 /**
245  * @name KmtSendStringToDriver
246  *
247  * Send an I/O control message with a string argument to the driver opened with KmtOpenDriver
248  *
249  * @param ControlCode
250  * @param String
251  *
252  * @return Win32 error code as returned by DeviceIoControl
253  */
254 DWORD
255 KmtSendStringToDriver(
256     IN DWORD ControlCode,
257     IN PCSTR String)
258 {
259     DWORD BytesRead;
260 
261     assert(ControlCode < 0x400);
262 
263     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, (DWORD)strlen(String), NULL, 0, &BytesRead, NULL))
264         return GetLastError();
265 
266     return ERROR_SUCCESS;
267 }
268 
269 /**
270  * @name KmtSendWStringToDriver
271  *
272  * Send an I/O control message with a wide string argument to the driver opened with KmtOpenDriver
273  *
274  * @param ControlCode
275  * @param String
276  *
277  * @return Win32 error code as returned by DeviceIoControl
278  */
279 DWORD
280 KmtSendWStringToDriver(
281     IN DWORD ControlCode,
282     IN PCWSTR String)
283 {
284     DWORD BytesRead;
285 
286     assert(ControlCode < 0x400);
287 
288     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, (DWORD)wcslen(String) * sizeof(WCHAR), NULL, 0, &BytesRead, NULL))
289         return GetLastError();
290 
291     return ERROR_SUCCESS;
292 }
293 
294 /**
295  * @name KmtSendUlongToDriver
296  *
297  * Send an I/O control message with an integer argument to the driver opened with KmtOpenDriver
298  *
299  * @param ControlCode
300  * @param Value
301  *
302  * @return Win32 error code as returned by DeviceIoControl
303  */
304 DWORD
305 KmtSendUlongToDriver(
306     IN DWORD ControlCode,
307     IN DWORD Value)
308 {
309     DWORD BytesRead;
310 
311     assert(ControlCode < 0x400);
312 
313     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), &Value, sizeof(Value), NULL, 0, &BytesRead, NULL))
314         return GetLastError();
315 
316     return ERROR_SUCCESS;
317 }
318 
319 /**
320  * @name KmtSendBufferToDriver
321  *
322  * Send an I/O control message with the specified arguments to the driver opened with KmtOpenDriver
323  *
324  * @param ControlCode
325  * @param Buffer
326  * @param InLength
327  * @param OutLength
328  *
329  * @return Win32 error code as returned by DeviceIoControl
330  */
331 DWORD
332 KmtSendBufferToDriver(
333     IN DWORD ControlCode,
334     IN OUT PVOID Buffer OPTIONAL,
335     IN DWORD InLength,
336     IN OUT PDWORD OutLength)
337 {
338     assert(OutLength);
339     assert(Buffer || (!InLength && !*OutLength));
340     assert(ControlCode < 0x400);
341 
342     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), Buffer, InLength, Buffer, *OutLength, OutLength, NULL))
343         return GetLastError();
344 
345     return ERROR_SUCCESS;
346 }
347