[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [win-pv-devel] [PATCH] Add basic support for PV console
Xen toolstacks have, for some time, created a PV console even for HVM guests but, so far, Windows has had no frontend for this. This patch adds the basic plumbing for the PV console, under a new interface called XENBUS_CONSOLE. This interface is currently private to XENBUS (so not accessible by child drivers) and will initially only support writing. The patch adds a new log disposition that will write INFO, WARNING and ERROR level messages, so these can now be seen by attaching to the console backend in the toolstack domain. Future patches will add read support and a new character device to make the console available to user-space. Signed-off-by: Paul Durrant <paul.durrant@xxxxxxxxxx> --- get_xen_headers.py | 1 + include/console_interface.h | 107 ++++++ include/xen.h | 19 + include/xen/public/io/console.h | 51 +++ src/xen/log.h | 15 - src/xenbus/console.c | 820 ++++++++++++++++++++++++++++++++++++++++ src/xenbus/console.h | 67 ++++ src/xenbus/fdo.c | 109 +++++- vs2012/xenbus/xenbus.vcxproj | 1 + vs2013/xenbus/xenbus.vcxproj | 1 + vs2015/xenbus/xenbus.vcxproj | 1 + 11 files changed, 1158 insertions(+), 34 deletions(-) create mode 100644 include/console_interface.h create mode 100644 include/xen/public/io/console.h create mode 100644 src/xenbus/console.c create mode 100644 src/xenbus/console.h diff --git a/get_xen_headers.py b/get_xen_headers.py index f4e24d1..32f27ed 100755 --- a/get_xen_headers.py +++ b/get_xen_headers.py @@ -83,6 +83,7 @@ if __name__ == '__main__': copy_file(working, 'public\\hvm', 'hvm_info_table.h') copy_file(working, 'public\\io', 'xs_wire.h') + copy_file(working, 'public\\io', 'console.h') put_branch(working) diff --git a/include/console_interface.h b/include/console_interface.h new file mode 100644 index 0000000..bcea6fb --- /dev/null +++ b/include/console_interface.h @@ -0,0 +1,107 @@ +/* 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_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_WRITE + \brief Send characters to the console + + \param Interface The interface header + \param Buffer A character buffer + \param Length The length of the buffer + \param CRLF Substitute LF with CRLF +*/ +typedef NTSTATUS +(*XENBUS_CONSOLE_WRITE)( + IN PINTERFACE Interface, + IN PCHAR Data, + IN ULONG Length, + IN BOOLEAN CRLF + ); + +// {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_WRITE ConsoleWrite; +}; + +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/xen.h b/include/xen.h index 21511b8..470ccd4 100644 --- a/include/xen.h +++ b/include/xen.h @@ -56,6 +56,7 @@ #define EINVAL XEN_EINVAL #include <public/io/xs_wire.h> +#include <public/io/console.h> #include <public/version.h> #ifndef XEN_API @@ -412,6 +413,24 @@ LogResume( VOID ); +typedef struct _LOG_DISPOSITION LOG_DISPOSITION, *PLOG_DISPOSITION; + +XEN_API +NTSTATUS +LogAddDisposition( + IN LOG_LEVEL Mask, + IN VOID (*Function)(PVOID, PCHAR, ULONG), + IN PVOID Argument OPTIONAL, + OUT PLOG_DISPOSITION *Disposition + ); + +XEN_API +VOID +LogRemoveDisposition( + IN PLOG_DISPOSITION Disposition + ); + + // SYSTEM XEN_API diff --git a/include/xen/public/io/console.h b/include/xen/public/io/console.h new file mode 100644 index 0000000..6496018 --- /dev/null +++ b/include/xen/public/io/console.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * console.h + * + * Console I/O interface for Xen guest OSes. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_IO_CONSOLE_H__ +#define __XEN_PUBLIC_IO_CONSOLE_H__ + +typedef uint32_t XENCONS_RING_IDX; + +#define MASK_XENCONS_IDX(idx, ring) ((idx) & (sizeof(ring)-1)) + +struct xencons_interface { + char in[1024]; + char out[2048]; + XENCONS_RING_IDX in_cons, in_prod; + XENCONS_RING_IDX out_cons, out_prod; +}; + +#endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/xen/log.h b/src/xen/log.h index 67b4f16..6af3158 100644 --- a/src/xen/log.h +++ b/src/xen/log.h @@ -34,21 +34,6 @@ #include <xen.h> -typedef struct _LOG_DISPOSITION LOG_DISPOSITION, *PLOG_DISPOSITION; - -extern NTSTATUS -LogAddDisposition( - IN LOG_LEVEL Mask, - IN VOID (*Function)(PVOID, PCHAR, ULONG), - IN PVOID Argument OPTIONAL, - OUT PLOG_DISPOSITION *Disposition - ); - -extern VOID -LogRemoveDisposition( - IN PLOG_DISPOSITION Disposition - ); - extern NTSTATUS LogInitialize( VOID diff --git a/src/xenbus/console.c b/src/xenbus/console.c new file mode 100644 index 0000000..36e113e --- /dev/null +++ b/src/xenbus/console.c @@ -0,0 +1,820 @@ +/* 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 <ntddk.h> +#include <ntstrsafe.h> +#include <stdarg.h> +#include <stdlib.h> +#include <xen.h> + +#include "console.h" +#include "evtchn.h" +#include "fdo.h" +#include "high.h" +#include "dbg_print.h" +#include "assert.h" +#include "util.h" + +typedef struct _XENBUS_CONSOLE_BUFFER { + LIST_ENTRY ListEntry; + ULONG Offset; + ULONG Length; + CHAR Data[1]; // Variable length array +} XENBUS_CONSOLE_BUFFER, *PXENBUS_CONSOLE_BUFFER; + +struct _XENBUS_CONSOLE_CONTEXT { + PXENBUS_FDO Fdo; + KSPIN_LOCK Lock; + LONG References; + struct xencons_interface *Shared; + LIST_ENTRY OutList; + HIGH_LOCK OutLock; + KDPC Dpc; + ULONG Polls; + ULONG Dpcs; + ULONG Events; + XENBUS_EVTCHN_INTERFACE EvtchnInterface; + PHYSICAL_ADDRESS Address; + PXENBUS_EVTCHN_CHANNEL Channel; + XENBUS_SUSPEND_INTERFACE SuspendInterface; + XENBUS_DEBUG_INTERFACE DebugInterface; + PXENBUS_SUSPEND_CALLBACK SuspendCallbackEarly; + PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate; + PXENBUS_DEBUG_CALLBACK DebugCallback; + BOOLEAN Enabled; +}; + +C_ASSERT(sizeof (struct xencons_interface) <= PAGE_SIZE); + +#define XENBUS_CONSOLE_TAG 'SNOC' + +static FORCEINLINE PVOID +__ConsoleAllocate( + IN ULONG Length + ) +{ + return __AllocatePoolWithTag(NonPagedPool, Length, XENBUS_CONSOLE_TAG); +} + +static FORCEINLINE VOID +__ConsoleFree( + IN PVOID Buffer + ) +{ + __FreePoolWithTag(Buffer, XENBUS_CONSOLE_TAG); +} + +static ULONG +ConsoleCopyToRing( + IN PXENBUS_CONSOLE_CONTEXT Context, + IN PCHAR Data, + IN ULONG Length + ) +{ + struct xencons_interface *Shared; + XENCONS_RING_IDX cons; + XENCONS_RING_IDX prod; + ULONG Offset; + + Shared = Context->Shared; + + KeMemoryBarrier(); + + prod = Shared->out_prod; + cons = Shared->out_cons; + + KeMemoryBarrier(); + + Offset = 0; + while (Length != 0) { + ULONG Available; + ULONG Index; + ULONG CopyLength; + + Available = cons + sizeof (Shared->out) - prod; + + if (Available == 0) + break; + + Index = MASK_XENCONS_IDX(prod, Shared->out); + + CopyLength = __min(Length, Available); + CopyLength = __min(CopyLength, sizeof (Shared->out) - Index); + + RtlCopyMemory(&Shared->out[Index], Data + Offset, CopyLength); + + Offset += CopyLength; + Length -= CopyLength; + + prod += CopyLength; + } + + KeMemoryBarrier(); + + Shared->out_prod = prod; + + KeMemoryBarrier(); + + return Offset; +} + +static FORCEINLINE PXENBUS_CONSOLE_BUFFER +__ConsoleGetHeadBuffer( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + PLIST_ENTRY ListEntry; + PXENBUS_CONSOLE_BUFFER Buffer; + KIRQL Irql; + + AcquireHighLock(&Context->OutLock, &Irql); + ListEntry = (!IsListEmpty(&Context->OutList)) ? + Context->OutList.Flink : + NULL; + ReleaseHighLock(&Context->OutLock, Irql); + + if (ListEntry == NULL) + return NULL; + + Buffer = CONTAINING_RECORD(ListEntry, + XENBUS_CONSOLE_BUFFER, + ListEntry); + + return Buffer; +} + +static FORCEINLINE VOID +__ConsoleRemoveHeadBuffer( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + KIRQL Irql; + + AcquireHighLock(&Context->OutLock, &Irql); + (VOID) RemoveHeadList(&Context->OutList); + ReleaseHighLock(&Context->OutLock, Irql); +} + +static FORCEINLINE VOID +__ConsoleAddTailBuffer( + IN PXENBUS_CONSOLE_CONTEXT Context, + IN PXENBUS_CONSOLE_BUFFER Buffer + ) +{ + KIRQL Irql; + + AcquireHighLock(&Context->OutLock, &Irql); + InsertTailList(&Context->OutList, &Buffer->ListEntry); + ReleaseHighLock(&Context->OutLock, Irql); +} + +static BOOLEAN +ConsoleOut( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + PXENBUS_CONSOLE_BUFFER Buffer; + ULONG Written; + + Buffer = __ConsoleGetHeadBuffer(Context); + + if (Buffer == NULL) + return FALSE; + + Written = ConsoleCopyToRing(Context, + Buffer->Data + Buffer->Offset, + Buffer->Length - Buffer->Offset); + + Buffer->Offset += Written; + + ASSERT3U(Buffer->Offset, <=, Buffer->Length); + if (Buffer->Offset == Buffer->Length) { + __ConsoleRemoveHeadBuffer(Context); + __ConsoleFree(Buffer); + } + + if (Written != 0) + XENBUS_EVTCHN(Send, + &Context->EvtchnInterface, + Context->Channel); + + return TRUE; +} + +static VOID +ConsoleFlush( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + for (;;) { + if (!ConsoleOut(Context)) + break; + } +} + +static +_Function_class_(KDEFERRED_ROUTINE) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_min_(DISPATCH_LEVEL) +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID +ConsoleDpc( + IN PKDPC Dpc, + IN PVOID _Context, + IN PVOID Argument1, + IN PVOID Argument2 + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = _Context; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(Argument1); + UNREFERENCED_PARAMETER(Argument2); + + ASSERT(Context != NULL); + + KeAcquireSpinLockAtDpcLevel(&Context->Lock); + if (Context->References != 0) + (VOID) ConsoleOut(Context); + KeReleaseSpinLockFromDpcLevel(&Context->Lock); +} + +static +_Function_class_(KSERVICE_ROUTINE) +_IRQL_requires_(HIGH_LEVEL) +_IRQL_requires_same_ +BOOLEAN +ConsoleEvtchnCallback( + IN PKINTERRUPT InterruptObject, + IN PVOID Argument + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Argument; + + UNREFERENCED_PARAMETER(InterruptObject); + + ASSERT(Context != NULL); + + Context->Events++; + + if (KeInsertQueueDpc(&Context->Dpc, NULL, NULL)) + Context->Dpcs++; + + return TRUE; +} + +static VOID +ConsoleDisable( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + LogPrintf(LOG_LEVEL_INFO, + "CONSOLE: DISABLE\n"); + + Context->Enabled = FALSE; + + XENBUS_EVTCHN(Close, + &Context->EvtchnInterface, + Context->Channel); + Context->Channel = NULL; +} + +static VOID +ConsoleEnable( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + ULONGLONG Value; + ULONG Port; + NTSTATUS status; + + status = HvmGetParam(HVM_PARAM_CONSOLE_EVTCHN, &Value); + ASSERT(NT_SUCCESS(status)); + + Port = (ULONG)Value; + + Context->Channel = XENBUS_EVTCHN(Open, + &Context->EvtchnInterface, + XENBUS_EVTCHN_TYPE_FIXED, + ConsoleEvtchnCallback, + Context, + Port, + FALSE); + ASSERT(Context->Channel != NULL); + + XENBUS_EVTCHN(Unmask, + &Context->EvtchnInterface, + Context->Channel, + FALSE); + + Context->Enabled = TRUE; + + LogPrintf(LOG_LEVEL_INFO, + "CONSOLE: ENABLE (%u)\n", + Port); + + // Trigger an initial poll + if (KeInsertQueueDpc(&Context->Dpc, NULL, NULL)) + Context->Dpcs++; +} + +static PHYSICAL_ADDRESS +ConsoleGetAddress( + PXENBUS_CONSOLE_CONTEXT Context + ) +{ + PHYSICAL_ADDRESS Address; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Context); + + status = HvmGetParam(HVM_PARAM_CONSOLE_PFN, + (PULONGLONG)&Address.QuadPart); + ASSERT(NT_SUCCESS(status)); + + Address.QuadPart <<= PAGE_SHIFT; + + LogPrintf(LOG_LEVEL_INFO, + "CONSOLE: PAGE @ %08x.%08x\n", + Address.HighPart, + Address.LowPart); + + return Address; +} + +static VOID +ConsoleSuspendCallbackEarly( + IN PVOID Argument + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Argument; + PHYSICAL_ADDRESS Address; + + Address = ConsoleGetAddress(Context); + ASSERT3U(Address.QuadPart, ==, Context->Address.QuadPart); +} + +static VOID +ConsoleSuspendCallbackLate( + IN PVOID Argument + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Argument; + struct xencons_interface *Shared; + KIRQL Irql; + + Shared = Context->Shared; + + KeAcquireSpinLock(&Context->Lock, &Irql); + + ConsoleDisable(Context); + ConsoleEnable(Context); + + KeReleaseSpinLock(&Context->Lock, Irql); +} + +static VOID +ConsoleDebugCallback( + IN PVOID Argument, + IN BOOLEAN Crashing + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Argument; + + XENBUS_DEBUG(Printf, + &Context->DebugInterface, + "Address = %08x.%08x\n", + Context->Address.HighPart, + Context->Address.LowPart); + + if (!Crashing) { + struct xencons_interface *Shared; + + Shared = Context->Shared; + + XENBUS_DEBUG(Printf, + &Context->DebugInterface, + "out_cons = %08x out_prod = %08x\n", + Shared->out_cons, + Shared->out_prod); + + XENBUS_DEBUG(Printf, + &Context->DebugInterface, + "in_cons = %08x in_prod = %08x\n", + Shared->in_cons, + Shared->in_prod); + } + + XENBUS_DEBUG(Printf, + &Context->DebugInterface, + "Events = %lu Dpcs = %lu Polls = %lu\n", + Context->Events, + Context->Dpcs, + Context->Polls); +} + +static NTSTATUS +ConsoleWrite( + IN PINTERFACE Interface, + IN PCHAR Data, + IN ULONG Length, + IN BOOLEAN CRLF + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Interface->Context; + ULONG Extra; + PXENBUS_CONSOLE_BUFFER Buffer; + ULONG Index; + KIRQL Irql; + NTSTATUS status; + + Extra = 0; + if (CRLF) { + for (Index = 0; Index < Length; Index++) { + if (Data[Index] == '\n') + Extra++; + } + } + + Buffer = __ConsoleAllocate(FIELD_OFFSET(XENBUS_CONSOLE_BUFFER, Data) + + Length + Extra); + + status = STATUS_NO_MEMORY; + if (Buffer == NULL) + goto fail1; + + Index = 0; + while (Index < Length + Extra) { + if (CRLF && *Data == '\n') + Buffer->Data[Index++] = '\r'; + + Buffer->Data[Index++] = *Data++; + } + + Buffer->Length = Length + Extra; + + AcquireHighLock(&Context->OutLock, &Irql); + InsertTailList(&Context->OutList, &Buffer->ListEntry); + ReleaseHighLock(&Context->OutLock, Irql); + + if (KeInsertQueueDpc(&Context->Dpc, NULL, NULL)) + Context->Dpcs++; + + return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +static NTSTATUS +ConsoleAcquire( + IN PINTERFACE Interface + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Interface->Context; + KIRQL Irql; + NTSTATUS status; + + KeAcquireSpinLock(&Context->Lock, &Irql); + + if (Context->References++ != 0) + goto done; + + Trace("====>\n"); + + Context->Address = ConsoleGetAddress(Context); + Context->Shared = (struct xencons_interface *)MmMapIoSpace(Context->Address, + PAGE_SIZE, + MmCached); + status = STATUS_UNSUCCESSFUL; + if (Context->Shared == NULL) + goto fail1; + + status = XENBUS_EVTCHN(Acquire, &Context->EvtchnInterface); + if (!NT_SUCCESS(status)) + goto fail2; + + ConsoleEnable(Context); + + status = XENBUS_SUSPEND(Acquire, &Context->SuspendInterface); + if (!NT_SUCCESS(status)) + goto fail3; + + status = XENBUS_SUSPEND(Register, + &Context->SuspendInterface, + SUSPEND_CALLBACK_EARLY, + ConsoleSuspendCallbackEarly, + Context, + &Context->SuspendCallbackEarly); + if (!NT_SUCCESS(status)) + goto fail4; + + status = XENBUS_SUSPEND(Register, + &Context->SuspendInterface, + SUSPEND_CALLBACK_LATE, + ConsoleSuspendCallbackLate, + Context, + &Context->SuspendCallbackLate); + if (!NT_SUCCESS(status)) + goto fail5; + + status = XENBUS_DEBUG(Acquire, &Context->DebugInterface); + if (!NT_SUCCESS(status)) + goto fail6; + + status = XENBUS_DEBUG(Register, + &Context->DebugInterface, + __MODULE__ "|CONSOLE", + ConsoleDebugCallback, + Context, + &Context->DebugCallback); + if (!NT_SUCCESS(status)) + goto fail7; + + Trace("<====\n"); + +done: + KeReleaseSpinLock(&Context->Lock, Irql); + + return STATUS_SUCCESS; + +fail7: + Error("fail7\n"); + + XENBUS_DEBUG(Release, &Context->DebugInterface); + +fail6: + Error("fail6\n"); + + XENBUS_SUSPEND(Deregister, + &Context->SuspendInterface, + Context->SuspendCallbackLate); + Context->SuspendCallbackLate = NULL; + +fail5: + Error("fail5\n"); + + XENBUS_SUSPEND(Deregister, + &Context->SuspendInterface, + Context->SuspendCallbackEarly); + Context->SuspendCallbackEarly = NULL; + +fail4: + Error("fail4\n"); + + XENBUS_SUSPEND(Release, &Context->SuspendInterface); + +fail3: + Error("fail3\n"); + + ConsoleDisable(Context); + + XENBUS_EVTCHN(Release, &Context->EvtchnInterface); + +fail2: + Error("fail2\n"); + + MmUnmapIoSpace(Context->Shared, PAGE_SIZE); + Context->Shared = NULL; + +fail1: + Error("fail1 (%08x)\n", status); + + Context->Address.QuadPart = 0; + + --Context->References; + ASSERT3U(Context->References, ==, 0); + KeReleaseSpinLock(&Context->Lock, Irql); + + return status; +} + +static VOID +ConsoleRelease( + IN PINTERFACE Interface + ) +{ + PXENBUS_CONSOLE_CONTEXT Context = Interface->Context; + KIRQL Irql; + + KeAcquireSpinLock(&Context->Lock, &Irql); + + if (--Context->References > 0) + goto done; + + Trace("====>\n"); + + ConsoleFlush(Context); + + XENBUS_DEBUG(Deregister, + &Context->DebugInterface, + Context->DebugCallback); + Context->DebugCallback = NULL; + + XENBUS_DEBUG(Release, &Context->DebugInterface); + + XENBUS_SUSPEND(Deregister, + &Context->SuspendInterface, + Context->SuspendCallbackLate); + Context->SuspendCallbackLate = NULL; + + XENBUS_SUSPEND(Deregister, + &Context->SuspendInterface, + Context->SuspendCallbackEarly); + Context->SuspendCallbackEarly = NULL; + + XENBUS_SUSPEND(Release, &Context->SuspendInterface); + + ConsoleDisable(Context); + + XENBUS_EVTCHN(Release, &Context->EvtchnInterface); + + MmUnmapIoSpace(Context->Shared, PAGE_SIZE); + Context->Shared = NULL; + + Context->Address.QuadPart = 0; + + Trace("<====\n"); + +done: + KeReleaseSpinLock(&Context->Lock, Irql); +} + +static struct _XENBUS_CONSOLE_INTERFACE_V1 ConsoleInterfaceVersion1 = { + { sizeof (struct _XENBUS_CONSOLE_INTERFACE_V1), 1, NULL, NULL, NULL }, + ConsoleAcquire, + ConsoleRelease, + ConsoleWrite +}; + +NTSTATUS +ConsoleInitialize( + IN PXENBUS_FDO Fdo, + OUT PXENBUS_CONSOLE_CONTEXT *Context + ) +{ + NTSTATUS status; + + Trace("====>\n"); + + *Context = __ConsoleAllocate(sizeof (XENBUS_CONSOLE_CONTEXT)); + + status = STATUS_NO_MEMORY; + if (*Context == NULL) + goto fail1; + + status = EvtchnGetInterface(FdoGetEvtchnContext(Fdo), + XENBUS_EVTCHN_INTERFACE_VERSION_MAX, + (PINTERFACE)&(*Context)->EvtchnInterface, + sizeof ((*Context)->EvtchnInterface)); + ASSERT(NT_SUCCESS(status)); + ASSERT((*Context)->EvtchnInterface.Interface.Context != NULL); + + status = SuspendGetInterface(FdoGetSuspendContext(Fdo), + XENBUS_SUSPEND_INTERFACE_VERSION_MAX, + (PINTERFACE)&(*Context)->SuspendInterface, + sizeof ((*Context)->SuspendInterface)); + ASSERT(NT_SUCCESS(status)); + ASSERT((*Context)->SuspendInterface.Interface.Context != NULL); + + status = DebugGetInterface(FdoGetDebugContext(Fdo), + XENBUS_DEBUG_INTERFACE_VERSION_MAX, + (PINTERFACE)&(*Context)->DebugInterface, + sizeof ((*Context)->DebugInterface)); + ASSERT(NT_SUCCESS(status)); + ASSERT((*Context)->DebugInterface.Interface.Context != NULL); + + KeInitializeSpinLock(&(*Context)->Lock); + + InitializeListHead(&(*Context)->OutList); + InitializeHighLock(&(*Context)->OutLock); + + KeInitializeDpc(&(*Context)->Dpc, ConsoleDpc, *Context); + + (*Context)->Fdo = Fdo; + + Trace("<====\n"); + + return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +NTSTATUS +ConsoleGetInterface( + IN PXENBUS_CONSOLE_CONTEXT Context, + IN ULONG Version, + IN OUT PINTERFACE Interface, + IN ULONG Size + ) +{ + NTSTATUS status; + + ASSERT(Context != NULL); + + switch (Version) { + case 1: { + struct _XENBUS_CONSOLE_INTERFACE_V1 *ConsoleInterface; + + ConsoleInterface = (struct _XENBUS_CONSOLE_INTERFACE_V1 *)Interface; + + status = STATUS_BUFFER_OVERFLOW; + if (Size < sizeof (struct _XENBUS_CONSOLE_INTERFACE_V1)) + break; + + *ConsoleInterface = ConsoleInterfaceVersion1; + + ASSERT3U(Interface->Version, ==, Version); + Interface->Context = Context; + + status = STATUS_SUCCESS; + break; + } + default: + status = STATUS_NOT_SUPPORTED; + break; + } + + return status; +} + +ULONG +ConsoleGetReferences( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + return Context->References; +} + +VOID +ConsoleTeardown( + IN PXENBUS_CONSOLE_CONTEXT Context + ) +{ + Trace("====>\n"); + + ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL); + KeFlushQueuedDpcs(); + + Context->Polls = 0; + Context->Dpcs = 0; + Context->Events = 0; + + Context->Fdo = NULL; + + RtlZeroMemory(&Context->Dpc, sizeof (KDPC)); + + RtlZeroMemory(&Context->OutLock, sizeof (HIGH_LOCK)); + ASSERT(IsListEmpty(&Context->OutList)); + RtlZeroMemory(&Context->OutList, sizeof (LIST_ENTRY)); + + RtlZeroMemory(&Context->Lock, sizeof (KSPIN_LOCK)); + + RtlZeroMemory(&Context->DebugInterface, + sizeof (XENBUS_DEBUG_INTERFACE)); + + RtlZeroMemory(&Context->SuspendInterface, + sizeof (XENBUS_SUSPEND_INTERFACE)); + + RtlZeroMemory(&Context->EvtchnInterface, + sizeof (XENBUS_EVTCHN_INTERFACE)); + + ASSERT(IsZeroMemory(Context, sizeof (XENBUS_CONSOLE_CONTEXT))); + __ConsoleFree(Context); + + Trace("<====\n"); +} diff --git a/src/xenbus/console.h b/src/xenbus/console.h new file mode 100644 index 0000000..87be7ec --- /dev/null +++ b/src/xenbus/console.h @@ -0,0 +1,67 @@ +/* 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 _XENBUS_CONSOLE_H +#define _XENBUS_CONSOLE_H + +#include <ntddk.h> +#include <xen.h> +#include <console_interface.h> + +typedef struct _XENBUS_CONSOLE_CONTEXT XENBUS_CONSOLE_CONTEXT, *PXENBUS_CONSOLE_CONTEXT; + +#include "fdo.h" + +extern NTSTATUS +ConsoleInitialize( + IN PXENBUS_FDO Fdo, + OUT PXENBUS_CONSOLE_CONTEXT *Context + ); + +extern NTSTATUS +ConsoleGetInterface( + IN PXENBUS_CONSOLE_CONTEXT Context, + IN ULONG Version, + IN OUT PINTERFACE Interface, + IN ULONG Size + ); + +extern ULONG +ConsoleGetReferences( + IN PXENBUS_CONSOLE_CONTEXT Context + ); + +extern VOID +ConsoleTeardown( + IN PXENBUS_CONSOLE_CONTEXT Context + ); + +#endif // _XENBUS_CONSOLE_H diff --git a/src/xenbus/fdo.c b/src/xenbus/fdo.c index 7e68b36..41d789b 100644 --- a/src/xenbus/fdo.c +++ b/src/xenbus/fdo.c @@ -51,6 +51,7 @@ #include "evtchn.h" #include "debug.h" #include "store.h" +#include "console.h" #include "cache.h" #include "gnttab.h" #include "suspend.h" @@ -122,6 +123,7 @@ struct _XENBUS_FDO { PXENBUS_EVTCHN_CONTEXT EvtchnContext; PXENBUS_DEBUG_CONTEXT DebugContext; PXENBUS_STORE_CONTEXT StoreContext; + PXENBUS_CONSOLE_CONTEXT ConsoleContext; PXENBUS_RANGE_SET_CONTEXT RangeSetContext; PXENBUS_CACHE_CONTEXT CacheContext; PXENBUS_GNTTAB_CONTEXT GnttabContext; @@ -132,6 +134,7 @@ struct _XENBUS_FDO { XENBUS_SUSPEND_INTERFACE SuspendInterface; XENBUS_EVTCHN_INTERFACE EvtchnInterface; XENBUS_STORE_INTERFACE StoreInterface; + XENBUS_CONSOLE_INTERFACE ConsoleInterface; XENBUS_RANGE_SET_INTERFACE RangeSetInterface; XENBUS_BALLOON_INTERFACE BalloonInterface; @@ -140,6 +143,7 @@ struct _XENBUS_FDO { PXENBUS_EVTCHN_CHANNEL Channel; PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate; + PLOG_DISPOSITION LogDisposition; }; static FORCEINLINE PVOID @@ -785,6 +789,7 @@ DEFINE_FDO_GET_CONTEXT(SharedInfo, PXENBUS_SHARED_INFO_CONTEXT) DEFINE_FDO_GET_CONTEXT(Evtchn, PXENBUS_EVTCHN_CONTEXT) DEFINE_FDO_GET_CONTEXT(Debug, PXENBUS_DEBUG_CONTEXT) DEFINE_FDO_GET_CONTEXT(Store, PXENBUS_STORE_CONTEXT) +DEFINE_FDO_GET_CONTEXT(Console, PXENBUS_CONSOLE_CONTEXT) DEFINE_FDO_GET_CONTEXT(RangeSet, PXENBUS_RANGE_SET_CONTEXT) DEFINE_FDO_GET_CONTEXT(Cache, PXENBUS_CACHE_CONTEXT) DEFINE_FDO_GET_CONTEXT(Gnttab, PXENBUS_GNTTAB_CONTEXT) @@ -2608,6 +2613,22 @@ fail1: return status; } +static VOID +FdoOutputBuffer( + IN PVOID Argument, + IN PCHAR Buffer, + IN ULONG Length + ) +{ + PXENBUS_FDO Fdo = Argument; + + (VOID) XENBUS_CONSOLE(Write, + &Fdo->ConsoleInterface, + Buffer, + Length, + TRUE); +} + static FORCEINLINE NTSTATUS __FdoD3ToD0( IN PXENBUS_FDO Fdo @@ -2637,6 +2658,15 @@ __FdoD3ToD0( Fdo->Channel, FALSE); + status = LogAddDisposition(LOG_LEVEL_INFO | + LOG_LEVEL_WARNING | + LOG_LEVEL_ERROR | + LOG_LEVEL_CRITICAL, + FdoOutputBuffer, + Fdo, + &Fdo->LogDisposition); + ASSERT(NT_SUCCESS(status)); + status = XENBUS_STORE(WatchAdd, &Fdo->StoreInterface, NULL, @@ -2711,6 +2741,9 @@ fail3: fail2: Error("fail2\n"); + LogRemoveDisposition(Fdo->LogDisposition); + Fdo->LogDisposition = NULL; + XENBUS_EVTCHN(Close, &Fdo->EvtchnInterface, Fdo->Channel); @@ -2760,6 +2793,9 @@ __FdoD0ToD3( Fdo->ScanWatch); Fdo->ScanWatch = NULL; + LogRemoveDisposition(Fdo->LogDisposition); + Fdo->LogDisposition = NULL; + XENBUS_EVTCHN(Close, &Fdo->EvtchnInterface, Fdo->Channel); @@ -2975,15 +3011,19 @@ FdoD3ToD0( if (!NT_SUCCESS(status)) goto fail6; + status = XENBUS_CONSOLE(Acquire, &Fdo->ConsoleInterface); + if (!NT_SUCCESS(status)) + goto fail7; + if (Fdo->BalloonInterface.Interface.Context != NULL) { status = XENBUS_BALLOON(Acquire, &Fdo->BalloonInterface); if (!NT_SUCCESS(status)) - goto fail7; + goto fail8; } status = __FdoD3ToD0(Fdo); if (!NT_SUCCESS(status)) - goto fail8; + goto fail9; status = XENBUS_SUSPEND(Register, &Fdo->SuspendInterface, @@ -2992,7 +3032,7 @@ FdoD3ToD0( Fdo, &Fdo->SuspendCallbackLate); if (!NT_SUCCESS(status)) - goto fail9; + goto fail10; KeLowerIrql(Irql); @@ -3023,16 +3063,21 @@ not_active: return STATUS_SUCCESS; +fail10: + Error("fail10\n"); + + __FdoD0ToD3(Fdo); + fail9: Error("fail9\n"); - __FdoD0ToD3(Fdo); + if (Fdo->BalloonInterface.Interface.Context != NULL) + XENBUS_BALLOON(Release, &Fdo->BalloonInterface); fail8: Error("fail8\n"); - if (Fdo->BalloonInterface.Interface.Context != NULL) - XENBUS_BALLOON(Release, &Fdo->BalloonInterface); + XENBUS_CONSOLE(Release, &Fdo->ConsoleInterface); fail7: Error("fail7\n"); @@ -3156,6 +3201,8 @@ FdoD0ToD3( if (Fdo->BalloonInterface.Interface.Context != NULL) XENBUS_BALLOON(Release, &Fdo->BalloonInterface); + XENBUS_CONSOLE(Release, &Fdo->ConsoleInterface); + XENBUS_STORE(Release, &Fdo->StoreInterface); XENBUS_EVTCHN(Release, &Fdo->EvtchnInterface); @@ -3224,6 +3271,7 @@ FdoS3ToS4( BUG_ON(SharedInfoGetReferences(Fdo->SharedInfoContext) != 0); BUG_ON(EvtchnGetReferences(Fdo->EvtchnContext) != 0); BUG_ON(StoreGetReferences(Fdo->StoreContext) != 0); + BUG_ON(ConsoleGetReferences(Fdo->ConsoleContext) != 0); BUG_ON(GnttabGetReferences(Fdo->GnttabContext) != 0); BUG_ON(BalloonGetReferences(Fdo->BalloonContext) != 0); @@ -5090,26 +5138,30 @@ FdoCreate( if (!NT_SUCCESS(status)) goto fail13; - status = RangeSetInitialize(Fdo, &Fdo->RangeSetContext); + status = ConsoleInitialize(Fdo, &Fdo->ConsoleContext); if (!NT_SUCCESS(status)) goto fail14; - status = CacheInitialize(Fdo, &Fdo->CacheContext); + status = RangeSetInitialize(Fdo, &Fdo->RangeSetContext); if (!NT_SUCCESS(status)) goto fail15; - status = GnttabInitialize(Fdo, &Fdo->GnttabContext); + status = CacheInitialize(Fdo, &Fdo->CacheContext); if (!NT_SUCCESS(status)) goto fail16; - status = UnplugInitialize(Fdo, &Fdo->UnplugContext); + status = GnttabInitialize(Fdo, &Fdo->GnttabContext); if (!NT_SUCCESS(status)) goto fail17; + status = UnplugInitialize(Fdo, &Fdo->UnplugContext); + if (!NT_SUCCESS(status)) + goto fail18; + if (FdoIsBalloonEnabled(Fdo)) { status = BalloonInitialize(Fdo, &Fdo->BalloonContext); if (!NT_SUCCESS(status)) - goto fail18; + goto fail19; } status = DebugGetInterface(__FdoGetDebugContext(Fdo), @@ -5140,6 +5192,13 @@ FdoCreate( ASSERT(NT_SUCCESS(status)); ASSERT(Fdo->StoreInterface.Interface.Context != NULL); + status = ConsoleGetInterface(__FdoGetConsoleContext(Fdo), + XENBUS_CONSOLE_INTERFACE_VERSION_MAX, + (PINTERFACE)&Fdo->ConsoleInterface, + sizeof (Fdo->ConsoleInterface)); + ASSERT(NT_SUCCESS(status)); + ASSERT(Fdo->ConsoleInterface.Interface.Context != NULL); + status = RangeSetGetInterface(__FdoGetRangeSetContext(Fdo), XENBUS_RANGE_SET_INTERFACE_VERSION_MAX, (PINTERFACE)&Fdo->RangeSetInterface, @@ -5172,30 +5231,36 @@ done: return STATUS_SUCCESS; -fail18: - Error("fail18\n"); +fail19: + Error("fail19\n"); UnplugTeardown(Fdo->UnplugContext); Fdo->UnplugContext = NULL; -fail17: - Error("fail17\n"); +fail18: + Error("fail18\n"); GnttabTeardown(Fdo->GnttabContext); Fdo->GnttabContext = NULL; -fail16: - Error("fail16\n"); +fail17: + Error("fail17\n"); CacheTeardown(Fdo->CacheContext); Fdo->CacheContext = NULL; -fail15: - Error("fail15\n"); +fail16: + Error("fail16\n"); RangeSetTeardown(Fdo->RangeSetContext); Fdo->RangeSetContext = NULL; +fail15: + Error("fail15\n"); + + ConsoleTeardown(Fdo->ConsoleContext); + Fdo->ConsoleContext = NULL; + fail14: Error("fail14\n"); @@ -5318,6 +5383,9 @@ FdoDestroy( RtlZeroMemory(&Fdo->RangeSetInterface, sizeof (XENBUS_RANGE_SET_INTERFACE)); + RtlZeroMemory(&Fdo->ConsoleInterface, + sizeof (XENBUS_CONSOLE_INTERFACE)); + RtlZeroMemory(&Fdo->StoreInterface, sizeof (XENBUS_STORE_INTERFACE)); @@ -5347,6 +5415,9 @@ FdoDestroy( RangeSetTeardown(Fdo->RangeSetContext); Fdo->RangeSetContext = NULL; + ConsoleTeardown(Fdo->ConsoleContext); + Fdo->ConsoleContext = NULL; + StoreTeardown(Fdo->StoreContext); Fdo->StoreContext = NULL; diff --git a/vs2012/xenbus/xenbus.vcxproj b/vs2012/xenbus/xenbus.vcxproj index 2cffb9f..5bb3be9 100644 --- a/vs2012/xenbus/xenbus.vcxproj +++ b/vs2012/xenbus/xenbus.vcxproj @@ -68,6 +68,7 @@ <ItemGroup> <ClCompile Include="..\..\src\common\registry.c" /> <ClCompile Include="..\..\src\xenbus\bus.c" /> + <ClCompile Include="..\..\src\xenbus\console.c" /> <ClCompile Include="..\..\src\xenbus\dma.c" /> <ClCompile Include="..\..\src\xenbus\debug.c" /> <ClCompile Include="..\..\src\xenbus\driver.c" /> diff --git a/vs2013/xenbus/xenbus.vcxproj b/vs2013/xenbus/xenbus.vcxproj index 396245f..0856ddc 100644 --- a/vs2013/xenbus/xenbus.vcxproj +++ b/vs2013/xenbus/xenbus.vcxproj @@ -71,6 +71,7 @@ <ItemGroup> <ClCompile Include="..\..\src\common\registry.c" /> <ClCompile Include="..\..\src\xenbus\bus.c" /> + <ClCompile Include="..\..\src\xenbus\console.c" /> <ClCompile Include="..\..\src\xenbus\dma.c" /> <ClCompile Include="..\..\src\xenbus\debug.c" /> <ClCompile Include="..\..\src\xenbus\driver.c" /> diff --git a/vs2015/xenbus/xenbus.vcxproj b/vs2015/xenbus/xenbus.vcxproj index 7fbc7bd..038d3d7 100644 --- a/vs2015/xenbus/xenbus.vcxproj +++ b/vs2015/xenbus/xenbus.vcxproj @@ -66,6 +66,7 @@ <ItemGroup> <ClCompile Include="..\..\src\common\registry.c" /> <ClCompile Include="..\..\src\xenbus\bus.c" /> + <ClCompile Include="..\..\src\xenbus\console.c" /> <ClCompile Include="..\..\src\xenbus\dma.c" /> <ClCompile Include="..\..\src\xenbus\debug.c" /> <ClCompile Include="..\..\src\xenbus\driver.c" /> -- 2.5.3 _______________________________________________ win-pv-devel mailing list win-pv-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/cgi-bin/mailman/listinfo/win-pv-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |