Author: tfaber Date: Sat Apr 27 18:18:17 2013 New Revision: 58873
URL: http://svn.reactos.org/svn/reactos?rev=58873&view=rev Log: [KMTESTS] - Implement a mechanism for kmtests to retrieve information from user-mode. Patch by Nikolay Borisov (nib9 at aber dot ac dot uk). - Currently supported function: QueryVirtualMemory ROSTESTS-96 #resolve
Modified: trunk/rostests/kmtests/include/kmt_public.h trunk/rostests/kmtests/include/kmt_test.h trunk/rostests/kmtests/kmtest/support.c trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c
Modified: trunk/rostests/kmtests/include/kmt_public.h URL: http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/include/kmt_public... ============================================================================== --- trunk/rostests/kmtests/include/kmt_public.h [iso-8859-1] (original) +++ trunk/rostests/kmtests/include/kmt_public.h [iso-8859-1] Sat Apr 27 18:18:17 2013 @@ -17,6 +17,12 @@ #define IOCTL_KMTEST_SET_RESULTBUFFER \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
+#define IOCTL_KMTEST_USERMODE_SEND_RESPONSE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_IN_DIRECT, FILE_WRITE_DATA) + +#define IOCTL_KMTEST_USERMODE_AWAIT_REQ \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_READ_DATA) + #define KMTEST_DEVICE_NAME L"Kmtest" #define KMTEST_DEVICE_DRIVER_PATH L"\Device\" KMTEST_DEVICE_NAME #define KMTEST_DEVICE_PATH L"\\.\Global\GLOBALROOT" KMTEST_DEVICE_DRIVER_PATH
Modified: trunk/rostests/kmtests/include/kmt_test.h URL: http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/include/kmt_test.h... ============================================================================== --- trunk/rostests/kmtests/include/kmt_test.h [iso-8859-1] (original) +++ trunk/rostests/kmtests/include/kmt_test.h [iso-8859-1] Sat Apr 27 18:18:17 2013 @@ -37,6 +37,48 @@ LONG LogBufferMaxLength; CHAR LogBuffer[ANYSIZE_ARRAY]; } KMT_RESULTBUFFER, *PKMT_RESULTBUFFER; + +#ifndef KMT_STANDALONE_DRIVER + +/* usermode call-back mechanism */ + +/* list of supported operations */ +typedef enum _KMT_CALLBACK_INFORMATION_CLASS +{ + QueryVirtualMemory +} KMT_CALLBACK_INFORMATION_CLASS, *PKMT_CALLBACK_INFORMATION_CLASS; + +/* TODO: "response" is a little generic */ +typedef union _KMT_RESPONSE +{ + MEMORY_BASIC_INFORMATION MemInfo; +} KMT_RESPONSE, *PKMT_RESPONSE; + +/* this struct is sent from driver to usermode */ +typedef struct _KMT_CALLBACK_REQUEST_PACKET +{ + ULONG RequestId; + KMT_CALLBACK_INFORMATION_CLASS OperationClass; + PVOID Parameters; +} KMT_CALLBACK_REQUEST_PACKET, *PKMT_CALLBACK_REQUEST_PACKET; + +PKMT_RESPONSE KmtUserModeCallback(KMT_CALLBACK_INFORMATION_CLASS Operation, PVOID Parameters); +VOID KmtFreeCallbackResponse(PKMT_RESPONSE Response); + +//macro to simplify using the mechanism +#define Test_NtQueryVirtualMemory(BaseAddress, Size, AllocationType, ProtectionType) \ + do { \ + PKMT_RESPONSE NtQueryTest = KmtUserModeCallback(QueryVirtualMemory, BaseAddress); \ + if (NtQueryTest != NULL) \ + { \ + ok_eq_hex(NtQueryTest->MemInfo.Protect, ProtectionType); \ + ok_eq_hex(NtQueryTest->MemInfo.State, AllocationType); \ + ok_eq_size(NtQueryTest->MemInfo.RegionSize, Size); \ + KmtFreeCallbackResponse(NtQueryTest); \ + } \ + } while (0) \ + +#endif
#ifdef KMT_STANDALONE_DRIVER #define KMT_KERNEL_MODE
Modified: trunk/rostests/kmtests/kmtest/support.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/kmtest/support.c?r... ============================================================================== --- trunk/rostests/kmtests/kmtest/support.c [iso-8859-1] (original) +++ trunk/rostests/kmtests/kmtest/support.c [iso-8859-1] Sat Apr 27 18:18:17 2013 @@ -11,8 +11,73 @@ #include <kmt_public.h>
#include <assert.h> +#include <debug.h>
extern HANDLE KmtestHandle; + +/** + * @name KmtUserCallbackThread + * + * Thread routine which awaits callback requests from kernel-mode + * + * @return Win32 error code + */ +DWORD +WINAPI +KmtUserCallbackThread( + PVOID Parameter) +{ + DWORD Error = ERROR_SUCCESS; + /* TODO: RequestPacket? */ + KMT_CALLBACK_REQUEST_PACKET RequestPacket; + KMT_RESPONSE Response; + DWORD BytesReturned; + HANDLE LocalKmtHandle; + + UNREFERENCED_PARAMETER(Parameter); + + /* concurrent IoCtls on the same (non-overlapped) handle aren't possible, + * so open a separate one. + * For more info http://www.osronline.com/showthread.cfm?link=230782 */ + LocalKmtHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (LocalKmtHandle == INVALID_HANDLE_VALUE) + error_goto(Error, cleanup); + + while (1) + { + if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_AWAIT_REQ, NULL, 0, &RequestPacket, sizeof(RequestPacket), &BytesReturned, NULL)) + error_goto(Error, cleanup); + ASSERT(BytesReturned == sizeof(RequestPacket)); + + switch (RequestPacket.OperationClass) + { + case QueryVirtualMemory: + { + SIZE_T InfoBufferSize = VirtualQuery(RequestPacket.Parameters, &Response.MemInfo, sizeof(Response.MemInfo)); + /* FIXME: an error is a valid result. That should go as a response to kernel mode instead of terminating the thread */ + if (InfoBufferSize == 0) + error_goto(Error, cleanup); + + if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE, &RequestPacket.RequestId, sizeof(RequestPacket.RequestId), &Response, sizeof(Response), &BytesReturned, NULL)) + error_goto(Error, cleanup); + ASSERT(BytesReturned == 0); + + break; + } + default: + DPRINT1("Unrecognized user-mode callback request\n"); + break; + } + } + +cleanup: + if (LocalKmtHandle != INVALID_HANDLE_VALUE) + CloseHandle(LocalKmtHandle); + + DPRINT("Callback handler dying! Error code %lu", Error); + return Error; +} +
/** * @name KmtRunKernelTest @@ -28,11 +93,17 @@ KmtRunKernelTest( IN PCSTR TestName) { + HANDLE CallbackThread; DWORD Error = ERROR_SUCCESS; DWORD BytesRead; + + CallbackThread = CreateThread(NULL, 0, KmtUserCallbackThread, NULL, 0, NULL);
if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, (DWORD)strlen(TestName), NULL, 0, &BytesRead, NULL)) error(Error); + + if (CallbackThread != NULL) + CloseHandle(CallbackThread);
return Error; }
Modified: trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/kmtest_drv/kmtest_... ============================================================================== --- trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c [iso-8859-1] (original) +++ trunk/rostests/kmtests/kmtest_drv/kmtest_drv.c [iso-8859-1] Sat Apr 27 18:18:17 2013 @@ -18,6 +18,22 @@ #include <kmt_public.h> #define KMT_DEFINE_TEST_FUNCTIONS #include <kmt_test.h> + +/* Usermode callback definitions */ +typedef struct _KMT_USER_WORK_ENTRY +{ + LIST_ENTRY ListEntry; + KEVENT WorkDoneEvent; + KMT_CALLBACK_REQUEST_PACKET Request; + PKMT_RESPONSE Response; +} KMT_USER_WORK_ENTRY, *PKMT_USER_WORK_ENTRY; + +typedef struct _KMT_USER_WORK_LIST +{ + LIST_ENTRY ListHead; + FAST_MUTEX Lock; + KEVENT NewWorkEvent; +} KMT_USER_WORK_LIST, *PKMT_USER_WORK_LIST;
/* Prototypes */ DRIVER_INITIALIZE DriverEntry; @@ -30,10 +46,13 @@ static DRIVER_DISPATCH DriverClose; __drv_dispatchType(IRP_MJ_DEVICE_CONTROL) static DRIVER_DISPATCH DriverIoControl; +static VOID KmtCleanUsermodeCallbacks(VOID);
/* Globals */ static PDEVICE_OBJECT MainDeviceObject; PDRIVER_OBJECT KmtDriverObject = NULL; +static KMT_USER_WORK_LIST WorkList; +static ULONG RequestId = 0;
/* Entry */ /** @@ -92,6 +111,10 @@ DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
+ ExInitializeFastMutex(&WorkList.Lock); + KeInitializeEvent(&WorkList.NewWorkEvent, NotificationEvent, FALSE); + InitializeListHead(&WorkList.ListHead); + cleanup: if (MainDeviceObject && !NT_SUCCESS(Status)) { @@ -122,6 +145,8 @@ UNREFERENCED_PARAMETER(DriverObject);
DPRINT("DriverUnload\n"); + + KmtCleanUsermodeCallbacks();
if (MainDeviceObject) { @@ -403,6 +428,93 @@ ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength); break; } + case IOCTL_KMTEST_USERMODE_AWAIT_REQ: + { + PLIST_ENTRY Entry; + PKMT_USER_WORK_ENTRY WorkItem; + + DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n", + IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); + + /* TODO: prevent multiple concurrent invocations */ + Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest, UserMode, FALSE, NULL); + if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC) + break; + + if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KMT_CALLBACK_REQUEST_PACKET)) + { + Status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + ASSERT(!IsListEmpty(&WorkList.ListHead)); + + Entry = WorkList.ListHead.Flink; + WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry); + + Length = sizeof(WorkItem->Request); + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, Length); + Status = STATUS_SUCCESS; + + KeResetEvent(&WorkList.NewWorkEvent); + break; + + } + case IOCTL_KMTEST_USERMODE_SEND_RESPONSE: + { + PLIST_ENTRY Entry; + PKMT_USER_WORK_ENTRY WorkEntry; + PVOID Response; + ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; + + DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n", + IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, + IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); + + if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE)) + { + Status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + /* FIXME: don't misuse the output buffer as an input! */ + Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); + if (Response == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + ExAcquireFastMutex(&WorkList.Lock); + + Status = STATUS_OBJECTID_NOT_FOUND; + + Entry = WorkList.ListHead.Flink; + while (Entry != &WorkList.ListHead) + { + WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry); + if (WorkEntry->Request.RequestId == *(PULONG)Irp->AssociatedIrp.SystemBuffer) + { + WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR'); + if (WorkEntry->Response == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(WorkEntry->Response, Response, ResponseSize); + KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE); + Status = STATUS_SUCCESS; + break; + } + + Entry = Entry->Flink; + } + + ExReleaseFastMutex(&WorkList.Lock); + + break; + } default: DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n", IoStackLocation->Parameters.DeviceIoControl.IoControlCode); @@ -417,3 +529,113 @@
return Status; } + +/** + * @name KmtUserModeCallback + * + * Enqueue a request to the usermode callback queue and blocks until the work + * is finished. + * + * @param Operation + * TODO + * @param Parameters + * TODO + * TODO: why is this PVOID? + * + * @return Response from user mode + */ +PKMT_RESPONSE +KmtUserModeCallback( + IN KMT_CALLBACK_INFORMATION_CLASS Operation, + IN PVOID Parameters) +{ + PKMT_RESPONSE Result; + NTSTATUS Status; + PKMT_USER_WORK_ENTRY WorkEntry; + LARGE_INTEGER Timeout; + + PAGED_CODE(); + + WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW'); + if (WorkEntry == NULL) + return NULL; + + KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE); + WorkEntry->Request.RequestId = RequestId++; + WorkEntry->Request.OperationClass = Operation; + WorkEntry->Request.Parameters = Parameters; + WorkEntry->Response = NULL; + + ExAcquireFastMutex(&WorkList.Lock); + InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry); + ExReleaseFastMutex(&WorkList.Lock); + + KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE); + + Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds + Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout); + + if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT) + { + DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status); + } + + ExAcquireFastMutex(&WorkList.Lock); + RemoveEntryList(&WorkEntry->ListEntry); + ExReleaseFastMutex(&WorkList.Lock); + + Result = WorkEntry->Response; + + ExFreePoolWithTag(WorkEntry, 'ekrW'); + + return Result; +} + +/** + * @name KmtFreeCallbackResponse + * + * TODO + * + * @param Response + * TODO + */ +VOID +KmtFreeCallbackResponse( + PKMT_RESPONSE Response) +{ + PAGED_CODE(); + + ExFreePoolWithTag(Response, 'pseR'); +} + +/** + * @name KmtCleanUsermodeCallbacks + * + * TODO + */ +static +VOID +KmtCleanUsermodeCallbacks(VOID) +{ + PLIST_ENTRY Entry; + + PAGED_CODE(); + + ExAcquireFastMutex(&WorkList.Lock); + + Entry = WorkList.ListHead.Flink; + while (Entry != &WorkList.ListHead) + { + PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry); + if (WorkEntry->Response != NULL) + { + KmtFreeCallbackResponse(WorkEntry->Response); + } + + Entry = Entry->Flink; + + ExFreePoolWithTag(WorkEntry, 'ekrW'); + } + + ExReleaseFastMutex(&WorkList.Lock); +}