+ Desc->Next = Indices->NextDescIdx % Ring->QueueSize;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Notify the host about the descriptor chain just built, and wait until the\r
+ host processes it.\r
+\r
+ @param[in] VirtIo The target virtio device to notify.\r
+\r
+ @param[in] VirtQueueId Identifies the queue for the target device.\r
+\r
+ @param[in,out] Ring The virtio ring with descriptors to submit.\r
+\r
+ @param[in] Indices Indices->NextDescIdx is not accessed.\r
+ Indices->HeadDescIdx identifies the head descriptor\r
+ of the descriptor chain.\r
+\r
+ @param[out] UsedLen On success, the total number of bytes, consecutively\r
+ across the buffers linked by the descriptor chain,\r
+ that the host wrote. May be NULL if the caller\r
+ doesn't care, or can compute the same information\r
+ from device-specific request structures linked by the\r
+ descriptor chain.\r
+\r
+ @return Error code from VirtIo->SetQueueNotify() if it fails.\r
+\r
+ @retval EFI_SUCCESS Otherwise, the host processed all descriptors.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFlush (\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN UINT16 VirtQueueId,\r
+ IN OUT VRING *Ring,\r
+ IN DESC_INDICES *Indices,\r
+ OUT UINT32 *UsedLen OPTIONAL\r
+ )\r
+{\r
+ UINT16 NextAvailIdx;\r
+ UINT16 LastUsedIdx;\r
+ EFI_STATUS Status;\r
+ UINTN PollPeriodUsecs;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.2 Updating the Available Ring\r
+ //\r
+ // It is not exactly clear from the wording of the virtio-0.9.5\r
+ // specification, but each entry in the Available Ring references only the\r
+ // head descriptor of any given descriptor chain.\r
+ //\r
+ NextAvailIdx = *Ring->Avail.Idx;\r
+ //\r
+ // (Due to our lock-step progress, this is where the host will produce the\r
+ // used element with the head descriptor's index in it.)\r
+ //\r
+ LastUsedIdx = NextAvailIdx;\r
+ Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] =\r
+ Indices->HeadDescIdx % Ring->QueueSize;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
+ //\r
+ MemoryFence();\r
+ *Ring->Avail.Idx = NextAvailIdx;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are\r
+ // OK.\r
+ //\r
+ MemoryFence();\r
+ Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device\r
+ // Wait until the host processes and acknowledges our descriptor chain. The\r
+ // condition we use for polling is greatly simplified and relies on the\r
+ // synchronous, lock-step progress.\r
+ //\r
+ // Keep slowing down until we reach a poll period of slightly above 1 ms.\r
+ //\r
+ PollPeriodUsecs = 1;\r
+ MemoryFence();\r
+ while (*Ring->Used.Idx != NextAvailIdx) {\r
+ gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay\r
+\r
+ if (PollPeriodUsecs < 1024) {\r
+ PollPeriodUsecs *= 2;\r
+ }\r
+ MemoryFence();\r
+ }\r
+\r
+ MemoryFence();\r
+\r
+ if (UsedLen != NULL) {\r
+ volatile CONST VRING_USED_ELEM *UsedElem;\r
+\r
+ UsedElem = &Ring->Used.UsedElem[LastUsedIdx % Ring->QueueSize];\r
+ ASSERT (UsedElem->Id == Indices->HeadDescIdx);\r
+ *UsedLen = UsedElem->Len;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver\r
+ understands.\r
+\r
+ In VirtIo 1.0, a device can reject a self-inconsistent feature bitmap through\r
+ the new VSTAT_FEATURES_OK status bit. (For example if the driver requests a\r
+ higher level feature but clears a prerequisite feature.) This function is a\r
+ small wrapper around VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures() that also\r
+ verifies if the VirtIo 1.0 device accepts the feature bitmap.\r
+\r
+ @param[in] VirtIo Report feature bits to this device.\r
+\r
+ @param[in] Features The set of feature bits that the driver wishes\r
+ to report. The caller is responsible to perform\r
+ any masking before calling this function; the\r
+ value is directly written with\r
+ VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures().\r
+\r
+ @param[in,out] DeviceStatus On input, the status byte most recently written\r
+ to the device's status register. On output (even\r
+ on error), DeviceStatus will be updated so that\r
+ it is suitable for further status bit\r
+ manipulation and writing to the device's status\r
+ register.\r
+\r
+ @retval EFI_SUCCESS The device accepted the configuration in Features.\r
+\r
+ @return EFI_UNSUPPORTED The device rejected the configuration in Features.\r
+\r
+ @retval EFI_UNSUPPORTED VirtIo->Revision is smaller than 1.0.0.\r
+\r
+ @return Error codes from the SetGuestFeatures(),\r
+ SetDeviceStatus(), GetDeviceStatus() member\r
+ functions.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10WriteFeatures (\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN UINT64 Features,\r
+ IN OUT UINT8 *DeviceStatus\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Status = VirtIo->SetGuestFeatures (VirtIo, Features);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *DeviceStatus |= VSTAT_FEATURES_OK;\r
+ Status = VirtIo->SetDeviceStatus (VirtIo, *DeviceStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = VirtIo->GetDeviceStatus (VirtIo, DeviceStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((*DeviceStatus & VSTAT_FEATURES_OK) == 0) {\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Provides the virtio device address required to access system memory from a\r
+ DMA bus master.\r
+\r
+ The interface follows the same usage pattern as defined in UEFI spec 2.6\r
+ (Section 13.2 PCI Root Bridge I/O Protocol)\r
+\r
+ The VirtioMapAllBytesInSharedBuffer() is similar to VIRTIO_MAP_SHARED\r
+ with exception that NumberOfBytes is IN-only parameter. The function\r
+ maps all the bytes specified in NumberOfBytes param in one consecutive\r
+ range.\r
+\r
+ @param[in] VirtIo The virtio device for which the mapping is\r
+ requested.\r
+\r
+ @param[in] Operation Indicates if the bus master is going to\r
+ read or write to system memory.\r
+\r
+ @param[in] HostAddress The system memory address to map to shared\r
+ buffer address.\r
+\r
+ @param[in] NumberOfBytes Number of bytes to map.\r
+\r
+ @param[out] DeviceAddress The resulting shared map address for the\r
+ bus master to access the hosts HostAddress.\r
+\r
+ @param[out] Mapping A resulting token to pass to\r
+ VIRTIO_UNMAP_SHARED.\r
+\r
+\r
+ @retval EFI_SUCCESS The NumberOfBytes is successfully mapped.\r
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a\r
+ common buffer.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to\r
+ a lack of resources. This includes the case\r
+ when NumberOfBytes bytes cannot be mapped\r
+ in one consecutive range.\r
+ @retval EFI_DEVICE_ERROR The system hardware could not map the\r
+ requested address.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioMapAllBytesInSharedBuffer (\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN VIRTIO_MAP_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN UINTN NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *MapInfo;\r
+ UINTN Size;\r
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
+\r
+ Size = NumberOfBytes;\r
+ Status = VirtIo->MapSharedBuffer (\r
+ VirtIo,\r
+ Operation,\r
+ HostAddress,\r
+ &Size,\r
+ &PhysicalAddress,\r
+ &MapInfo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Size < NumberOfBytes) {\r
+ goto Failed;\r
+ }\r
+\r
+ *Mapping = MapInfo;\r
+ *DeviceAddress = PhysicalAddress;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Failed:\r
+ VirtIo->UnmapSharedBuffer (VirtIo, MapInfo);\r
+ return EFI_OUT_OF_RESOURCES;\r
+}\r
+\r
+/**\r
+\r
+ Map the ring buffer so that it can be accessed equally by both guest\r
+ and hypervisor.\r
+\r
+ @param[in] VirtIo The virtio device instance.\r
+\r
+ @param[in] Ring The virtio ring to map.\r
+\r
+ @param[out] RingBaseShift A resulting translation offset, to be\r
+ passed to VirtIo->SetQueueAddress().\r
+\r
+ @param[out] Mapping A resulting token to pass to\r
+ VirtIo->UnmapSharedBuffer().\r
+\r
+ @return Status code from VirtIo->MapSharedBuffer()\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioRingMap (\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN VRING *Ring,\r
+ OUT UINT64 *RingBaseShift,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ VirtIo,\r
+ VirtioOperationBusMasterCommonBuffer,\r
+ Ring->Base,\r
+ EFI_PAGES_TO_SIZE (Ring->NumPages),\r
+ &DeviceAddress,\r
+ Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base;\r
+ return EFI_SUCCESS;\r