A non-transitional driver for VirtIo 1.0 PCI devices.\r
\r
Copyright (C) 2016, Red Hat, Inc.\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
#include <IndustryStandard/Pci.h>\r
#include <IndustryStandard/Virtio.h>\r
#include <Protocol/PciIo.h>\r
+#include <Protocol/PciRootBridgeIo.h>\r
#include <Protocol/VirtioDevice.h>\r
#include <Library/BaseMemoryLib.h>\r
#include <Library/DebugLib.h>\r
#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PciCapLib.h>\r
+#include <Library/PciCapPciIoLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
#include <Library/UefiLib.h>\r
\r
}\r
\r
\r
-/**\r
- Read a slice from PCI config space at the given offset, then advance the\r
- offset.\r
-\r
- @param [in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents the\r
- device.\r
-\r
- @param [in,out] Offset On input, the offset in PCI config space to start\r
- reading from. On output, the offset of the first byte\r
- that was not read. On error, Offset is not modified.\r
-\r
- @param [in] Size The number of bytes to read.\r
-\r
- @param [out] Buffer On output, the bytes read from PCI config space are\r
- stored in this object.\r
-\r
- @retval EFI_SUCCESS Size bytes have been transferred from PCI config space\r
- (from Offset) to Buffer, and Offset has been incremented\r
- by Size.\r
-\r
- @return Error codes from PciIo->Pci.Read().\r
-**/\r
-STATIC\r
-EFI_STATUS\r
-ReadConfigSpace (\r
- IN EFI_PCI_IO_PROTOCOL *PciIo,\r
- IN OUT UINT32 *Offset,\r
- IN UINTN Size,\r
- OUT VOID *Buffer\r
- )\r
-{\r
- EFI_STATUS Status;\r
-\r
- Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, *Offset, Size, Buffer);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
- *Offset += (UINT32)Size;\r
- return EFI_SUCCESS;\r
-}\r
-\r
-\r
/*\r
Traverse the PCI capabilities list of a virtio-1.0 device, and capture the\r
locations of the interesting virtio-1.0 register blocks.\r
will have been updated from the PCI\r
capabilities found.\r
\r
- @param[in] CapabilityPtr The offset of the first capability in PCI\r
- config space, taken from the standard PCI\r
- device header.\r
-\r
@retval EFI_SUCCESS Traversal successful.\r
\r
- @return Error codes from the ReadConfigSpace() and GetBarType()\r
- helper functions.\r
+ @return Error codes from PciCapPciIoLib, PciCapLib, and the\r
+ GetBarType() helper function.\r
*/\r
STATIC\r
EFI_STATUS\r
ParseCapabilities (\r
- IN OUT VIRTIO_1_0_DEV *Device,\r
- IN UINT8 CapabilityPtr\r
+ IN OUT VIRTIO_1_0_DEV *Device\r
)\r
{\r
- UINT32 Offset;\r
- VIRTIO_PCI_CAP_LINK CapLink;\r
-\r
- for (Offset = CapabilityPtr & 0xFC;\r
- Offset > 0;\r
- Offset = CapLink.CapNext & 0xFC\r
- ) {\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
+ PCI_CAP_DEV *PciDevice;\r
+ PCI_CAP_LIST *CapList;\r
+ UINT16 VendorInstance;\r
+ PCI_CAP *VendorCap;\r
+\r
+ Status = PciCapPciIoDeviceInit (Device->PciIo, &PciDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ Status = PciCapListInit (PciDevice, &CapList);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UninitPciDevice;\r
+ }\r
+\r
+ for (VendorInstance = 0;\r
+ !EFI_ERROR (PciCapListFindCap (CapList, PciCapNormal,\r
+ EFI_PCI_CAPABILITY_ID_VENDOR, VendorInstance,\r
+ &VendorCap));\r
+ VendorInstance++) {\r
UINT8 CapLen;\r
VIRTIO_PCI_CAP VirtIoCap;\r
VIRTIO_1_0_CONFIG *ParsedConfig;\r
\r
- //\r
- // Read capability identifier and link to next capability.\r
- //\r
- Status = ReadConfigSpace (Device->PciIo, &Offset, sizeof CapLink,\r
- &CapLink);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
- if (CapLink.CapId != 0x09) {\r
- //\r
- // Not a vendor-specific capability, move to the next one.\r
- //\r
- continue;\r
- }\r
-\r
//\r
// Big enough to accommodate a VIRTIO_PCI_CAP structure?\r
//\r
- Status = ReadConfigSpace (Device->PciIo, &Offset, sizeof CapLen, &CapLen);\r
+ Status = PciCapRead (PciDevice, VendorCap,\r
+ OFFSET_OF (EFI_PCI_CAPABILITY_VENDOR_HDR, Length), &CapLen,\r
+ sizeof CapLen);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto UninitCapList;\r
}\r
- if (CapLen < sizeof CapLink + sizeof CapLen + sizeof VirtIoCap) {\r
+ if (CapLen < sizeof VirtIoCap) {\r
//\r
// Too small, move to next.\r
//\r
//\r
// Read interesting part of capability.\r
//\r
- Status = ReadConfigSpace (Device->PciIo, &Offset, sizeof VirtIoCap,\r
- &VirtIoCap);\r
+ Status = PciCapRead (PciDevice, VendorCap, 0, &VirtIoCap, sizeof VirtIoCap);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto UninitCapList;\r
}\r
+\r
switch (VirtIoCap.ConfigType) {\r
case VIRTIO_PCI_CAP_COMMON_CFG:\r
ParsedConfig = &Device->CommonConfig;\r
//\r
Status = GetBarType (Device->PciIo, VirtIoCap.Bar, &ParsedConfig->BarType);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto UninitCapList;\r
}\r
ParsedConfig->Bar = VirtIoCap.Bar;\r
ParsedConfig->Offset = VirtIoCap.Offset;\r
// This capability has an additional field called NotifyOffsetMultiplier;\r
// parse it too.\r
//\r
- if (CapLen < sizeof CapLink + sizeof CapLen + sizeof VirtIoCap +\r
- sizeof Device->NotifyOffsetMultiplier) {\r
+ if (CapLen < sizeof VirtIoCap + sizeof Device->NotifyOffsetMultiplier) {\r
//\r
// Too small, move to next.\r
//\r
continue;\r
}\r
\r
- Status = ReadConfigSpace (Device->PciIo, &Offset,\r
- sizeof Device->NotifyOffsetMultiplier,\r
- &Device->NotifyOffsetMultiplier);\r
+ Status = PciCapRead (PciDevice, VendorCap, sizeof VirtIoCap,\r
+ &Device->NotifyOffsetMultiplier,\r
+ sizeof Device->NotifyOffsetMultiplier);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto UninitCapList;\r
}\r
}\r
\r
ParsedConfig->Exists = TRUE;\r
}\r
\r
- return EFI_SUCCESS;\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+UninitCapList:\r
+ PciCapListUninit (CapList);\r
+\r
+UninitPciDevice:\r
+ PciCapPciIoDeviceUninit (PciDevice);\r
+\r
+ return Status;\r
}\r
\r
\r
EFIAPI\r
Virtio10SetQueueAddress (\r
IN VIRTIO_DEVICE_PROTOCOL *This,\r
- IN VRING *Ring\r
+ IN VRING *Ring,\r
+ IN UINT64 RingBaseShift\r
)\r
{\r
VIRTIO_1_0_DEV *Dev;\r
Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
\r
Address = (UINTN)Ring->Desc;\r
+ Address += RingBaseShift;\r
Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueDesc),\r
sizeof Address, &Address);\r
}\r
\r
Address = (UINTN)Ring->Avail.Flags;\r
+ Address += RingBaseShift;\r
Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueAvail),\r
sizeof Address, &Address);\r
}\r
\r
Address = (UINTN)Ring->Used.Flags;\r
+ Address += RingBaseShift;\r
Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueUsed),\r
sizeof Address, &Address);\r
return Status;\r
}\r
\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10AllocateSharedPages (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINTN Pages,\r
+ IN OUT VOID **HostAddress\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+ EFI_STATUS Status;\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ Status = Dev->PciIo->AllocateBuffer (\r
+ Dev->PciIo,\r
+ AllocateAnyPages,\r
+ EfiBootServicesData,\r
+ Pages,\r
+ HostAddress,\r
+ EFI_PCI_ATTRIBUTE_MEMORY_CACHED\r
+ );\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+Virtio10FreeSharedPages (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ Dev->PciIo->FreeBuffer (\r
+ Dev->PciIo,\r
+ Pages,\r
+ HostAddress\r
+ );\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10MapSharedBuffer (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN VIRTIO_MAP_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_1_0_DEV *Dev;\r
+ EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation;\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ //\r
+ // Map VIRTIO_MAP_OPERATION to EFI_PCI_IO_PROTOCOL_OPERATION\r
+ //\r
+ switch (Operation) {\r
+ case VirtioOperationBusMasterRead:\r
+ PciIoOperation = EfiPciIoOperationBusMasterRead;\r
+ break;\r
+ case VirtioOperationBusMasterWrite:\r
+ PciIoOperation = EfiPciIoOperationBusMasterWrite;\r
+ break;\r
+ case VirtioOperationBusMasterCommonBuffer:\r
+ PciIoOperation = EfiPciIoOperationBusMasterCommonBuffer;\r
+ break;\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = Dev->PciIo->Map (\r
+ Dev->PciIo,\r
+ PciIoOperation,\r
+ HostAddress,\r
+ NumberOfBytes,\r
+ DeviceAddress,\r
+ Mapping\r
+ );\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10UnmapSharedBuffer (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN VOID *Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_1_0_DEV *Dev;\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ Status = Dev->PciIo->Unmap (\r
+ Dev->PciIo,\r
+ Mapping\r
+ );\r
+\r
+ return Status;\r
+}\r
\r
STATIC CONST VIRTIO_DEVICE_PROTOCOL mVirtIoTemplate = {\r
VIRTIO_SPEC_REVISION (1, 0, 0),\r
Virtio10GetDeviceStatus,\r
Virtio10SetDeviceStatus,\r
Virtio10WriteDevice,\r
- Virtio10ReadDevice\r
+ Virtio10ReadDevice,\r
+ Virtio10AllocateSharedPages,\r
+ Virtio10FreeSharedPages,\r
+ Virtio10MapSharedBuffer,\r
+ Virtio10UnmapSharedBuffer\r
};\r
\r
\r
\r
Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040;\r
\r
- Status = ParseCapabilities (Device, Pci.Device.CapabilityPtr);\r
+ Status = ParseCapabilities (Device);\r
if (EFI_ERROR (Status)) {\r
goto ClosePciIo;\r
}\r
goto ClosePciIo;\r
}\r
\r
- SetAttributes = EFI_PCI_IO_ATTRIBUTE_BUS_MASTER;\r
+ SetAttributes = (EFI_PCI_IO_ATTRIBUTE_BUS_MASTER |\r
+ EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE);\r
UpdateAttributes (&Device->CommonConfig, &SetAttributes);\r
UpdateAttributes (&Device->NotifyConfig, &SetAttributes);\r
UpdateAttributes (&Device->SpecificConfig, &SetAttributes);\r