From: Paul Durrant Date: Fri, 5 May 2017 14:24:27 +0000 (+0100) Subject: Add console functionality X-Git-Tag: 9.0.0-rc1~50 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=75aab4af16b405184b731ef98351c1bc3f68e429;p=pvdrivers%2Fwin%2Fxencons.git Add console functionality This patch adds a new interface so that user-space code can open a character device to the PV console along with all the necessary dispatch handling for basic functionality. Signed-off-by: Paul Durrant --- diff --git a/include/console_interface.h b/include/console_interface.h new file mode 100644 index 0000000..7c10cef --- /dev/null +++ b/include/console_interface.h @@ -0,0 +1,184 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*! \file console_interface.h + \brief XENBUS CONSOLE Interface + + This interface provides access to XenConsole +*/ + +#ifndef _XENBUS_CONSOLE_INTERFACE_H +#define _XENBUS_CONSOLE_INTERFACE_H + +#ifndef _WINDLL + +/*! \typedef XENBUS_CONSOLE_WAKEUP + \brief XenStore watch handle +*/ +typedef struct _XENBUS_CONSOLE_WAKEUP XENBUS_CONSOLE_WAKEUP, *PXENBUS_CONSOLE_WAKEUP; + +/*! \typedef XENBUS_CONSOLE_ACQUIRE + \brief Acquire a reference to the CONSOLE interface + + \param Interface The interface header +*/ +typedef NTSTATUS +(*XENBUS_CONSOLE_ACQUIRE)( + IN PINTERFACE Interface + ); + +/*! \typedef XENBUS_CONSOLE_RELEASE + \brief Release a reference to the CONSOLE interface + + \param Interface The interface header +*/ +typedef VOID +(*XENBUS_CONSOLE_RELEASE)( + IN PINTERFACE Interface + ); + +/*! \typedef XENBUS_CONSOLE_CAN_READ + \brief Get characters from the console + + \param Interface The interface header + + \return A boolean which is true if there is data to read +*/ +typedef BOOLEAN +(*XENBUS_CONSOLE_CAN_READ)( + IN PINTERFACE Interface + ); + +/*! \typedef XENBUS_CONSOLE_READ + \brief Get characters from the console + + \param Interface The interface header + \param Buffer A character buffer + \param Length The length of the buffer + + \return The number of characters read +*/ +typedef ULONG +(*XENBUS_CONSOLE_READ)( + IN PINTERFACE Interface, + IN PCHAR Data, + IN ULONG Length + ); + +/*! \typedef XENBUS_CONSOLE_CAN_WRITE + \brief Get characters from the console + + \param Interface The interface header + + \return A boolean which is true if there is space to write +*/ +typedef BOOLEAN +(*XENBUS_CONSOLE_CAN_WRITE)( + IN PINTERFACE Interface + ); + +/*! \typedef XENBUS_CONSOLE_WRITE + \brief Send characters to the console + + \param Interface The interface header + \param Buffer A character buffer + \param Length The length of the buffer + + \return The number of characters written +*/ +typedef ULONG +(*XENBUS_CONSOLE_WRITE)( + IN PINTERFACE Interface, + IN PCHAR Data, + IN ULONG Length + ); + +/*! \typedef XENBUS_CONSOLE_WAKEUP_ADD + \brief Add a wakeup item + + \param Interface The interface header + \param Event A pointer to an event object to be signalled when there + is activity on the rings + \param Wakeup A pointer to a wakeup handle to be initialized +*/ +typedef NTSTATUS +(*XENBUS_CONSOLE_WAKEUP_ADD)( + IN PINTERFACE Interface, + IN PKEVENT Event, + OUT PXENBUS_CONSOLE_WAKEUP *Wakeup + ); + +/*! \typedef XENBUS_CONSOLE_WAKEUP_REMOVE + \brief Remove a wakeup item + + \param Interface The interface header + \param Wakeup The wakeup handle +*/ +typedef VOID +(*XENBUS_CONSOLE_WAKEUP_REMOVE)( + IN PINTERFACE Interface, + IN PXENBUS_CONSOLE_WAKEUP Wakeup + ); + +// {04c4f738-034a-4268-bd20-a92ac90d4f82} +DEFINE_GUID(GUID_XENBUS_CONSOLE_INTERFACE, +0x04c4f738, 0x034a, 0x4268, 0xbd, 0x20, 0xa9, 0x2a, 0xc9, 0x0d, 0x4f, 0x82); + +/*! \struct _XENBUS_CONSOLE_INTERFACE_V1 + \brief CONSOLE interface version 1 + \ingroup interfaces +*/ +struct _XENBUS_CONSOLE_INTERFACE_V1 { + INTERFACE Interface; + XENBUS_CONSOLE_ACQUIRE ConsoleAcquire; + XENBUS_CONSOLE_RELEASE ConsoleRelease; + XENBUS_CONSOLE_CAN_READ ConsoleCanRead; + XENBUS_CONSOLE_READ ConsoleRead; + XENBUS_CONSOLE_CAN_WRITE ConsoleCanWrite; + XENBUS_CONSOLE_WRITE ConsoleWrite; + XENBUS_CONSOLE_WAKEUP_ADD ConsoleWakeupAdd; + XENBUS_CONSOLE_WAKEUP_REMOVE ConsoleWakeupRemove; +}; + +typedef struct _XENBUS_CONSOLE_INTERFACE_V1 XENBUS_CONSOLE_INTERFACE, *PXENBUS_CONSOLE_INTERFACE; + +/*! \def XENBUS_CONSOLE + \brief Macro at assist in method invocation +*/ +#define XENBUS_CONSOLE(_Method, _Interface, ...) \ + (_Interface)->Console ## _Method((PINTERFACE)(_Interface), __VA_ARGS__) + +#endif // _WINDLL + +#define XENBUS_CONSOLE_INTERFACE_VERSION_MIN 1 +#define XENBUS_CONSOLE_INTERFACE_VERSION_MAX 1 + +#endif // _XENBUS_CONSOLE_INTERFACE_H diff --git a/include/xencons_device.h b/include/xencons_device.h new file mode 100644 index 0000000..eaf57d8 --- /dev/null +++ b/include/xencons_device.h @@ -0,0 +1,39 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _XENCONS_DEVICE_H +#define _XENCONS_DEVICE_H + +// {0D3EDD21-8EF9-4DFF-856C-8C68BF4FDCA3} +DEFINE_GUID(GUID_XENCONS_DEVICE, + 0xd3edd21, 0x8ef9, 0x4dff, 0x85, 0x6c, 0x8c, 0x68, 0xbf, 0x4f, 0xdc, 0xa3); + +#endif // _XENCONS_DEVICE_H diff --git a/src/xencons/driver.h b/src/xencons/driver.h index 05c5407..4a2eb61 100644 --- a/src/xencons/driver.h +++ b/src/xencons/driver.h @@ -60,6 +60,8 @@ typedef struct _XENCONS_DX { PDEVICE_OBJECT DeviceObject; DEVICE_OBJECT_TYPE Type; + UNICODE_STRING Link; + DEVICE_PNP_STATE DevicePnpState; DEVICE_PNP_STATE PreviousDevicePnpState; diff --git a/src/xencons/fdo.c b/src/xencons/fdo.c index 9f39c6d..57e7f6d 100644 --- a/src/xencons/fdo.c +++ b/src/xencons/fdo.c @@ -33,17 +33,21 @@ #include #include +#include #include #include #include #include #include +#include +#include #include #include "driver.h" #include "registry.h" #include "fdo.h" +#include "stream.h" #include "thread.h" #include "names.h" #include "dbg_print.h" @@ -65,6 +69,12 @@ typedef struct _FDO_RESOURCE { CM_PARTIAL_RESOURCE_DESCRIPTOR Translated; } FDO_RESOURCE, *PFDO_RESOURCE; +typedef struct _FDO_HANDLE { + LIST_ENTRY ListEntry; + PFILE_OBJECT FileObject; + PXENCONS_STREAM Stream; +} FDO_HANDLE, *PFDO_HANDLE; + struct _XENCONS_FDO { PXENCONS_DX Dx; PDEVICE_OBJECT LowerDeviceObject; @@ -83,9 +93,13 @@ struct _XENCONS_FDO { FDO_RESOURCE Resource[RESOURCE_COUNT]; + LIST_ENTRY HandleList; + KSPIN_LOCK HandleLock; + XENBUS_DEBUG_INTERFACE DebugInterface; XENBUS_SUSPEND_INTERFACE SuspendInterface; XENBUS_STORE_INTERFACE StoreInterface; + XENBUS_CONSOLE_INTERFACE ConsoleInterface; PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate; }; @@ -819,6 +833,7 @@ FdoD3ToD0( IN PXENCONS_FDO Fdo ) { + PXENCONS_DX Dx = Fdo->Dx; POWER_STATE PowerState; KIRQL Irql; NTSTATUS status; @@ -858,12 +873,15 @@ FdoD3ToD0( DevicePowerState, PowerState); +#pragma prefast(suppress:28123) + (VOID) IoSetDeviceInterfaceState(&Dx->Link, TRUE); + Trace("<====\n"); return STATUS_SUCCESS; fail3: - Error("fail3\n"); + Error("fail4\n"); __FdoD0ToD3(Fdo); @@ -890,6 +908,7 @@ FdoD0ToD3( IN PXENCONS_FDO Fdo ) { + PXENCONS_DX Dx = Fdo->Dx; POWER_STATE PowerState; KIRQL Irql; @@ -898,6 +917,9 @@ FdoD0ToD3( Trace("====>\n"); +#pragma prefast(suppress:28123) + (VOID) IoSetDeviceInterfaceState(&Dx->Link, FALSE); + PowerState.DeviceState = PowerDeviceD3; PoSetPowerState(Fdo->Dx->DeviceObject, DevicePowerState, @@ -2071,6 +2093,223 @@ done: return status; } +static NTSTATUS +FdoCreateHandle( + IN PXENCONS_FDO Fdo, + IN PFILE_OBJECT FileObject + ) +{ + PFDO_HANDLE Handle; + KIRQL Irql; + NTSTATUS status; + + Handle = __FdoAllocate(sizeof (FDO_HANDLE)); + + status = STATUS_NO_MEMORY; + if (Handle == NULL) + goto fail1; + + status = StreamCreate(Fdo, &Handle->Stream); + if (!NT_SUCCESS(status)) + goto fail2; + + Handle->FileObject = FileObject; + + KeAcquireSpinLock(&Fdo->HandleLock, &Irql); + InsertTailList(&Fdo->HandleList, &Handle->ListEntry); + KeReleaseSpinLock(&Fdo->HandleLock, Irql); + + Trace("%p\n", Handle->FileObject); + + return STATUS_SUCCESS; + +fail2: + Error("fail2\n"); + + ASSERT(IsZeroMemory(Handle, sizeof (FDO_HANDLE))); + __FdoFree(Handle); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +static PFDO_HANDLE +FdoFindHandle( + IN PXENCONS_FDO Fdo, + IN PFILE_OBJECT FileObject + ) +{ + KIRQL Irql; + PLIST_ENTRY ListEntry; + PFDO_HANDLE Handle; + NTSTATUS status; + + KeAcquireSpinLock(&Fdo->HandleLock, &Irql); + + for (ListEntry = Fdo->HandleList.Flink; + ListEntry != &Fdo->HandleList; + ListEntry = ListEntry->Flink) { + Handle = CONTAINING_RECORD(ListEntry, + FDO_HANDLE, + ListEntry); + + if (Handle->FileObject == FileObject) + goto found; + } + + status = STATUS_UNSUCCESSFUL; + goto fail1; + +found: + KeReleaseSpinLock(&Fdo->HandleLock, Irql); + + return Handle; + +fail1: + Error("fail1 (%08x)\n", status); + + KeReleaseSpinLock(&Fdo->HandleLock, Irql); + + return NULL; +} + +static VOID +FdoDestroyHandle( + IN PXENCONS_FDO Fdo, + IN PFDO_HANDLE Handle + ) +{ + KIRQL Irql; + + KeAcquireSpinLock(&Fdo->HandleLock, &Irql); + RemoveEntryList(&Handle->ListEntry); + KeReleaseSpinLock(&Fdo->HandleLock, Irql); + + RtlZeroMemory(&Handle->ListEntry, sizeof (LIST_ENTRY)); + + Trace("%p\n", Handle->FileObject); + + StreamDestroy(Handle->Stream); + Handle->Stream = NULL; + + Handle->FileObject = NULL; + + ASSERT(IsZeroMemory(Handle, sizeof (FDO_HANDLE))); + __FdoFree(Handle); +} + +static DECLSPEC_NOINLINE NTSTATUS +FdoDispatchCreate( + IN PXENCONS_FDO Fdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + status = FdoCreateHandle(Fdo, StackLocation->FileObject); + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +FdoDispatchCleanup( + IN PXENCONS_FDO Fdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + PFDO_HANDLE Handle; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + Handle = FdoFindHandle(Fdo, StackLocation->FileObject); + + status = STATUS_UNSUCCESSFUL; + if (Handle == NULL) + goto fail1; + + FdoDestroyHandle(Fdo, Handle); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + +fail1: + Error("fail1 (%08x)\n", status); + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +FdoDispatchClose( + IN PXENCONS_FDO Fdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Fdo); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +FdoDispatchReadWrite( + IN PXENCONS_FDO Fdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + PFDO_HANDLE Handle; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + Handle = FdoFindHandle(Fdo, StackLocation->FileObject); + + status = STATUS_UNSUCCESSFUL; + if (Handle == NULL) + goto fail1; + + IoMarkIrpPending(Irp); + + status = StreamPutQueue(Handle->Stream, Irp); + if (!NT_SUCCESS(status)) + goto fail2; + + return STATUS_PENDING; + +fail2: + Error("fail2\n"); + +fail1: + Error("fail1 (%08x)\n", status); + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + static DECLSPEC_NOINLINE NTSTATUS FdoDispatchDefault( IN PXENCONS_FDO Fdo, @@ -2092,11 +2331,17 @@ FdoDispatch( ) { PIO_STACK_LOCATION StackLocation; + UCHAR MajorFunction; NTSTATUS status; StackLocation = IoGetCurrentIrpStackLocation(Irp); + MajorFunction = StackLocation->MajorFunction; + + Trace("====> (%02x:%s)\n", + MajorFunction, + MajorFunctionName(MajorFunction)); - switch (StackLocation->MajorFunction) { + switch (MajorFunction) { case IRP_MJ_PNP: status = FdoDispatchPnp(Fdo, Irp); break; @@ -2105,11 +2350,33 @@ FdoDispatch( status = FdoDispatchPower(Fdo, Irp); break; + case IRP_MJ_CREATE: + status = FdoDispatchCreate(Fdo, Irp); + break; + + case IRP_MJ_CLEANUP: + status = FdoDispatchCleanup(Fdo, Irp); + break; + + case IRP_MJ_CLOSE: + status = FdoDispatchClose(Fdo, Irp); + break; + + case IRP_MJ_READ: + case IRP_MJ_WRITE: + status = FdoDispatchReadWrite(Fdo, Irp); + break; + default: status = FdoDispatchDefault(Fdo, Irp); break; } + Trace("<==== (%02x:%s)(%08x)\n", + MajorFunction, + MajorFunctionName(MajorFunction), + status); + return status; } @@ -2213,6 +2480,7 @@ FdoGet ## _Interface ## Interface( \ DEFINE_FDO_GET_INTERFACE(Debug, PXENBUS_DEBUG_INTERFACE) DEFINE_FDO_GET_INTERFACE(Suspend, PXENBUS_SUSPEND_INTERFACE) DEFINE_FDO_GET_INTERFACE(Store, PXENBUS_STORE_INTERFACE) +DEFINE_FDO_GET_INTERFACE(Console, PXENBUS_CONSOLE_INTERFACE) NTSTATUS FdoCreate( @@ -2239,6 +2507,13 @@ FdoCreate( Dx = (PXENCONS_DX)FunctionDeviceObject->DeviceExtension; RtlZeroMemory(Dx, sizeof (XENCONS_DX)); + status = IoRegisterDeviceInterface(PhysicalDeviceObject, + &GUID_XENCONS_DEVICE, + NULL, + &Dx->Link); + if (!NT_SUCCESS(status)) + goto fail2; + Dx->Type = FUNCTION_DEVICE_OBJECT; Dx->DeviceObject = FunctionDeviceObject; Dx->DevicePnpState = Added; @@ -2249,7 +2524,7 @@ FdoCreate( status = STATUS_NO_MEMORY; if (Fdo == NULL) - goto fail2; + goto fail3; Fdo->Dx = Dx; Fdo->PhysicalDeviceObject = PhysicalDeviceObject; @@ -2258,22 +2533,22 @@ FdoCreate( status = ThreadCreate(FdoSystemPower, Fdo, &Fdo->SystemPowerThread); if (!NT_SUCCESS(status)) - goto fail3; + goto fail4; status = ThreadCreate(FdoDevicePower, Fdo, &Fdo->DevicePowerThread); if (!NT_SUCCESS(status)) - goto fail4; + goto fail5; status = __FdoAcquireLowerBusInterface(Fdo); if (!NT_SUCCESS(status)) - goto fail5; + goto fail6; if (FdoGetBusData(Fdo, PCI_WHICHSPACE_CONFIG, &DeviceID, FIELD_OFFSET(PCI_COMMON_HEADER, DeviceID), FIELD_SIZE(PCI_COMMON_HEADER, DeviceID)) == 0) - goto fail6; + goto fail7; __FdoSetVendorName(Fdo, DeviceID); @@ -2286,7 +2561,7 @@ FdoCreate( sizeof (Fdo->DebugInterface), FALSE); if (!NT_SUCCESS(status)) - goto fail7; + goto fail8; status = FDO_QUERY_INTERFACE(Fdo, XENBUS, @@ -2295,7 +2570,7 @@ FdoCreate( sizeof (Fdo->SuspendInterface), FALSE); if (!NT_SUCCESS(status)) - goto fail8; + goto fail9; status = FDO_QUERY_INTERFACE(Fdo, XENBUS, @@ -2304,7 +2579,21 @@ FdoCreate( sizeof (Fdo->StoreInterface), FALSE); if (!NT_SUCCESS(status)) - goto fail9; + goto fail10; + + status = FDO_QUERY_INTERFACE(Fdo, + XENBUS, + CONSOLE, + (PINTERFACE)&Fdo->ConsoleInterface, + sizeof (Fdo->ConsoleInterface), + FALSE); + if (!NT_SUCCESS(status)) + goto fail11; + + InitializeListHead(&Fdo->HandleList); + KeInitializeSpinLock(&Fdo->HandleLock); + + FunctionDeviceObject->Flags |= DO_BUFFERED_IO; Dx->Fdo = Fdo; @@ -2315,44 +2604,50 @@ FdoCreate( FunctionDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; -fail9: - Error("fail9\n"); +fail11: + Error("fail11\n"); + + RtlZeroMemory(&Fdo->ConsoleInterface, + sizeof (XENBUS_CONSOLE_INTERFACE)); + +fail10: + Error("fail10\n"); RtlZeroMemory(&Fdo->SuspendInterface, sizeof (XENBUS_SUSPEND_INTERFACE)); -fail8: - Error("fail8\n"); +fail9: + Error("fail9\n"); RtlZeroMemory(&Fdo->DebugInterface, sizeof (XENBUS_DEBUG_INTERFACE)); -fail7: - Error("fail7\n"); +fail8: + Error("fail8\n"); RtlZeroMemory(Fdo->VendorName, MAXNAMELEN); -fail6: - Error("fail6\n"); +fail7: + Error("fail7\n"); __FdoReleaseLowerBusInterface(Fdo); -fail5: - Error("fail5\n"); +fail6: + Error("fail6\n"); ThreadAlert(Fdo->DevicePowerThread); ThreadJoin(Fdo->DevicePowerThread); Fdo->DevicePowerThread = NULL; -fail4: - Error("fail4\n"); +fail5: + Error("fail5\n"); ThreadAlert(Fdo->SystemPowerThread); ThreadJoin(Fdo->SystemPowerThread); Fdo->SystemPowerThread = NULL; -fail3: - Error("fail3\n"); +fail4: + Error("fail4\n"); #pragma prefast(suppress:28183) // Fdo->LowerDeviceObject could be NULL IoDetachDevice(Fdo->LowerDeviceObject); @@ -2364,6 +2659,11 @@ fail3: ASSERT(IsZeroMemory(Fdo, sizeof (XENCONS_FDO))); __FdoFree(Fdo); +fail3: + Error("fail3\n"); + + RtlFreeUnicodeString(&Dx->Link); + fail2: Error("fail2\n"); @@ -2393,6 +2693,14 @@ FdoDestroy( Dx->Fdo = NULL; + RtlZeroMemory(&Fdo->HandleLock, sizeof (KSPIN_LOCK)); + + ASSERT(IsListEmpty(&Fdo->HandleList)); + RtlZeroMemory(&Fdo->HandleList, sizeof (LIST_ENTRY)); + + RtlZeroMemory(&Fdo->ConsoleInterface, + sizeof (XENBUS_CONSOLE_INTERFACE)); + RtlZeroMemory(&Fdo->StoreInterface, sizeof (XENBUS_STORE_INTERFACE)); @@ -2424,5 +2732,7 @@ FdoDestroy( ASSERT(IsZeroMemory(Fdo, sizeof (XENCONS_FDO))); __FdoFree(Fdo); + RtlFreeUnicodeString(&Dx->Link); + IoDeleteDevice(FunctionDeviceObject); } diff --git a/src/xencons/fdo.h b/src/xencons/fdo.h index d8cdb55..19eaa42 100644 --- a/src/xencons/fdo.h +++ b/src/xencons/fdo.h @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "driver.h" @@ -44,6 +46,18 @@ FdoDispatch( IN PIRP Irp ); +#define DECLARE_FDO_GET_INTERFACE(_Interface, _Type) \ +extern VOID \ +FdoGet ## _Interface ## Interface( \ + IN PXENCONS_FDO Fdo, \ + OUT _Type _Interface ## Interface \ + ); + +DECLARE_FDO_GET_INTERFACE(Debug, PXENBUS_DEBUG_INTERFACE) +DECLARE_FDO_GET_INTERFACE(Suspend, PXENBUS_SUSPEND_INTERFACE) +DECLARE_FDO_GET_INTERFACE(Store, PXENBUS_STORE_INTERFACE) +DECLARE_FDO_GET_INTERFACE(Console, PXENBUS_CONSOLE_INTERFACE) + extern NTSTATUS FdoCreate( IN PDEVICE_OBJECT PhysicalDeviceObject diff --git a/src/xencons/names.h b/src/xencons/names.h index 9cbcc03..1c06563 100644 --- a/src/xencons/names.h +++ b/src/xencons/names.h @@ -247,4 +247,51 @@ DeviceUsageTypeName( #undef _DEVICE_USAGE_TYPE_NAME } +static FORCEINLINE const CHAR * +MajorFunctionName( + IN ULONG Function + ) +{ +#define _MAJOR_FUNCTION_NAME(_Function) \ + case IRP_MJ_ ## _Function: \ + return #_Function; + + switch (Function) { + _MAJOR_FUNCTION_NAME(CREATE); + _MAJOR_FUNCTION_NAME(CREATE_NAMED_PIPE); + _MAJOR_FUNCTION_NAME(CLOSE); + _MAJOR_FUNCTION_NAME(READ); + _MAJOR_FUNCTION_NAME(WRITE); + _MAJOR_FUNCTION_NAME(QUERY_INFORMATION); + _MAJOR_FUNCTION_NAME(SET_INFORMATION); + _MAJOR_FUNCTION_NAME(QUERY_EA); + _MAJOR_FUNCTION_NAME(SET_EA); + _MAJOR_FUNCTION_NAME(FLUSH_BUFFERS); + _MAJOR_FUNCTION_NAME(QUERY_VOLUME_INFORMATION); + _MAJOR_FUNCTION_NAME(SET_VOLUME_INFORMATION); + _MAJOR_FUNCTION_NAME(DIRECTORY_CONTROL); + _MAJOR_FUNCTION_NAME(FILE_SYSTEM_CONTROL); + _MAJOR_FUNCTION_NAME(DEVICE_CONTROL); + _MAJOR_FUNCTION_NAME(INTERNAL_DEVICE_CONTROL); + _MAJOR_FUNCTION_NAME(SHUTDOWN); + _MAJOR_FUNCTION_NAME(LOCK_CONTROL); + _MAJOR_FUNCTION_NAME(CLEANUP); + _MAJOR_FUNCTION_NAME(CREATE_MAILSLOT); + _MAJOR_FUNCTION_NAME(QUERY_SECURITY); + _MAJOR_FUNCTION_NAME(SET_SECURITY); + _MAJOR_FUNCTION_NAME(POWER); + _MAJOR_FUNCTION_NAME(SYSTEM_CONTROL); + _MAJOR_FUNCTION_NAME(DEVICE_CHANGE); + _MAJOR_FUNCTION_NAME(QUERY_QUOTA); + _MAJOR_FUNCTION_NAME(SET_QUOTA); + _MAJOR_FUNCTION_NAME(PNP); + default: + break; + } + + return "UNKNOWN"; + +#undef _MAJOR_FUNCTION_NAME +} + #endif // _XENCONS_NAMES_H_ diff --git a/src/xencons/stream.c b/src/xencons/stream.c new file mode 100644 index 0000000..0f4c129 --- /dev/null +++ b/src/xencons/stream.c @@ -0,0 +1,440 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include "fdo.h" +#include "stream.h" +#include "thread.h" +#include "names.h" +#include "dbg_print.h" +#include "assert.h" +#include "util.h" + +#define STREAM_POOL 'ETRS' + +struct _XENCONS_STREAM { + PXENCONS_FDO Fdo; + PXENCONS_THREAD Thread; + IO_CSQ Csq; + LIST_ENTRY List; + KSPIN_LOCK Lock; + XENBUS_CONSOLE_INTERFACE ConsoleInterface; +}; + +static FORCEINLINE PVOID +__StreamAllocate( + IN ULONG Length + ) +{ + return __AllocatePoolWithTag(NonPagedPool, Length, STREAM_POOL); +} + +static FORCEINLINE VOID +__StreamFree( + IN PVOID Buffer + ) +{ + __FreePoolWithTag(Buffer, STREAM_POOL); +} + +IO_CSQ_INSERT_IRP_EX StreamCsqInsertIrpEx; + +NTSTATUS +StreamCsqInsertIrpEx( + IN PIO_CSQ Csq, + IN PIRP Irp, + IN PVOID InsertContext OPTIONAL + ) +{ + BOOLEAN ReInsert = (BOOLEAN)(ULONG_PTR)InsertContext; + PXENCONS_STREAM Stream; + + Stream = CONTAINING_RECORD(Csq, XENCONS_STREAM, Csq); + + if (ReInsert) { + // This only occurs if the worker thread de-queued the IRP but + // then found the console to be blocked. + InsertHeadList(&Stream->List, &Irp->Tail.Overlay.ListEntry); + } else { + InsertTailList(&Stream->List, &Irp->Tail.Overlay.ListEntry); + ThreadWake(Stream->Thread); + } + + return STATUS_SUCCESS; +} + +IO_CSQ_REMOVE_IRP StreamCsqRemoveIrp; + +VOID +StreamCsqRemoveIrp( + IN PIO_CSQ Csq, + IN PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Csq); + + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + +IO_CSQ_PEEK_NEXT_IRP StreamCsqPeekNextIrp; + +PIRP +StreamCsqPeekNextIrp( + IN PIO_CSQ Csq, + IN PIRP Irp, + IN PVOID PeekContext OPTIONAL + ) +{ + PXENCONS_STREAM Stream; + PLIST_ENTRY ListEntry; + PIRP NextIrp; + + UNREFERENCED_PARAMETER(PeekContext); + + Stream = CONTAINING_RECORD(Csq, XENCONS_STREAM, Csq); + + ListEntry = (Irp == NULL) ? + Stream->List.Flink : + Irp->Tail.Overlay.ListEntry.Flink; + + if (ListEntry == &Stream->List) + return NULL; + + NextIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry); + + return NextIrp; +} + +#pragma warning(push) +#pragma warning(disable:28167) // function changes IRQL + +IO_CSQ_ACQUIRE_LOCK StreamCsqAcquireLock; + +VOID +StreamCsqAcquireLock( + IN PIO_CSQ Csq, + OUT PKIRQL Irql + ) +{ + PXENCONS_STREAM Stream; + + Stream = CONTAINING_RECORD(Csq, XENCONS_STREAM, Csq); + + KeAcquireSpinLock(&Stream->Lock, Irql); +} + +IO_CSQ_RELEASE_LOCK StreamCsqReleaseLock; + +VOID +StreamCsqReleaseLock( + IN PIO_CSQ Csq, + IN KIRQL Irql + ) +{ + PXENCONS_STREAM Stream; + + Stream = CONTAINING_RECORD(Csq, XENCONS_STREAM, Csq); + + KeReleaseSpinLock(&Stream->Lock, Irql); +} + +#pragma warning(pop) + +IO_CSQ_COMPLETE_CANCELED_IRP StreamCsqCompleteCanceledIrp; + +VOID +StreamCsqCompleteCanceledIrp( + IN PIO_CSQ Csq, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + UCHAR MajorFunction; + + UNREFERENCED_PARAMETER(Csq); + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + MajorFunction = StackLocation->MajorFunction; + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + Trace("COMPLETE (%02x:%s)\n", + MajorFunction, + MajorFunctionName(MajorFunction)); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} + +static NTSTATUS +StreamWorker( + IN PXENCONS_THREAD Self, + IN PVOID Context + ) +{ + PXENCONS_STREAM Stream = Context; + PKEVENT Event; + PXENBUS_CONSOLE_WAKEUP Wakeup; + NTSTATUS status; + + Trace("====>\n"); + + Event = ThreadGetEvent(Self); + + status = XENBUS_CONSOLE(Acquire, + &Stream->ConsoleInterface); + if (!NT_SUCCESS(status)) + goto fail1; + + status = XENBUS_CONSOLE(WakeupAdd, + &Stream->ConsoleInterface, + Event, + &Wakeup); + if (!NT_SUCCESS(status)) + goto fail2; + + for (;;) { + PIRP Irp; + + (VOID) KeWaitForSingleObject(Event, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(Event); + + if (ThreadIsAlerted(Self)) + break; + + for (Irp = IoCsqRemoveNextIrp(&Stream->Csq, NULL); + Irp != NULL; + Irp = IoCsqRemoveNextIrp(&Stream->Csq, NULL)) { + PIO_STACK_LOCATION StackLocation; + UCHAR MajorFunction; + BOOLEAN Blocked; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + MajorFunction = StackLocation->MajorFunction; + + switch (MajorFunction) { + case IRP_MJ_READ: + Blocked = !XENBUS_CONSOLE(CanRead, + &Stream->ConsoleInterface); + break; + + case IRP_MJ_WRITE: + Blocked = !XENBUS_CONSOLE(CanWrite, + &Stream->ConsoleInterface); + break; + + default: + ASSERT(FALSE); + + Blocked = TRUE; + break; + } + + if (Blocked) { + status = IoCsqInsertIrpEx(&Stream->Csq, + Irp, + NULL, + (PVOID)TRUE); + ASSERT(NT_SUCCESS(status)); + + break; + } + + switch (MajorFunction) { + case IRP_MJ_READ: { + ULONG Length; + PCHAR Buffer; + ULONG Read; + + Length = StackLocation->Parameters.Read.Length; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + Read = XENBUS_CONSOLE(Read, + &Stream->ConsoleInterface, + Buffer, + Length); + + Irp->IoStatus.Information = Read; + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + } + case IRP_MJ_WRITE: { + ULONG Length; + PCHAR Buffer; + ULONG Written; + + Length = StackLocation->Parameters.Write.Length; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + Written = XENBUS_CONSOLE(Write, + &Stream->ConsoleInterface, + Buffer, + Length); + + Irp->IoStatus.Information = Written; + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + } + default: + ASSERT(FALSE); + + Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + break; + } + + Trace("COMPLETE (%02x:%s) (%u bytes)\n", + MajorFunction, + MajorFunctionName(MajorFunction), + Irp->IoStatus.Information); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + } + + XENBUS_CONSOLE(WakeupRemove, + &Stream->ConsoleInterface, + Wakeup); + + XENBUS_CONSOLE(Release, &Stream->ConsoleInterface); + + Trace("<====\n"); + + return STATUS_SUCCESS; + +fail2: + Error("fail2\n"); + + XENBUS_CONSOLE(Release, &Stream->ConsoleInterface); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +NTSTATUS +StreamCreate( + IN PXENCONS_FDO Fdo, + OUT PXENCONS_STREAM *Stream + ) +{ + NTSTATUS status; + + *Stream = __StreamAllocate(sizeof (XENCONS_STREAM)); + + status = STATUS_NO_MEMORY; + if (*Stream == NULL) + goto fail1; + + FdoGetConsoleInterface(Fdo, &(*Stream)->ConsoleInterface); + + KeInitializeSpinLock(&(*Stream)->Lock); + InitializeListHead(&(*Stream)->List); + + status = IoCsqInitializeEx(&(*Stream)->Csq, + StreamCsqInsertIrpEx, + StreamCsqRemoveIrp, + StreamCsqPeekNextIrp, + StreamCsqAcquireLock, + StreamCsqReleaseLock, + StreamCsqCompleteCanceledIrp); + if (!NT_SUCCESS(status)) + goto fail2; + + status = ThreadCreate(StreamWorker, + *Stream, + &(*Stream)->Thread); + if (!NT_SUCCESS(status)) + goto fail3; + + (*Stream)->Fdo = Fdo; + + return STATUS_SUCCESS; + +fail3: + Error("fail3\n"); + + RtlZeroMemory(&(*Stream)->Csq, sizeof (IO_CSQ)); + +fail2: + Error("fail2\n"); + + RtlZeroMemory(&(*Stream)->List, sizeof (LIST_ENTRY)); + RtlZeroMemory(&(*Stream)->Lock, sizeof (KSPIN_LOCK)); + + RtlZeroMemory(&(*Stream)->ConsoleInterface, + sizeof (XENBUS_CONSOLE_INTERFACE)); + + ASSERT(IsZeroMemory(*Stream, sizeof (XENCONS_STREAM))); + __StreamFree(*Stream); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +VOID +StreamDestroy( + IN PXENCONS_STREAM Stream + ) +{ + Stream->Fdo = NULL; + + ThreadAlert(Stream->Thread); + ThreadJoin(Stream->Thread); + Stream->Thread = NULL; + + RtlZeroMemory(&Stream->Csq, sizeof (IO_CSQ)); + + RtlZeroMemory(&Stream->List, sizeof (LIST_ENTRY)); + RtlZeroMemory(&Stream->Lock, sizeof (KSPIN_LOCK)); + + RtlZeroMemory(&Stream->ConsoleInterface, + sizeof (XENBUS_CONSOLE_INTERFACE)); + + ASSERT(IsZeroMemory(Stream, sizeof (XENCONS_STREAM))); + __StreamFree(Stream); +} + +NTSTATUS +StreamPutQueue( + IN PXENCONS_STREAM Stream, + IN PIRP Irp + ) +{ + return IoCsqInsertIrpEx(&Stream->Csq, Irp, NULL, (PVOID)FALSE); +} diff --git a/src/xencons/stream.h b/src/xencons/stream.h new file mode 100644 index 0000000..5fa85ce --- /dev/null +++ b/src/xencons/stream.h @@ -0,0 +1,58 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _XENCONS_STREAM_H +#define _XENCONS_STREAM_H + +#include + +#include "fdo.h" + +typedef struct _XENCONS_STREAM XENCONS_STREAM, *PXENCONS_STREAM; + +extern NTSTATUS +StreamCreate( + IN PXENCONS_FDO Fdo, + OUT PXENCONS_STREAM *Stream + ); + +extern VOID +StreamDestroy( + IN PXENCONS_STREAM Stream + ); + +extern NTSTATUS +StreamPutQueue( + IN PXENCONS_STREAM Stream, + IN PIRP Irp + ); + +#endif // _XENCONS_STREAM_H diff --git a/vs2015/xencons/xencons.vcxproj b/vs2015/xencons/xencons.vcxproj index 9f8d4ee..ff65d9f 100644 --- a/vs2015/xencons/xencons.vcxproj +++ b/vs2015/xencons/xencons.vcxproj @@ -67,6 +67,7 @@ +