--- /dev/null
+/** @file\r
+ Minimal block driver for Mini-OS.\r
+\r
+ Copyright (c) 2007-2008 Samuel Thibault.\r
+ Copyright (C) 2014, Citrix Ltd.\r
+\r
+ Redistribution and use in source and binary forms, with or without\r
+ modification, are permitted provided that the following conditions\r
+ are met:\r
+ 1. Redistributions of source code must retain the above copyright\r
+ notice, this list of conditions and the following disclaimer.\r
+ 2. Redistributions in binary form must reproduce the above copyright\r
+ notice, this list of conditions and the following disclaimer in the\r
+ documentation and/or other materials provided with the distribution.\r
+\r
+ THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND\r
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE\r
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+ SUCH DAMAGE.\r
+**/\r
+\r
+#include <Library/PrintLib.h>\r
+#include <Library/DebugLib.h>\r
+\r
+#include "BlockFront.h"\r
+\r
+#include <IndustryStandard/Xen/io/protocols.h>\r
+#include <IndustryStandard/Xen/io/xenbus.h>\r
+\r
+//\r
+// Header used for UINT32_MAX and UINT16_MAX\r
+//\r
+#include "inttypes.h"\r
+\r
+/**\r
+ Helper to read an integer from XenStore.\r
+\r
+ If the number overflows according to the range defined by UINT64,\r
+ then ASSERT().\r
+\r
+ @param This A pointer to a XENBUS_PROTOCOL instance.\r
+ @param Node The XenStore node to read from.\r
+ @param FromBackend Read frontend or backend value.\r
+ @param ValuePtr Where to put the value.\r
+\r
+ @retval XENSTORE_STATUS_SUCCESS If succefull, will update ValuePtr.\r
+ @return Any other return value indicate the error,\r
+ ValuePtr is not updated in this case.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenBusReadUint64 (\r
+ IN XENBUS_PROTOCOL *This,\r
+ IN CONST CHAR8 *Node,\r
+ IN BOOLEAN FromBackend,\r
+ OUT UINT64 *ValuePtr\r
+ )\r
+{\r
+ XENSTORE_STATUS Status;\r
+ CHAR8 *Ptr;\r
+\r
+ if (!FromBackend) {\r
+ Status = This->XsRead (This, XST_NIL, Node, (VOID**)&Ptr);\r
+ } else {\r
+ Status = This->XsBackendRead (This, XST_NIL, Node, (VOID**)&Ptr);\r
+ }\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ return Status;\r
+ }\r
+ // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64.\r
+ *ValuePtr = AsciiStrDecimalToUint64 (Ptr);\r
+ FreePool (Ptr);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Free an instance of XEN_BLOCK_FRONT_DEVICE.\r
+\r
+ @param Dev The instance to free.\r
+**/\r
+STATIC\r
+VOID\r
+XenPvBlockFree (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev\r
+ )\r
+{\r
+ XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
+\r
+ if (Dev->RingRef != 0) {\r
+ XenBusIo->GrantEndAccess (XenBusIo, Dev->RingRef);\r
+ }\r
+ if (Dev->Ring.sring != NULL) {\r
+ FreePages (Dev->Ring.sring, 1);\r
+ }\r
+ if (Dev->EventChannel != 0) {\r
+ XenBusIo->EventChannelClose (XenBusIo, Dev->EventChannel);\r
+ }\r
+ FreePool (Dev);\r
+}\r
+\r
+/**\r
+ Wait until until the backend has reached the ExpectedState.\r
+\r
+ @param Dev A XEN_BLOCK_FRONT_DEVICE instance.\r
+ @param ExpectedState The backend state expected.\r
+ @param LastStatePtr An optional pointer where to right the final state.\r
+\r
+ @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState\r
+ or return an error otherwise.\r
+**/\r
+STATIC\r
+XENSTORE_STATUS\r
+XenPvBlkWaitForBackendState (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev,\r
+ IN XenbusState ExpectedState,\r
+ OUT XenbusState *LastStatePtr OPTIONAL\r
+ )\r
+{\r
+ XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
+ XenbusState State;\r
+ UINT64 Value;\r
+ XENSTORE_STATUS Status = XENSTORE_STATUS_SUCCESS;\r
+\r
+ while (TRUE) {\r
+ Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ return Status;\r
+ }\r
+ if (Value > XenbusStateReconfigured) {\r
+ //\r
+ // Value is not a State value.\r
+ //\r
+ return XENSTORE_STATUS_EIO;\r
+ }\r
+ State = Value;\r
+ if (State == ExpectedState) {\r
+ break;\r
+ } else if (State > ExpectedState) {\r
+ Status = XENSTORE_STATUS_FAIL;\r
+ break;\r
+ }\r
+ DEBUG ((EFI_D_INFO,\r
+ "XenPvBlk: waiting backend state %d, current: %d\n",\r
+ ExpectedState, State));\r
+ XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken);\r
+ }\r
+\r
+ if (LastStatePtr != NULL) {\r
+ *LastStatePtr = State;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+EFI_STATUS\r
+XenPvBlockFrontInitialization (\r
+ IN XENBUS_PROTOCOL *XenBusIo,\r
+ IN CONST CHAR8 *NodeName,\r
+ OUT XEN_BLOCK_FRONT_DEVICE **DevPtr\r
+ )\r
+{\r
+ XENSTORE_TRANSACTION xbt;\r
+ CHAR8 *DeviceType;\r
+ blkif_sring_t *SharedRing;\r
+ XENSTORE_STATUS Status;\r
+ XEN_BLOCK_FRONT_DEVICE *Dev;\r
+ XenbusState State;\r
+ UINT64 Value;\r
+\r
+ ASSERT (NodeName != NULL);\r
+\r
+ Dev = AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE));\r
+ Dev->Signature = XEN_BLOCK_FRONT_SIGNATURE;\r
+ Dev->NodeName = NodeName;\r
+ Dev->XenBusIo = XenBusIo;\r
+ Dev->DeviceId = XenBusIo->DeviceId;\r
+\r
+ XenBusIo->XsRead (XenBusIo, XST_NIL, "device-type", (VOID**)&DeviceType);\r
+ if (AsciiStrCmp (DeviceType, "cdrom") == 0) {\r
+ Dev->MediaInfo.CdRom = TRUE;\r
+ } else {\r
+ Dev->MediaInfo.CdRom = FALSE;\r
+ }\r
+ FreePool (DeviceType);\r
+\r
+ Status = XenBusReadUint64 (XenBusIo, "backend-id", FALSE, &Value);\r
+ if (Status != XENSTORE_STATUS_SUCCESS || Value > UINT16_MAX) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to get backend-id (%d)\n",\r
+ Status));\r
+ goto Error;\r
+ }\r
+ Dev->DomainId = Value;\r
+ XenBusIo->EventChannelAllocate (XenBusIo, Dev->DomainId, &Dev->EventChannel);\r
+\r
+ SharedRing = (blkif_sring_t*) AllocatePages (1);\r
+ SHARED_RING_INIT (SharedRing);\r
+ FRONT_RING_INIT (&Dev->Ring, SharedRing, EFI_PAGE_SIZE);\r
+ XenBusIo->GrantAccess (XenBusIo,\r
+ Dev->DomainId,\r
+ (INTN) SharedRing >> EFI_PAGE_SHIFT,\r
+ FALSE,\r
+ &Dev->RingRef);\r
+\r
+Again:\r
+ Status = XenBusIo->XsTransactionStart (XenBusIo, &xbt);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_WARN, "XenPvBlk: Failed to start transaction, %d\n", Status));\r
+ goto Error;\r
+ }\r
+\r
+ Status = XenBusIo->XsPrintf (XenBusIo, xbt, NodeName, "ring-ref", "%d",\r
+ Dev->RingRef);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write ring-ref.\n"));\r
+ goto AbortTransaction;\r
+ }\r
+ Status = XenBusIo->XsPrintf (XenBusIo, xbt, NodeName,\r
+ "event-channel", "%d", Dev->EventChannel);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write event-channel.\n"));\r
+ goto AbortTransaction;\r
+ }\r
+ Status = XenBusIo->XsPrintf (XenBusIo, xbt, NodeName,\r
+ "protocol", "%a", XEN_IO_PROTO_ABI_NATIVE);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to write protocol.\n"));\r
+ goto AbortTransaction;\r
+ }\r
+\r
+ Status = XenBusIo->SetState (XenBusIo, xbt, XenbusStateConnected);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: Failed to switch state.\n"));\r
+ goto AbortTransaction;\r
+ }\r
+\r
+ Status = XenBusIo->XsTransactionEnd (XenBusIo, xbt, FALSE);\r
+ if (Status == XENSTORE_STATUS_EAGAIN) {\r
+ goto Again;\r
+ }\r
+\r
+ XenBusIo->RegisterWatchBackend (XenBusIo, "state", &Dev->StateWatchToken);\r
+\r
+ //\r
+ // Waiting for backend\r
+ //\r
+ Status = XenPvBlkWaitForBackendState (Dev, XenbusStateConnected, &State);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n",\r
+ XenBusIo->Type, XenBusIo->DeviceId, Status, State));\r
+ goto Error2;\r
+ }\r
+\r
+ Status = XenBusReadUint64 (XenBusIo, "info", TRUE, &Value);\r
+ if (Status != XENSTORE_STATUS_SUCCESS || Value > UINT32_MAX) {\r
+ goto Error2;\r
+ }\r
+ Dev->MediaInfo.VDiskInfo = Value;\r
+ if (Dev->MediaInfo.VDiskInfo & VDISK_READONLY) {\r
+ Dev->MediaInfo.ReadWrite = FALSE;\r
+ } else {\r
+ Dev->MediaInfo.ReadWrite = TRUE;\r
+ }\r
+\r
+ Status = XenBusReadUint64 (XenBusIo, "sectors", TRUE, &Dev->MediaInfo.Sectors);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ goto Error2;\r
+ }\r
+\r
+ Status = XenBusReadUint64 (XenBusIo, "sector-size", TRUE, &Value);\r
+ if (Status != XENSTORE_STATUS_SUCCESS || Value > UINT32_MAX) {\r
+ goto Error2;\r
+ }\r
+ if (Value % 512 != 0) {\r
+ //\r
+ // This is not supported by the driver.\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: Unsupported sector-size value %d, "\r
+ "it must be a multiple of 512\n", Value));\r
+ goto Error2;\r
+ }\r
+ Dev->MediaInfo.SectorSize = Value;\r
+\r
+ // Default value\r
+ Value = 0;\r
+ XenBusReadUint64 (XenBusIo, "feature-barrier", TRUE, &Value);\r
+ if (Value == 1) {\r
+ Dev->MediaInfo.FeatureBarrier = TRUE;\r
+ } else {\r
+ Dev->MediaInfo.FeatureBarrier = FALSE;\r
+ }\r
+\r
+ // Default value\r
+ Value = 0;\r
+ XenBusReadUint64 (XenBusIo, "feature-flush-cache", TRUE, &Value);\r
+ if (Value == 1) {\r
+ Dev->MediaInfo.FeatureFlushCache = TRUE;\r
+ } else {\r
+ Dev->MediaInfo.FeatureFlushCache = FALSE;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "XenPvBlk: New disk with %ld sectors of %d bytes\n",\r
+ Dev->MediaInfo.Sectors, Dev->MediaInfo.SectorSize));\r
+\r
+ *DevPtr = Dev;\r
+ return EFI_SUCCESS;\r
+\r
+Error2:\r
+ XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken);\r
+ XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref");\r
+ XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel");\r
+ XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol");\r
+ goto Error;\r
+AbortTransaction:\r
+ XenBusIo->XsTransactionEnd (XenBusIo, xbt, TRUE);\r
+Error:\r
+ XenPvBlockFree (Dev);\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+VOID\r
+XenPvBlockFrontShutdown (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev\r
+ )\r
+{\r
+ XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
+ XENSTORE_STATUS Status;\r
+ UINT64 Value;\r
+\r
+ XenPvBlockSync (Dev);\r
+\r
+ Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosing);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: error while changing state to Closing: %d\n",\r
+ Status));\r
+ goto Close;\r
+ }\r
+\r
+ Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosing, NULL);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: error while waiting for closing backend state: %d\n",\r
+ Status));\r
+ goto Close;\r
+ }\r
+\r
+ Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosed);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: error while changing state to Closed: %d\n",\r
+ Status));\r
+ goto Close;\r
+ }\r
+\r
+ Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosed, NULL);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: error while waiting for closed backend state: %d\n",\r
+ Status));\r
+ goto Close;\r
+ }\r
+\r
+ Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateInitialising);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: error while changing state to initialising: %d\n",\r
+ Status));\r
+ goto Close;\r
+ }\r
+\r
+ while (TRUE) {\r
+ Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value);\r
+ if (Status != XENSTORE_STATUS_SUCCESS) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: error while waiting for new backend state: %d\n",\r
+ Status));\r
+ goto Close;\r
+ }\r
+ if (Value <= XenbusStateInitWait || Value >= XenbusStateClosed) {\r
+ break;\r
+ }\r
+ DEBUG ((EFI_D_INFO,\r
+ "XenPvBlk: waiting backend state %d, current: %d\n",\r
+ XenbusStateInitWait, Value));\r
+ XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken);\r
+ }\r
+\r
+Close:\r
+ XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken);\r
+ XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref");\r
+ XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel");\r
+ XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol");\r
+\r
+ XenPvBlockFree (Dev);\r
+}\r
+\r
+STATIC\r
+VOID\r
+XenPvBlockWaitSlot (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev\r
+ )\r
+{\r
+ /* Wait for a slot */\r
+ if (RING_FULL (&Dev->Ring)) {\r
+ while (TRUE) {\r
+ XenPvBlockAsyncIoPoll (Dev);\r
+ if (!RING_FULL (&Dev->Ring)) {\r
+ break;\r
+ }\r
+ /* Really no slot, could wait for an event on Dev->EventChannel. */\r
+ }\r
+ }\r
+}\r
+\r
+VOID\r
+XenPvBlockAsyncIo (\r
+ IN OUT XEN_BLOCK_FRONT_IO *IoData,\r
+ IN BOOLEAN IsWrite\r
+ )\r
+{\r
+ XEN_BLOCK_FRONT_DEVICE *Dev = IoData->Dev;\r
+ XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
+ blkif_request_t *Request;\r
+ RING_IDX RingIndex;\r
+ BOOLEAN Notify;\r
+ INT32 NumSegments, Index;\r
+ UINTN Start, End;\r
+\r
+ // Can't io at non-sector-aligned location\r
+ ASSERT(!(IoData->Sector & ((Dev->MediaInfo.SectorSize / 512) - 1)));\r
+ // Can't io non-sector-sized amounts\r
+ ASSERT(!(IoData->Size & (Dev->MediaInfo.SectorSize - 1)));\r
+ // Can't io non-sector-aligned buffer\r
+ ASSERT(!((UINTN) IoData->Buffer & (Dev->MediaInfo.SectorSize - 1)));\r
+\r
+ Start = (UINTN) IoData->Buffer & ~EFI_PAGE_MASK;\r
+ End = ((UINTN) IoData->Buffer + IoData->Size + EFI_PAGE_SIZE - 1) & ~EFI_PAGE_MASK;\r
+ IoData->NumRef = NumSegments = (End - Start) / EFI_PAGE_SIZE;\r
+\r
+ ASSERT (NumSegments <= BLKIF_MAX_SEGMENTS_PER_REQUEST);\r
+\r
+ XenPvBlockWaitSlot (Dev);\r
+ RingIndex = Dev->Ring.req_prod_pvt;\r
+ Request = RING_GET_REQUEST (&Dev->Ring, RingIndex);\r
+\r
+ Request->operation = IsWrite ? BLKIF_OP_WRITE : BLKIF_OP_READ;\r
+ Request->nr_segments = NumSegments;\r
+ Request->handle = Dev->DeviceId;\r
+ Request->id = (UINTN) IoData;\r
+ Request->sector_number = IoData->Sector;\r
+\r
+ for (Index = 0; Index < NumSegments; Index++) {\r
+ Request->seg[Index].first_sect = 0;\r
+ Request->seg[Index].last_sect = EFI_PAGE_SIZE / 512 - 1;\r
+ }\r
+ Request->seg[0].first_sect = ((UINTN) IoData->Buffer & EFI_PAGE_MASK) / 512;\r
+ Request->seg[NumSegments - 1].last_sect =\r
+ (((UINTN) IoData->Buffer + IoData->Size - 1) & EFI_PAGE_MASK) / 512;\r
+ for (Index = 0; Index < NumSegments; Index++) {\r
+ UINTN Data = Start + Index * EFI_PAGE_SIZE;\r
+ XenBusIo->GrantAccess (XenBusIo, Dev->DomainId,\r
+ Data >> EFI_PAGE_SHIFT, IsWrite,\r
+ &Request->seg[Index].gref);\r
+ IoData->GrantRef[Index] = Request->seg[Index].gref;\r
+ }\r
+\r
+ Dev->Ring.req_prod_pvt = RingIndex + 1;\r
+\r
+ MemoryFence ();\r
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify);\r
+\r
+ if (Notify) {\r
+ UINT32 ReturnCode;\r
+ ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel);\r
+ if (ReturnCode != 0) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",\r
+ ReturnCode));\r
+ }\r
+ }\r
+}\r
+\r
+EFI_STATUS\r
+XenPvBlockIo (\r
+ IN OUT XEN_BLOCK_FRONT_IO *IoData,\r
+ IN BOOLEAN IsWrite\r
+ )\r
+{\r
+ //\r
+ // Status value that correspond to an IO in progress.\r
+ //\r
+ IoData->Status = EFI_ALREADY_STARTED;\r
+ XenPvBlockAsyncIo (IoData, IsWrite);\r
+\r
+ while (IoData->Status == EFI_ALREADY_STARTED) {\r
+ XenPvBlockAsyncIoPoll (IoData->Dev);\r
+ }\r
+\r
+ return IoData->Status;\r
+}\r
+\r
+STATIC\r
+VOID\r
+XenPvBlockPushOperation (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev,\r
+ IN UINT8 Operation,\r
+ IN UINT64 Id\r
+ )\r
+{\r
+ INT32 Index;\r
+ blkif_request_t *Request;\r
+ BOOLEAN Notify;\r
+\r
+ XenPvBlockWaitSlot (Dev);\r
+ Index = Dev->Ring.req_prod_pvt;\r
+ Request = RING_GET_REQUEST(&Dev->Ring, Index);\r
+ Request->operation = Operation;\r
+ Request->nr_segments = 0;\r
+ Request->handle = Dev->DeviceId;\r
+ Request->id = Id;\r
+ /* Not needed anyway, but the backend will check it */\r
+ Request->sector_number = 0;\r
+ Dev->Ring.req_prod_pvt = Index + 1;\r
+ MemoryFence ();\r
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify);\r
+ if (Notify) {\r
+ XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo;\r
+ UINT32 ReturnCode;\r
+ ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel);\r
+ if (ReturnCode != 0) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n",\r
+ ReturnCode));\r
+ }\r
+ }\r
+}\r
+\r
+VOID\r
+XenPvBlockSync (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev\r
+ )\r
+{\r
+ if (Dev->MediaInfo.ReadWrite) {\r
+ if (Dev->MediaInfo.FeatureBarrier) {\r
+ XenPvBlockPushOperation (Dev, BLKIF_OP_WRITE_BARRIER, 0);\r
+ }\r
+\r
+ if (Dev->MediaInfo.FeatureFlushCache) {\r
+ XenPvBlockPushOperation (Dev, BLKIF_OP_FLUSH_DISKCACHE, 0);\r
+ }\r
+ }\r
+\r
+ /* Note: This won't finish if another thread enqueues requests. */\r
+ while (TRUE) {\r
+ XenPvBlockAsyncIoPoll (Dev);\r
+ if (RING_FREE_REQUESTS (&Dev->Ring) == RING_SIZE (&Dev->Ring)) {\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+VOID\r
+XenPvBlockAsyncIoPoll (\r
+ IN XEN_BLOCK_FRONT_DEVICE *Dev\r
+ )\r
+{\r
+ RING_IDX ProducerIndex, ConsumerIndex;\r
+ blkif_response_t *Response;\r
+ INT32 More;\r
+\r
+ do {\r
+ ProducerIndex = Dev->Ring.sring->rsp_prod;\r
+ /* Ensure we see queued responses up to 'ProducerIndex'. */\r
+ MemoryFence ();\r
+ ConsumerIndex = Dev->Ring.rsp_cons;\r
+\r
+ while (ConsumerIndex != ProducerIndex) {\r
+ XEN_BLOCK_FRONT_IO *IoData = NULL;\r
+ INT16 Status;\r
+\r
+ Response = RING_GET_RESPONSE (&Dev->Ring, ConsumerIndex);\r
+\r
+ IoData = (VOID *) (UINTN) Response->id;\r
+ Status = Response->status;\r
+\r
+ switch (Response->operation) {\r
+ case BLKIF_OP_READ:\r
+ case BLKIF_OP_WRITE:\r
+ {\r
+ INT32 Index;\r
+\r
+ if (Status != BLKIF_RSP_OKAY) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: "\r
+ "%a error %d on %a at sector %p, num bytes %p\n",\r
+ Response->operation == BLKIF_OP_READ ? "read" : "write",\r
+ Status, IoData->Dev->NodeName,\r
+ IoData->Sector,\r
+ IoData->Size));\r
+ }\r
+\r
+ for (Index = 0; Index < IoData->NumRef; Index++) {\r
+ Dev->XenBusIo->GrantEndAccess (Dev->XenBusIo, IoData->GrantRef[Index]);\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ case BLKIF_OP_WRITE_BARRIER:\r
+ if (Status != BLKIF_RSP_OKAY) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: write barrier error %d\n", Status));\r
+ }\r
+ break;\r
+ case BLKIF_OP_FLUSH_DISKCACHE:\r
+ if (Status != BLKIF_RSP_OKAY) {\r
+ DEBUG ((EFI_D_ERROR, "XenPvBlk: flush error %d\n", Status));\r
+ }\r
+ break;\r
+\r
+ default:\r
+ DEBUG ((EFI_D_ERROR,\r
+ "XenPvBlk: unrecognized block operation %d response (status %d)\n",\r
+ Response->operation, Status));\r
+ break;\r
+ }\r
+\r
+ Dev->Ring.rsp_cons = ++ConsumerIndex;\r
+ if (IoData != NULL) {\r
+ IoData->Status = Status ? EFI_DEVICE_ERROR : EFI_SUCCESS;\r
+ }\r
+ if (Dev->Ring.rsp_cons != ConsumerIndex) {\r
+ /* We reentered, we must not continue here */\r
+ break;\r
+ }\r
+ }\r
+\r
+ RING_FINAL_CHECK_FOR_RESPONSES (&Dev->Ring, More);\r
+ } while (More != 0);\r
+}\r