#include "driver.h"
#include "frontend.h"
+#include "ring.h"
#include "thread.h"
#include "dbg_print.h"
#include "assert.h"
PXENBUS_SUSPEND_CALLBACK SuspendCallback;
PXENBUS_DEBUG_CALLBACK DebugCallback;
PXENBUS_STORE_WATCH Watch;
+
+ PXENCONS_RING Ring;
};
static const PCHAR
if (!FrontendIsOnline(Frontend))
break;
- FrontendSetXenbusState(Frontend,
- XenbusStateClosed);
-
FrontendWaitForBackendXenbusStateChange(Frontend,
&State);
+
+ switch (State) {
+ case XenbusStateClosing:
+ FrontendSetXenbusState(Frontend,
+ XenbusStateClosed);
+ break;
+ case XenbusStateClosed:
+ break;
+ default:
+ FrontendSetXenbusState(Frontend,
+ XenbusStateClosing);
+ break;
+ }
}
FrontendReleaseBackend(Frontend);
if (!FrontendIsOnline(Frontend))
break;
- FrontendSetXenbusState(Frontend,
- XenbusStateInitialising);
-
FrontendWaitForBackendXenbusStateChange(Frontend,
&State);
+ switch (State) {
+ case XenbusStateInitWait:
+ break;
+ case XenbusStateClosed:
+ FrontendSetXenbusState(Frontend,
+ XenbusStateClosed);
+ break;
+ default:
+ FrontendSetXenbusState(Frontend,
+ XenbusStateInitialising);
+ break;
+ }
}
status = STATUS_UNSUCCESSFUL;
if (!NT_SUCCESS(status))
goto fail2;
- // TODO: Connect Ring
+ status = RingConnect(Frontend->Ring);
+ if (!NT_SUCCESS(status))
+ goto fail3;
Attempt = 0;
do {
if (!NT_SUCCESS(status))
break;
- // TODO: StoreWrite Ring
+ status = RingStoreWrite(Frontend->Ring,
+ Transaction);
+ if (!NT_SUCCESS(status))
+ goto abort;
status = XENBUS_STORE(TransactionEnd,
&Frontend->StoreInterface,
continue;
- //abort:
- // (VOID)XENBUS_STORE(TransactionEnd,
- // &Frontend->StoreInterface,
- // Transaction,
- // FALSE);
- // break;
+ abort:
+ (VOID)XENBUS_STORE(TransactionEnd,
+ &Frontend->StoreInterface,
+ Transaction,
+ FALSE);
+ break;
} while (status == STATUS_RETRY);
if (!NT_SUCCESS(status))
if (!FrontendIsOnline(Frontend))
break;
- FrontendSetXenbusState(Frontend,
- XenbusStateConnected);
-
FrontendWaitForBackendXenbusStateChange(Frontend,
&State);
+
+ switch (State) {
+ case XenbusStateInitWait:
+ FrontendSetXenbusState(Frontend,
+ XenbusStateConnected);
+ break;
+ case XenbusStateConnected:
+ break;
+ case XenbusStateUnknown:
+ case XenbusStateClosing:
+ case XenbusStateClosed:
+ FrontendSetOffline(Frontend);
+ break;
+ default:
+ break;
+ }
}
status = STATUS_UNSUCCESSFUL;
if (NT_SUCCESS(status)) {
Length = (ULONG)strlen(Buffer);
- Frontend->Name = __FrontendAllocate(Length);
+ Frontend->Name = __FrontendAllocate(Length + 1);
if (Frontend->Name)
RtlCopyMemory(Frontend->Name, Buffer, Length);
if (NT_SUCCESS(status)) {
Length = (ULONG)strlen(Buffer);
- Frontend->Protocol = __FrontendAllocate(Length);
+ Frontend->Protocol = __FrontendAllocate(Length + 1);
if (Frontend->Protocol)
RtlCopyMemory(Frontend->Protocol, Buffer, Length);
fail4:
Error("fail4\n");
-//fail3:
+ RingDisconnect(Frontend->Ring);
+
+fail3:
Error("fail3\n");
+ XENBUS_DEBUG(Deregister,
+ &Frontend->DebugInterface,
+ Frontend->DebugCallback);
+ Frontend->DebugCallback = NULL;
+
fail2:
Error("fail2\n");
+ XENBUS_DEBUG(Release, &Frontend->DebugInterface);
+
fail1:
Error("fail1 (%08x)\n", status);
__FrontendFree(Frontend->Name);
Frontend->Name = NULL;
- // TODO: Disconnect Ring
+ RingDisconnect(Frontend->Ring);
XENBUS_DEBUG(Deregister,
&Frontend->DebugInterface,
IN PXENCONS_FRONTEND Frontend
)
{
+ NTSTATUS status;
+
Trace("====>\n");
- UNREFERENCED_PARAMETER(Frontend);
- // TODO: Enable Ring
+ status = RingEnable(Frontend->Ring);
+ if (!NT_SUCCESS(status))
+ goto fail1;
Trace("<====\n");
return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
}
static VOID
{
Trace("====>\n");
- UNREFERENCED_PARAMETER(Frontend);
- // TODO: Disable Ring
+ RingDisable(Frontend->Ring);
Trace("<====\n");
}
ASSERT3U(Frontend->State, == , FRONTEND_UNKNOWN);
UNREFERENCED_PARAMETER(Frontend);
+ // Current backends dont like re-opening after being closed
//(VOID)FrontendSetState(Frontend, FRONTEND_CLOSED);
}
ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL);
UNREFERENCED_PARAMETER(Frontend);
- //(VOID)FrontendSetState(Frontend, FRONTEND_UNKNOWN);
+ (VOID)FrontendSetState(Frontend, FRONTEND_UNKNOWN);
}
static DECLSPEC_NOINLINE VOID
IN PFILE_OBJECT FileObject
)
{
- UNREFERENCED_PARAMETER(Context);
- UNREFERENCED_PARAMETER(FileObject);
+ PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context;
- return STATUS_SUCCESS;
+ return RingOpen(Frontend->Ring, FileObject);
}
static NTSTATUS
IN PFILE_OBJECT FileObject
)
{
- UNREFERENCED_PARAMETER(Context);
- UNREFERENCED_PARAMETER(FileObject);
+ PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context;
- return STATUS_SUCCESS;
+ return RingClose(Frontend->Ring, FileObject);
}
static NTSTATUS
switch (StackLocation->MajorFunction) {
case IRP_MJ_READ:
case IRP_MJ_WRITE:
- return STATUS_DEVICE_NOT_READY;
+ return RingPutQueue(Frontend->Ring, Irp);
case IRP_MJ_DEVICE_CONTROL:
return FrontendGetProperty(Frontend, Irp);
FdoGetSuspendInterface(PdoGetFdo(Pdo), &Frontend->SuspendInterface);
FdoGetStoreInterface(PdoGetFdo(Pdo), &Frontend->StoreInterface);
- // TODO: Initialize Ring
+ status = RingCreate(Frontend, &Frontend->Ring);
+ if (!NT_SUCCESS(status))
+ goto fail4;
KeInitializeEvent(&Frontend->EjectEvent, NotificationEvent, FALSE);
RtlZeroMemory(&Frontend->EjectEvent, sizeof(KEVENT));
- // TODO: Teardown Ring
+ RingDestroy(Frontend->Ring);
+ Frontend->Ring = NULL;
-//fail4:
+fail4:
Error("fail4\n");
RtlZeroMemory(&Frontend->StoreInterface,
RtlZeroMemory(&Frontend->EjectEvent, sizeof(KEVENT));
- // TODO: Teardown Ring
+ RingDestroy(Frontend->Ring);
+ Frontend->Ring = NULL;
RtlZeroMemory(&Frontend->StoreInterface,
sizeof(XENBUS_STORE_INTERFACE));
--- /dev/null
+/* 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 <stdlib.h>
+
+#include <xen.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <evtchn_interface.h>
+
+#include "frontend.h"
+#include "ring.h"
+#include "names.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+typedef struct _XENCONS_QUEUE {
+ IO_CSQ Csq;
+ LIST_ENTRY List;
+ KSPIN_LOCK Lock;
+} XENCONS_QUEUE, *PXENCONS_QUEUE;
+
+struct _XENCONS_RING {
+ PXENCONS_FRONTEND Frontend;
+ BOOLEAN Connected;
+ BOOLEAN Enabled;
+ KSPIN_LOCK Lock;
+ PXENBUS_GNTTAB_CACHE GnttabCache;
+ struct xencons_interface *Shared;
+ PMDL Mdl;
+ PXENBUS_GNTTAB_ENTRY Entry;
+ KDPC Dpc;
+ ULONG Dpcs;
+ ULONG Events;
+ PXENBUS_EVTCHN_CHANNEL Channel;
+ XENBUS_GNTTAB_INTERFACE GnttabInterface;
+ XENBUS_STORE_INTERFACE StoreInterface;
+ XENBUS_EVTCHN_INTERFACE EvtchnInterface;
+ XENBUS_DEBUG_INTERFACE DebugInterface;
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+ XENCONS_QUEUE Read;
+ XENCONS_QUEUE Write;
+ ULONG BytesRead;
+ ULONG BytesWritten;
+};
+
+#define MAXNAMELEN 128
+#define XENCONS_RING_TAG 'GNIR'
+
+static FORCEINLINE PVOID
+__RingAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocatePoolWithTag(NonPagedPool, Length, XENCONS_RING_TAG);
+}
+
+static FORCEINLINE VOID
+__RingFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, XENCONS_RING_TAG);
+}
+
+IO_CSQ_INSERT_IRP_EX RingCsqInsertIrpEx;
+
+NTSTATUS
+RingCsqInsertIrpEx(
+ IN PIO_CSQ Csq,
+ IN PIRP Irp,
+ IN PVOID InsertContext OPTIONAL
+ )
+{
+ BOOLEAN ReInsert = (BOOLEAN)(ULONG_PTR)InsertContext;
+ PXENCONS_QUEUE Queue;
+
+ Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq);
+
+ if (ReInsert) {
+ // This only occurs if the DPC de-queued the IRP but
+ // then found the console to be blocked.
+ InsertHeadList(&Queue->List, &Irp->Tail.Overlay.ListEntry);
+ } else {
+ InsertTailList(&Queue->List, &Irp->Tail.Overlay.ListEntry);
+ }
+
+ return STATUS_PENDING;
+}
+
+IO_CSQ_REMOVE_IRP RingCsqRemoveIrp;
+
+VOID
+RingCsqRemoveIrp(
+ IN PIO_CSQ Csq,
+ IN PIRP Irp
+ )
+{
+ UNREFERENCED_PARAMETER(Csq);
+
+ RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+}
+
+IO_CSQ_PEEK_NEXT_IRP RingCsqPeekNextIrp;
+
+PIRP
+RingCsqPeekNextIrp(
+ IN PIO_CSQ Csq,
+ IN PIRP Irp,
+ IN PVOID PeekContext OPTIONAL
+ )
+{
+ PXENCONS_QUEUE Queue;
+ PLIST_ENTRY ListEntry;
+ PIRP NextIrp;
+
+ Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq);
+
+ ListEntry = (Irp == NULL) ?
+ Queue->List.Flink :
+ Irp->Tail.Overlay.ListEntry.Flink;
+
+ while (ListEntry != &Queue->List) {
+ PIO_STACK_LOCATION StackLocation;
+
+ NextIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
+ if (PeekContext == NULL)
+ return NextIrp;
+
+ StackLocation = IoGetCurrentIrpStackLocation(NextIrp);
+ if (StackLocation->FileObject == (PFILE_OBJECT)PeekContext)
+ return NextIrp;
+
+ ListEntry = ListEntry->Flink;
+ }
+
+ return NULL;
+}
+
+#pragma warning(push)
+#pragma warning(disable:28167) // function changes IRQL
+
+IO_CSQ_ACQUIRE_LOCK RingCsqAcquireLock;
+
+VOID
+RingCsqAcquireLock(
+ IN PIO_CSQ Csq,
+ OUT PKIRQL Irql
+ )
+{
+ PXENCONS_QUEUE Queue;
+
+ Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq);
+
+ KeAcquireSpinLock(&Queue->Lock, Irql);
+}
+
+IO_CSQ_RELEASE_LOCK RingCsqReleaseLock;
+
+VOID
+RingCsqReleaseLock(
+ IN PIO_CSQ Csq,
+ IN KIRQL Irql
+ )
+{
+ PXENCONS_QUEUE Queue;
+
+ Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq);
+
+ KeReleaseSpinLock(&Queue->Lock, Irql);
+}
+
+#pragma warning(pop)
+
+IO_CSQ_COMPLETE_CANCELED_IRP RingCsqCompleteCanceledIrp;
+
+VOID
+RingCsqCompleteCanceledIrp(
+ 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("CANCELLED (%02x:%s)\n",
+ MajorFunction,
+ MajorFunctionName(MajorFunction));
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+static FORCEINLINE VOID
+__RingCancelRequests(
+ IN PXENCONS_RING Ring,
+ IN PFILE_OBJECT FileObject
+ )
+{
+ for (;;) {
+ PIRP Irp;
+
+ Irp = IoCsqRemoveNextIrp(&Ring->Read.Csq, FileObject);
+ if (Irp == NULL)
+ break;
+
+ RingCsqCompleteCanceledIrp(&Ring->Read.Csq, Irp);
+ }
+ for (;;) {
+ PIRP Irp;
+
+ Irp = IoCsqRemoveNextIrp(&Ring->Write.Csq, FileObject);
+ if (Irp == NULL)
+ break;
+
+ RingCsqCompleteCanceledIrp(&Ring->Write.Csq, Irp);
+ }
+}
+
+static VOID
+RingAcquireLock(
+ IN PVOID Argument
+ )
+{
+ PXENCONS_RING Ring = Argument;
+
+ KeAcquireSpinLockAtDpcLevel(&Ring->Lock);
+}
+
+static VOID
+RingReleaseLock(
+ IN PVOID Argument
+ )
+{
+ PXENCONS_RING Ring = Argument;
+
+#pragma prefast(suppress:26110)
+ KeReleaseSpinLockFromDpcLevel(&Ring->Lock);
+}
+
+NTSTATUS
+RingOpen(
+ IN PXENCONS_RING Ring,
+ IN PFILE_OBJECT FileObject
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+ UNREFERENCED_PARAMETER(FileObject);
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RingClose(
+ IN PXENCONS_RING Ring,
+ IN PFILE_OBJECT FileObject
+ )
+{
+ __RingCancelRequests(Ring, FileObject);
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RingPutQueue(
+ IN PXENCONS_RING Ring,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (StackLocation->MajorFunction) {
+ case IRP_MJ_READ:
+ status = IoCsqInsertIrpEx(&Ring->Read.Csq,
+ Irp,
+ NULL,
+ (PVOID)FALSE);
+ break;
+
+ case IRP_MJ_WRITE:
+ status = IoCsqInsertIrpEx(&Ring->Write.Csq,
+ Irp,
+ NULL,
+ (PVOID)FALSE);
+ break;
+
+ default:
+ ASSERT(FALSE);
+ status = STATUS_NOT_SUPPORTED; // Keep SDV happy
+ break;
+ }
+ if (status != STATUS_PENDING)
+ goto fail1;
+
+ KeInsertQueueDpc(&Ring->Dpc, NULL, NULL);
+ return STATUS_PENDING;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static ULONG
+RingCopyFromRead(
+ IN PXENCONS_RING Ring,
+ IN PCHAR Data,
+ IN ULONG Length
+ )
+{
+ struct xencons_interface *Shared;
+ XENCONS_RING_IDX cons;
+ XENCONS_RING_IDX prod;
+ ULONG Offset;
+
+ Shared = Ring->Shared;
+
+ KeMemoryBarrier();
+
+ cons = Shared->in_cons;
+ prod = Shared->in_prod;
+
+ KeMemoryBarrier();
+
+ Offset = 0;
+ while (Length != 0) {
+ ULONG Available;
+ ULONG Index;
+ ULONG CopyLength;
+
+ Available = prod - cons;
+
+ if (Available == 0)
+ break;
+
+ Index = MASK_XENCONS_IDX(cons, Shared->in);
+
+ CopyLength = __min(Length, Available);
+ CopyLength = __min(CopyLength, sizeof(Shared->in) - Index);
+
+ RtlCopyMemory(Data + Offset, &Shared->in[Index], CopyLength);
+
+ Offset += CopyLength;
+ Length -= CopyLength;
+
+ cons += CopyLength;
+ }
+
+ KeMemoryBarrier();
+
+ Shared->in_cons = cons;
+
+ KeMemoryBarrier();
+
+ return Offset;
+}
+
+static ULONG
+RingCopyToWrite(
+ IN PXENCONS_RING Ring,
+ IN PCHAR Data,
+ IN ULONG Length
+ )
+{
+ struct xencons_interface *Shared;
+ XENCONS_RING_IDX cons;
+ XENCONS_RING_IDX prod;
+ ULONG Offset;
+
+ Shared = Ring->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 BOOLEAN
+RingPoll(
+ IN PXENCONS_RING Ring
+ )
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ ULONG Length;
+ PCHAR Buffer;
+ NTSTATUS status;
+
+ for (;;) {
+ ULONG Read;
+
+ Irp = IoCsqRemoveNextIrp(&Ring->Read.Csq, NULL);
+ if (Irp == NULL)
+ break;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ ASSERT(StackLocation->MajorFunction == IRP_MJ_READ);
+
+ Length = StackLocation->Parameters.Read.Length;
+ Buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ Read = RingCopyFromRead(Ring,
+ Buffer,
+ Length);
+ if (Read == 0) {
+ status = IoCsqInsertIrpEx(&Ring->Read.Csq,
+ Irp,
+ NULL,
+ (PVOID)TRUE);
+ ASSERT(status == STATUS_PENDING);
+ break;
+ }
+
+ Ring->BytesRead += Read;
+
+ Irp->IoStatus.Information = Read;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ Trace("COMPLETE (READ) (%u bytes)\n",
+ Irp->IoStatus.Information);
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+ for (;;) {
+ ULONG Written;
+
+ Irp = IoCsqRemoveNextIrp(&Ring->Write.Csq, NULL);
+ if (Irp == NULL)
+ break;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ ASSERT(StackLocation->MajorFunction == IRP_MJ_WRITE);
+
+ Length = StackLocation->Parameters.Write.Length;
+ Buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ Written = RingCopyToWrite(Ring,
+ Buffer,
+ Length);
+ if (Written == 0) {
+ status = IoCsqInsertIrpEx(&Ring->Write.Csq,
+ Irp,
+ NULL,
+ (PVOID)TRUE);
+ ASSERT(status == STATUS_PENDING);
+ break;
+ }
+
+ Ring->BytesWritten += Written;
+
+ Irp->IoStatus.Information = Written;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ Trace("COMPLETE (WRITE) (%u bytes)\n",
+ Irp->IoStatus.Information);
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+ return FALSE;
+}
+
+__drv_functionClass(KDEFERRED_ROUTINE)
+__drv_maxIRQL(DISPATCH_LEVEL)
+__drv_minIRQL(PASSIVE_LEVEL)
+__drv_sameIRQL
+static VOID
+RingDpc(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID Argument1,
+ IN PVOID Argument2
+ )
+{
+ PXENCONS_RING Ring = Context;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(Argument1);
+ UNREFERENCED_PARAMETER(Argument2);
+
+ ASSERT(Ring != NULL);
+
+ for (;;) {
+ BOOLEAN Enabled;
+ BOOLEAN Retry;
+ KIRQL Irql;
+
+ KeAcquireSpinLock(&Ring->Lock, &Irql);
+ Enabled = Ring->Enabled;
+ KeReleaseSpinLock(&Ring->Lock, Irql);
+
+ if (!Enabled)
+ break;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ Retry = RingPoll(Ring);
+ KeLowerIrql(Irql);
+
+ if (!Retry)
+ break;
+ }
+
+ (VOID) XENBUS_EVTCHN(Unmask,
+ &Ring->EvtchnInterface,
+ Ring->Channel,
+ FALSE,
+ FALSE);
+}
+
+KSERVICE_ROUTINE RingEvtchnCallback;
+
+BOOLEAN
+RingEvtchnCallback(
+ IN PKINTERRUPT InterruptObject,
+ IN PVOID Argument
+ )
+{
+ PXENCONS_RING Ring = Argument;
+
+ UNREFERENCED_PARAMETER(InterruptObject);
+
+ ASSERT(Ring != NULL);
+
+ Ring->Events++;
+
+ if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL))
+ Ring->Dpcs++;
+
+ return TRUE;
+}
+
+static VOID
+RingDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ PXENCONS_RING Ring = Argument;
+
+ UNREFERENCED_PARAMETER(Crashing);
+
+ XENBUS_DEBUG(Printf,
+ &Ring->DebugInterface,
+ "0x%p [%s]\n",
+ Ring,
+ (Ring->Enabled) ? "ENABLED" : "DISABLED");
+
+ // Dump shared ring
+ XENBUS_DEBUG(Printf,
+ &Ring->DebugInterface,
+ "SHARED: in_cons = %u in_prod = %u out_cons = %u out_prod = %u\n",
+ Ring->Shared->in_cons,
+ Ring->Shared->in_prod,
+ Ring->Shared->out_cons,
+ Ring->Shared->out_prod);
+
+ XENBUS_DEBUG(Printf,
+ &Ring->DebugInterface,
+ "BYTES: read = %u written = %u\n",
+ Ring->BytesRead,
+ Ring->BytesWritten);
+}
+
+NTSTATUS
+RingEnable(
+ IN PXENCONS_RING Ring
+ )
+{
+ Trace("====>\n");
+
+ KeAcquireSpinLockAtDpcLevel(&Ring->Lock);
+ Ring->Enabled = TRUE;
+ KeReleaseSpinLockFromDpcLevel(&Ring->Lock);
+
+ (VOID)KeInsertQueueDpc(&Ring->Dpc, NULL, NULL);
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+RingDisable(
+ IN PXENCONS_RING Ring
+ )
+{
+ Trace("====>\n");
+
+ ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL);
+
+ KeAcquireSpinLockAtDpcLevel(&Ring->Lock);
+ Ring->Enabled = FALSE;
+ KeReleaseSpinLockFromDpcLevel(&Ring->Lock);
+
+ Trace("<====\n");
+}
+
+NTSTATUS
+RingConnect(
+ IN PXENCONS_RING Ring
+ )
+{
+ CHAR Name[MAXNAMELEN];
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ ASSERT(!Ring->Connected);
+
+ status = XENBUS_DEBUG(Acquire, &Ring->DebugInterface);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = XENBUS_EVTCHN(Acquire, &Ring->EvtchnInterface);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = XENBUS_GNTTAB(Acquire, &Ring->GnttabInterface);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = XENBUS_STORE(Acquire, &Ring->StoreInterface);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ status = RtlStringCbPrintfA(Name,
+ sizeof(Name),
+ "console_%s_gnttab",
+ PdoGetName(FrontendGetPdo(Ring->Frontend)));
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ status = XENBUS_GNTTAB(CreateCache,
+ &Ring->GnttabInterface,
+ Name,
+ 0,
+ RingAcquireLock,
+ RingReleaseLock,
+ Ring,
+ &Ring->GnttabCache);
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ Ring->Mdl = __AllocatePage();
+
+ status = STATUS_NO_MEMORY;
+ if (Ring->Mdl == NULL)
+ goto fail7;
+
+ ASSERT(Ring->Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+ Ring->Shared = Ring->Mdl->MappedSystemVa;
+ ASSERT(Ring->Shared != NULL);
+
+ status = XENBUS_GNTTAB(PermitForeignAccess,
+ &Ring->GnttabInterface,
+ Ring->GnttabCache,
+ TRUE,
+ FrontendGetBackendDomain(Ring->Frontend),
+ MmGetMdlPfnArray(Ring->Mdl)[0],
+ FALSE,
+ &Ring->Entry);
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ Ring->Channel = XENBUS_EVTCHN(Open,
+ &Ring->EvtchnInterface,
+ XENBUS_EVTCHN_TYPE_UNBOUND,
+ RingEvtchnCallback,
+ Ring,
+ FrontendGetBackendDomain(Ring->Frontend),
+ TRUE);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Ring->Channel == NULL)
+ goto fail9;
+
+ (VOID)XENBUS_EVTCHN(Unmask,
+ &Ring->EvtchnInterface,
+ Ring->Channel,
+ FALSE,
+ TRUE);
+
+ status = XENBUS_DEBUG(Register,
+ &Ring->DebugInterface,
+ __MODULE__ "|POLLER",
+ RingDebugCallback,
+ Ring,
+ &Ring->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail10;
+
+ Ring->Connected = TRUE;
+
+ Trace("<====\n");
+ return STATUS_SUCCESS;
+
+fail10:
+ Error("fail10\n");
+
+ Ring->Events = 0;
+
+ XENBUS_EVTCHN(Close,
+ &Ring->EvtchnInterface,
+ Ring->Channel);
+ Ring->Channel = NULL;
+
+fail9:
+ Error("fail9\n");
+
+ (VOID)XENBUS_GNTTAB(RevokeForeignAccess,
+ &Ring->GnttabInterface,
+ Ring->GnttabCache,
+ TRUE,
+ Ring->Entry);
+ Ring->Entry = NULL;
+
+fail8:
+ Error("fail8\n");
+
+ RtlZeroMemory(Ring->Shared, PAGE_SIZE);
+
+ Ring->Shared = NULL;
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+
+fail7:
+ Error("fail7\n");
+
+ XENBUS_GNTTAB(DestroyCache,
+ &Ring->GnttabInterface,
+ Ring->GnttabCache);
+ Ring->GnttabCache = NULL;
+
+fail6:
+ Error("fail6\n");
+
+fail5:
+ Error("fail5\n");
+
+ XENBUS_STORE(Release, &Ring->StoreInterface);
+
+fail4:
+ Error("fail4\n");
+
+ XENBUS_GNTTAB(Release, &Ring->GnttabInterface);
+
+fail3:
+ Error("fail3\n");
+
+ XENBUS_EVTCHN(Release, &Ring->EvtchnInterface);
+
+fail2:
+ Error("fail2\n");
+
+ XENBUS_DEBUG(Release, &Ring->DebugInterface);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+RingStoreWrite(
+ IN PXENCONS_RING Ring,
+ IN PVOID Transaction
+ )
+{
+ ULONG Port;
+ ULONG GrantRef;
+ NTSTATUS status;
+
+ Port = XENBUS_EVTCHN(GetPort,
+ &Ring->EvtchnInterface,
+ Ring->Channel);
+
+ status = XENBUS_STORE(Printf,
+ &Ring->StoreInterface,
+ Transaction,
+ FrontendGetPath(Ring->Frontend),
+ "port",
+ "%u",
+ Port);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ GrantRef = XENBUS_GNTTAB(GetReference,
+ &Ring->GnttabInterface,
+ Ring->Entry);
+
+ status = XENBUS_STORE(Printf,
+ &Ring->StoreInterface,
+ Transaction,
+ FrontendGetPath(Ring->Frontend),
+ "ring-ref",
+ "%u",
+ GrantRef);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+RingDisconnect(
+ IN PXENCONS_RING Ring
+ )
+{
+ Trace("====>\n");
+
+ ASSERT(Ring->Connected);
+ Ring->Connected = FALSE;
+
+ XENBUS_DEBUG(Deregister,
+ &Ring->DebugInterface,
+ Ring->DebugCallback);
+ Ring->DebugCallback = NULL;
+
+ Ring->Dpcs = 0;
+ Ring->Events = 0;
+ Ring->BytesRead = 0;
+ Ring->BytesWritten = 0;
+
+ XENBUS_EVTCHN(Close,
+ &Ring->EvtchnInterface,
+ Ring->Channel);
+ Ring->Channel = NULL;
+
+ (VOID)XENBUS_GNTTAB(RevokeForeignAccess,
+ &Ring->GnttabInterface,
+ Ring->GnttabCache,
+ TRUE,
+ Ring->Entry);
+ Ring->Entry = NULL;
+
+ RtlZeroMemory(Ring->Shared, PAGE_SIZE);
+
+ Ring->Shared = NULL;
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+
+ XENBUS_GNTTAB(DestroyCache,
+ &Ring->GnttabInterface,
+ Ring->GnttabCache);
+ Ring->GnttabCache = NULL;
+
+ XENBUS_STORE(Release, &Ring->StoreInterface);
+
+ XENBUS_GNTTAB(Release, &Ring->GnttabInterface);
+
+ XENBUS_EVTCHN(Release, &Ring->EvtchnInterface);
+
+ XENBUS_DEBUG(Release, &Ring->DebugInterface);
+
+ Trace("<====\n");
+}
+
+NTSTATUS
+RingCreate(
+ IN PXENCONS_FRONTEND Frontend,
+ OUT PXENCONS_RING *Ring
+ )
+{
+ NTSTATUS status;
+
+ *Ring = __RingAllocate(sizeof(XENCONS_RING));
+
+ status = STATUS_NO_MEMORY;
+ if (*Ring == NULL)
+ goto fail1;
+
+ (*Ring)->Frontend = Frontend;
+
+ FdoGetDebugInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+ &(*Ring)->DebugInterface);
+
+ FdoGetEvtchnInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+ &(*Ring)->EvtchnInterface);
+
+ FdoGetGnttabInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+ &(*Ring)->GnttabInterface);
+
+ FdoGetStoreInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+ &(*Ring)->StoreInterface);
+
+ KeInitializeSpinLock(&(*Ring)->Lock);
+
+ KeInitializeThreadedDpc(&(*Ring)->Dpc, RingDpc, *Ring);
+
+ KeInitializeSpinLock(&(*Ring)->Read.Lock);
+ InitializeListHead(&(*Ring)->Read.List);
+
+ status = IoCsqInitializeEx(&(*Ring)->Read.Csq,
+ RingCsqInsertIrpEx,
+ RingCsqRemoveIrp,
+ RingCsqPeekNextIrp,
+ RingCsqAcquireLock,
+ RingCsqReleaseLock,
+ RingCsqCompleteCanceledIrp);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ KeInitializeSpinLock(&(*Ring)->Write.Lock);
+ InitializeListHead(&(*Ring)->Write.List);
+
+ status = IoCsqInitializeEx(&(*Ring)->Write.Csq,
+ RingCsqInsertIrpEx,
+ RingCsqRemoveIrp,
+ RingCsqPeekNextIrp,
+ RingCsqAcquireLock,
+ RingCsqReleaseLock,
+ RingCsqCompleteCanceledIrp);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ RtlZeroMemory(&(*Ring)->Write.List, sizeof(LIST_ENTRY));
+ RtlZeroMemory(&(*Ring)->Write.Lock, sizeof(KSPIN_LOCK));
+
+ RtlZeroMemory(&(*Ring)->Read.Csq, sizeof(IO_CSQ));
+
+fail2:
+ Error("fail2\n");
+
+ RtlZeroMemory(&(*Ring)->Read.List, sizeof(LIST_ENTRY));
+ RtlZeroMemory(&(*Ring)->Read.Lock, sizeof(KSPIN_LOCK));
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+RingDestroy(
+ IN PXENCONS_RING Ring
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+ // Cancel all outstanding IRPs
+ __RingCancelRequests(Ring, NULL);
+
+ ASSERT(IsListEmpty(&Ring->Read.List));
+ ASSERT(IsListEmpty(&Ring->Write.List));
+
+ RtlZeroMemory(&Ring->Write.Csq, sizeof(IO_CSQ));
+
+ RtlZeroMemory(&Ring->Write.List, sizeof(LIST_ENTRY));
+ RtlZeroMemory(&Ring->Write.Lock, sizeof(KSPIN_LOCK));
+
+ RtlZeroMemory(&Ring->Read.Csq, sizeof(IO_CSQ));
+
+ RtlZeroMemory(&Ring->Read.List, sizeof(LIST_ENTRY));
+ RtlZeroMemory(&Ring->Read.Lock, sizeof(KSPIN_LOCK));
+
+ RtlZeroMemory(&Ring->Dpc, sizeof(KDPC));
+
+ RtlZeroMemory(&Ring->Lock, sizeof(KSPIN_LOCK));
+
+ RtlZeroMemory(&Ring->StoreInterface,
+ sizeof(XENBUS_STORE_INTERFACE));
+
+ RtlZeroMemory(&Ring->GnttabInterface,
+ sizeof(XENBUS_GNTTAB_INTERFACE));
+
+ RtlZeroMemory(&Ring->EvtchnInterface,
+ sizeof(XENBUS_EVTCHN_INTERFACE));
+
+ RtlZeroMemory(&Ring->DebugInterface,
+ sizeof(XENBUS_DEBUG_INTERFACE));
+
+ Ring->Frontend = NULL;
+
+ ASSERT(IsZeroMemory(Ring, sizeof(XENCONS_RING)));
+ __RingFree(Ring);
+}