\r
Utility functions used by virtio device drivers.\r
\r
- Copyright (C) 2012, Red Hat, Inc.\r
+ Copyright (C) 2012-2016, Red Hat, Inc.\r
Portion of Copyright (C) 2013, ARM Ltd.\r
+ Copyright (C) 2017, AMD Inc, All rights reserved.<BR>\r
\r
- This program and the accompanying materials are licensed and made available\r
- under the terms and conditions of the BSD License which accompanies this\r
- distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
- WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include <Library/BaseLib.h>\r
#include <Library/BaseMemoryLib.h>\r
#include <Library/DebugLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
\r
#include <Library/VirtioLib.h>\r
\r
-\r
/**\r
\r
Configure a virtio ring.\r
- 1.1 Virtqueues,\r
- 2.3 Virtqueue Configuration.\r
\r
+ @param[in] VirtIo The virtio device which will use the ring.\r
+\r
@param[in] The number of descriptors to allocate for the\r
virtio ring, as requested by the host.\r
\r
@param[out] Ring The virtio ring to set up.\r
\r
- @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous\r
- pages for the requested QueueSize. Fields of\r
- Ring have indeterminate value.\r
+ @return Status codes propagated from\r
+ VirtIo->AllocateSharedPages().\r
\r
@retval EFI_SUCCESS Allocation and setup successful. Ring->Base\r
(and nothing else) is responsible for\r
EFI_STATUS\r
EFIAPI\r
VirtioRingInit (\r
- IN UINT16 QueueSize,\r
- OUT VRING *Ring\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN UINT16 QueueSize,\r
+ OUT VRING *Ring\r
)\r
{\r
- UINTN RingSize;\r
- volatile UINT8 *RingPagesPtr;\r
+ EFI_STATUS Status;\r
+ UINTN RingSize;\r
+ volatile UINT8 *RingPagesPtr;\r
\r
RingSize = ALIGN_VALUE (\r
sizeof *Ring->Desc * QueueSize +\r
sizeof *Ring->Avail.Idx +\r
sizeof *Ring->Avail.Ring * QueueSize +\r
sizeof *Ring->Avail.UsedEvent,\r
- EFI_PAGE_SIZE);\r
+ EFI_PAGE_SIZE\r
+ );\r
\r
RingSize += ALIGN_VALUE (\r
sizeof *Ring->Used.Flags +\r
sizeof *Ring->Used.Idx +\r
sizeof *Ring->Used.UsedElem * QueueSize +\r
sizeof *Ring->Used.AvailEvent,\r
- EFI_PAGE_SIZE);\r
+ EFI_PAGE_SIZE\r
+ );\r
\r
+ //\r
+ // Allocate a shared ring buffer\r
+ //\r
Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);\r
- Ring->Base = AllocatePages (Ring->NumPages);\r
- if (Ring->Base == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
+ Status = VirtIo->AllocateSharedPages (\r
+ VirtIo,\r
+ Ring->NumPages,\r
+ &Ring->Base\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
}\r
+\r
SetMem (Ring->Base, RingSize, 0x00);\r
RingPagesPtr = Ring->Base;\r
\r
- Ring->Desc = (volatile VOID *) RingPagesPtr;\r
+ Ring->Desc = (volatile VOID *)RingPagesPtr;\r
RingPagesPtr += sizeof *Ring->Desc * QueueSize;\r
\r
- Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.Flags;\r
+ Ring->Avail.Flags = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Avail.Flags;\r
\r
- Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.Idx;\r
+ Ring->Avail.Idx = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Avail.Idx;\r
\r
- Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;\r
+ Ring->Avail.Ring = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;\r
\r
- Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.UsedEvent;\r
+ Ring->Avail.UsedEvent = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Avail.UsedEvent;\r
\r
- RingPagesPtr = (volatile UINT8 *) Ring->Base +\r
- ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,\r
- EFI_PAGE_SIZE);\r
+ RingPagesPtr = (volatile UINT8 *)Ring->Base +\r
+ ALIGN_VALUE (\r
+ RingPagesPtr - (volatile UINT8 *)Ring->Base,\r
+ EFI_PAGE_SIZE\r
+ );\r
\r
- Ring->Used.Flags = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.Flags;\r
+ Ring->Used.Flags = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Used.Flags;\r
\r
- Ring->Used.Idx = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.Idx;\r
+ Ring->Used.Idx = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Used.Idx;\r
\r
- Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;\r
+ Ring->Used.UsedElem = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;\r
\r
- Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.AvailEvent;\r
+ Ring->Used.AvailEvent = (volatile VOID *)RingPagesPtr;\r
+ RingPagesPtr += sizeof *Ring->Used.AvailEvent;\r
\r
Ring->QueueSize = QueueSize;\r
return EFI_SUCCESS;\r
}\r
\r
-\r
/**\r
\r
Tear down the internal resources of a configured virtio ring.\r
invoking this function: the VSTAT_DRIVER_OK bit must be clear in\r
VhdrDeviceStatus.\r
\r
- @param[out] Ring The virtio ring to clean up.\r
+ @param[in] VirtIo The virtio device which was using the ring.\r
+\r
+ @param[out] Ring The virtio ring to clean up.\r
\r
**/\r
VOID\r
EFIAPI\r
VirtioRingUninit (\r
- IN OUT VRING *Ring\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN OUT VRING *Ring\r
)\r
{\r
- FreePages (Ring->Base, Ring->NumPages);\r
+ VirtIo->FreeSharedPages (VirtIo, Ring->NumPages, Ring->Base);\r
SetMem (Ring, sizeof *Ring, 0x00);\r
}\r
\r
-\r
/**\r
\r
Turn off interrupt notifications from the host, and prepare for appending\r
VOID\r
EFIAPI\r
VirtioPrepare (\r
- IN OUT VRING *Ring,\r
- OUT DESC_INDICES *Indices\r
+ IN OUT VRING *Ring,\r
+ OUT DESC_INDICES *Indices\r
)\r
{\r
//\r
// Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.\r
// We're going to poll the answer, the host should not send an interrupt.\r
//\r
- *Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
+ *Ring->Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;\r
\r
//\r
// Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\r
Indices->NextDescIdx = Indices->HeadDescIdx;\r
}\r
\r
-\r
/**\r
\r
Append a contiguous buffer for transmission / reception via the virtio ring.\r
The caller is responsible for initializing *Indices with VirtioPrepare()\r
first.\r
\r
- @param[in,out] Ring The virtio ring to append the buffer to, as a\r
- descriptor.\r
+ @param[in,out] Ring The virtio ring to append the buffer to,\r
+ as a descriptor.\r
\r
- @param[in] BufferPhysAddr (Guest pseudo-physical) start address of the\r
- transmit / receive buffer.\r
+ @param[in] BufferDeviceAddress (Bus master device) start address of the\r
+ transmit / receive buffer.\r
\r
- @param[in] BufferSize Number of bytes to transmit or receive.\r
+ @param[in] BufferSize Number of bytes to transmit or receive.\r
\r
- @param[in] Flags A bitmask of VRING_DESC_F_* flags. The caller\r
- computes this mask dependent on further buffers to\r
- append and transfer direction.\r
- VRING_DESC_F_INDIRECT is unsupported. The\r
- VRING_DESC.Next field is always set, but the host\r
- only interprets it dependent on VRING_DESC_F_NEXT.\r
+ @param[in] Flags A bitmask of VRING_DESC_F_* flags. The\r
+ caller computes this mask dependent on\r
+ further buffers to append and transfer\r
+ direction. VRING_DESC_F_INDIRECT is\r
+ unsupported. The VRING_DESC.Next field is\r
+ always set, but the host only interprets\r
+ it dependent on VRING_DESC_F_NEXT.\r
\r
- @param[in,out] Indices Indices->HeadDescIdx is not accessed.\r
- On input, Indices->NextDescIdx identifies the next\r
- descriptor to carry the buffer. On output,\r
- Indices->NextDescIdx is incremented by one, modulo\r
- 2^16.\r
+ @param[in,out] Indices Indices->HeadDescIdx is not accessed.\r
+ On input, Indices->NextDescIdx identifies\r
+ the next descriptor to carry the buffer.\r
+ On output, Indices->NextDescIdx is\r
+ incremented by one, modulo 2^16.\r
\r
**/\r
VOID\r
EFIAPI\r
VirtioAppendDesc (\r
- IN OUT VRING *Ring,\r
- IN UINTN BufferPhysAddr,\r
- IN UINT32 BufferSize,\r
- IN UINT16 Flags,\r
- IN OUT DESC_INDICES *Indices\r
+ IN OUT VRING *Ring,\r
+ IN UINT64 BufferDeviceAddress,\r
+ IN UINT32 BufferSize,\r
+ IN UINT16 Flags,\r
+ IN OUT DESC_INDICES *Indices\r
)\r
{\r
- volatile VRING_DESC *Desc;\r
+ volatile VRING_DESC *Desc;\r
\r
Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize];\r
- Desc->Addr = BufferPhysAddr;\r
+ Desc->Addr = BufferDeviceAddress;\r
Desc->Len = BufferSize;\r
Desc->Flags = Flags;\r
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
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
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
+ 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
- EFI_STATUS Status;\r
- UINTN PollPeriodUsecs;\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
// 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
+ 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
+ MemoryFence ();\r
Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
// Keep slowing down until we reach a poll period of slightly above 1 ms.\r
//\r
PollPeriodUsecs = 1;\r
- MemoryFence();\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
+ 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
+ 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
- MemoryFence();\r
+ *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base;\r
return EFI_SUCCESS;\r
}\r