+/** @file\r
+ A non-transitional driver for VirtIo 1.0 PCI devices.\r
+\r
+ Copyright (C) 2016, Red Hat, Inc.\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
+**/\r
+\r
+#include <IndustryStandard/Pci.h>\r
+#include <IndustryStandard/Virtio.h>\r
+#include <Protocol/PciIo.h>\r
+#include <Protocol/VirtioDevice.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+\r
+#include "Virtio10.h"\r
+\r
+\r
+//\r
+// Utility functions\r
+//\r
+\r
+/**\r
+ Transfer data between the caller and a register in a virtio-1.0 register\r
+ block.\r
+\r
+ @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents\r
+ the device.\r
+\r
+ @param[in] Config The "fat pointer" structure that identifies the\r
+ register block to access.\r
+\r
+ @param[in] Write TRUE if the register should be written, FALSE if\r
+ the register should be read.\r
+\r
+ @param[in] FieldOffset The offset of the register within the register\r
+ block.\r
+\r
+ @param[in] FieldSize The size of the register within the register\r
+ block. Can be one of 1, 2, 4 and 8. Accesses to\r
+ 8-byte registers are broken up into two 4-byte\r
+ accesses.\r
+\r
+ @param[in,out] Buffer When Write is TRUE, the register is written with\r
+ data from Buffer. When Write is FALSE, the caller\r
+ receives the register value into Buffer.\r
+\r
+ @retval EFI_SUCCESS Register access successful.\r
+\r
+ @retval EFI_INVALID_PARAMETER The register block pointed-to by Config\r
+ doesn't exist; or FieldOffset and FieldSize\r
+ would overflow the register block; or\r
+ FieldSize is invalid.\r
+\r
+ @return Error codes from\r
+ EFI_PCI_IO_PROTOCOL.(Io|Mem).(Read|Write)\r
+ member functions.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Virtio10Transfer (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN VIRTIO_1_0_CONFIG *Config,\r
+ IN BOOLEAN Write,\r
+ IN UINTN FieldOffset,\r
+ IN UINTN FieldSize,\r
+ IN OUT VOID *Buffer\r
+ )\r
+{\r
+ UINTN Count;\r
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
+ EFI_PCI_IO_PROTOCOL_ACCESS *BarType;\r
+ EFI_PCI_IO_PROTOCOL_IO_MEM Access;\r
+\r
+ if (!Config->Exists ||\r
+ FieldSize > Config->Length ||\r
+ FieldOffset > Config->Length - FieldSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Count = 1;\r
+ switch (FieldSize) {\r
+ case 1:\r
+ Width = EfiPciIoWidthUint8;\r
+ break;\r
+\r
+ case 2:\r
+ Width = EfiPciIoWidthUint16;\r
+ break;\r
+\r
+ case 8:\r
+ Count = 2;\r
+ //\r
+ // fall through\r
+ //\r
+\r
+ case 4:\r
+ Width = EfiPciIoWidthUint32;\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ BarType = (Config->BarType == Virtio10BarTypeMem) ? &PciIo->Mem : &PciIo->Io;\r
+ Access = Write ? BarType->Write : BarType->Read;\r
+\r
+ return Access (PciIo, Width, Config->Bar, Config->Offset + FieldOffset,\r
+ Count, Buffer);\r
+}\r
+\r
+\r
+/**\r
+ Determine if a PCI BAR is IO or MMIO.\r
+\r
+ @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents the\r
+ device.\r
+\r
+ @param[in] BarIndex The number of the BAR whose type the caller is\r
+ interested in.\r
+\r
+ @param[out] BarType On output, a VIRTIO_1_0_BAR_TYPE value that gives the\r
+ type of the BAR.\r
+\r
+ @retval EFI_SUCCESS The BAR type has been recognized and stored in\r
+ BarType.\r
+\r
+ @retval EFI_UNSUPPORTED The BAR type couldn't be identified.\r
+\r
+ @return Error codes from\r
+ EFI_PCI_IO_PROTOCOL.GetBarAttributes().\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetBarType (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN UINT8 BarIndex,\r
+ OUT VIRTIO_1_0_BAR_TYPE *BarType\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *Resources;\r
+\r
+ Status = PciIo->GetBarAttributes (PciIo, BarIndex, NULL, &Resources);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = EFI_UNSUPPORTED;\r
+\r
+ if (*(UINT8 *)Resources == ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR) {\r
+ EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
+\r
+ Descriptor = Resources;\r
+ switch (Descriptor->ResType) {\r
+ case ACPI_ADDRESS_SPACE_TYPE_MEM:\r
+ *BarType = Virtio10BarTypeMem;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ case ACPI_ADDRESS_SPACE_TYPE_IO:\r
+ *BarType = Virtio10BarTypeIo;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+\r
+ FreePool (Resources);\r
+ return Status;\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
+\r
+ @param[in,out] Device The VIRTIO_1_0_DEV structure that identifies\r
+ the device. On input, the caller is responsible\r
+ that the Device->PciIo member be live, and that\r
+ the CommonConfig, NotifyConfig,\r
+ NotifyOffsetMultiplier and SpecificConfig\r
+ members be zeroed. On output, said members\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
+*/\r
+STATIC\r
+EFI_STATUS\r
+ParseCapabilities (\r
+ IN OUT VIRTIO_1_0_DEV *Device,\r
+ IN UINT8 CapabilityPtr\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
+ 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
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ if (CapLen < sizeof CapLink + sizeof CapLen + sizeof VirtIoCap) {\r
+ //\r
+ // Too small, move to next.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Read interesting part of capability.\r
+ //\r
+ Status = ReadConfigSpace (Device->PciIo, &Offset, sizeof VirtIoCap,\r
+ &VirtIoCap);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ switch (VirtIoCap.ConfigType) {\r
+ case VIRTIO_PCI_CAP_COMMON_CFG:\r
+ ParsedConfig = &Device->CommonConfig;\r
+ break;\r
+ case VIRTIO_PCI_CAP_NOTIFY_CFG:\r
+ ParsedConfig = &Device->NotifyConfig;\r
+ break;\r
+ case VIRTIO_PCI_CAP_DEVICE_CFG:\r
+ ParsedConfig = &Device->SpecificConfig;\r
+ break;\r
+ default:\r
+ //\r
+ // Capability is not interesting.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Save the location of the register block into ParsedConfig.\r
+ //\r
+ Status = GetBarType (Device->PciIo, VirtIoCap.Bar, &ParsedConfig->BarType);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ ParsedConfig->Bar = VirtIoCap.Bar;\r
+ ParsedConfig->Offset = VirtIoCap.Offset;\r
+ ParsedConfig->Length = VirtIoCap.Length;\r
+\r
+ if (VirtIoCap.ConfigType == VIRTIO_PCI_CAP_NOTIFY_CFG) {\r
+ //\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
+ //\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
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Capability parsed successfully.\r
+ //\r
+ ParsedConfig->Exists = TRUE;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Accumulate the BAR type of a virtio-1.0 register block into a UINT64\r
+ attribute map, such that the latter is suitable for enabling IO / MMIO\r
+ decoding with EFI_PCI_IO_PROTOCOL.Attributes().\r
+\r
+ @param[in] Config The "fat pointer" structure that identifies the\r
+ register block. It is allowed for the register\r
+ block not to exist.\r
+\r
+ @param[in,out] Attributes On output, if the register block exists,\r
+ EFI_PCI_IO_ATTRIBUTE_MEMORY or\r
+ EFI_PCI_IO_ATTRIBUTE_IO is OR-ed into Attributes,\r
+ according to the register block's BAR type.\r
+**/\r
+STATIC\r
+VOID\r
+UpdateAttributes (\r
+ IN VIRTIO_1_0_CONFIG *Config,\r
+ IN OUT UINT64 *Attributes\r
+ )\r
+{\r
+ if (Config->Exists) {\r
+ *Attributes |= (Config->BarType == Virtio10BarTypeMem) ?\r
+ EFI_PCI_IO_ATTRIBUTE_MEMORY:\r
+ EFI_PCI_IO_ATTRIBUTE_IO;\r
+ }\r
+}\r
+\r
+\r
+//\r
+// VIRTIO_DEVICE_PROTOCOL member functions\r
+//\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10GetDeviceFeatures (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ OUT UINT64 *DeviceFeatures\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+ UINT32 Selector;\r
+ UINT32 Features32[2];\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ for (Selector = 0; Selector < 2; ++Selector) {\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Select the low or high half of the features.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeatureSelect),\r
+ sizeof Selector, &Selector);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Fetch that half.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeature),\r
+ sizeof Features32[Selector], &Features32[Selector]);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ *DeviceFeatures = LShiftU64 (Features32[1], 32) | Features32[0];\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetGuestFeatures (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT64 Features\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+ UINT32 Selector;\r
+ UINT32 Features32[2];\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ Features32[0] = (UINT32)Features;\r
+ Features32[1] = (UINT32)RShiftU64 (Features, 32);\r
+\r
+ for (Selector = 0; Selector < 2; ++Selector) {\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Select the low or high half of the features.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeatureSelect),\r
+ sizeof Selector, &Selector);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Write that half.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeature),\r
+ sizeof Features32[Selector], &Features32[Selector]);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetQueueAddress (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN VRING *Ring\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+ EFI_STATUS Status;\r
+ UINT64 Address;\r
+ UINT16 Enable;\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ Address = (UINTN)Ring->Desc;\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueDesc),\r
+ sizeof Address, &Address);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Address = (UINTN)Ring->Avail.Flags;\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueAvail),\r
+ sizeof Address, &Address);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Address = (UINTN)Ring->Used.Flags;\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueUsed),\r
+ sizeof Address, &Address);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Enable = 1;\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueEnable),\r
+ sizeof Enable, &Enable);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetQueueSel (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT16 Index\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 = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
+ sizeof Index, &Index);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetQueueNotify (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT16 Index\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+ EFI_STATUS Status;\r
+ UINT16 SavedQueueSelect;\r
+ UINT16 NotifyOffset;\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ //\r
+ // Read NotifyOffset first. NotifyOffset is queue specific, so we have\r
+ // to stash & restore the current queue selector around it.\r
+ //\r
+ // So, start with saving the current queue selector.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
+ sizeof SavedQueueSelect, &SavedQueueSelect);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Select the requested queue.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
+ sizeof Index, &Index);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Read the QueueNotifyOff field.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueNotifyOff),\r
+ sizeof NotifyOffset, &NotifyOffset);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Re-select the original queue.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
+ sizeof SavedQueueSelect, &SavedQueueSelect);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // We can now kick the queue.\r
+ //\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->NotifyConfig, TRUE,\r
+ NotifyOffset * Dev->NotifyOffsetMultiplier,\r
+ sizeof Index, &Index);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetQueueAlign (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT32 Alignment\r
+ )\r
+{\r
+ return (Alignment == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetPageSize (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT32 PageSize\r
+ )\r
+{\r
+ return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10GetQueueNumMax (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ OUT UINT16 *QueueNumMax\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 = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSize),\r
+ sizeof *QueueNumMax, QueueNumMax);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetQueueNum (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT16 QueueSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT16 CurrentSize;\r
+\r
+ //\r
+ // This member function is required for VirtIo MMIO, and a no-op in\r
+ // VirtIo PCI 0.9.5. In VirtIo 1.0, drivers can theoretically use this\r
+ // member to reduce memory consumption, but none of our drivers do. So\r
+ // just check that they set the size that is already in effect.\r
+ //\r
+ Status = Virtio10GetQueueNumMax (This, &CurrentSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ return (CurrentSize == QueueSize) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10GetDeviceStatus (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ OUT UINT8 *DeviceStatus\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 = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),\r
+ sizeof *DeviceStatus, DeviceStatus);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10SetDeviceStatus (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINT8 DeviceStatus\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 = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),\r
+ sizeof DeviceStatus, &DeviceStatus);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10WriteDevice (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINTN FieldOffset,\r
+ IN UINTN FieldSize,\r
+ IN UINT64 Value\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 = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, TRUE,\r
+ FieldOffset, FieldSize, &Value);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10ReadDevice (\r
+ IN VIRTIO_DEVICE_PROTOCOL *This,\r
+ IN UINTN FieldOffset,\r
+ IN UINTN FieldSize,\r
+ IN UINTN BufferSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Dev;\r
+ EFI_STATUS Status;\r
+\r
+ if (FieldSize != BufferSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
+\r
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, FALSE,\r
+ FieldOffset, FieldSize, Buffer);\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC CONST VIRTIO_DEVICE_PROTOCOL mVirtIoTemplate = {\r
+ VIRTIO_SPEC_REVISION (1, 0, 0),\r
+ 0, // SubSystemDeviceId, filled in dynamically\r
+ Virtio10GetDeviceFeatures,\r
+ Virtio10SetGuestFeatures,\r
+ Virtio10SetQueueAddress,\r
+ Virtio10SetQueueSel,\r
+ Virtio10SetQueueNotify,\r
+ Virtio10SetQueueAlign,\r
+ Virtio10SetPageSize,\r
+ Virtio10GetQueueNumMax,\r
+ Virtio10SetQueueNum,\r
+ Virtio10GetDeviceStatus,\r
+ Virtio10SetDeviceStatus,\r
+ Virtio10WriteDevice,\r
+ Virtio10ReadDevice\r
+};\r
+\r
+\r
+//\r
+// EFI_DRIVER_BINDING_PROTOCOL member functions\r
+//\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10BindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ PCI_TYPE00 Pci;\r
+\r
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ (VOID **)&PciIo, This->DriverBindingHandle,\r
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0,\r
+ sizeof Pci / sizeof (UINT32), &Pci);\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseProtocol;\r
+ }\r
+\r
+ //\r
+ // Recognize non-transitional modern devices. Also, we'll have to parse the\r
+ // PCI capability list, so make sure the CapabilityPtr field will be valid.\r
+ //\r
+ if (Pci.Hdr.VendorId == VIRTIO_VENDOR_ID &&\r
+ Pci.Hdr.DeviceId >= 0x1040 &&\r
+ Pci.Hdr.DeviceId <= 0x107F &&\r
+ Pci.Hdr.RevisionID >= 0x01 &&\r
+ Pci.Device.SubsystemID >= 0x40 &&\r
+ (Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+CloseProtocol:\r
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle, DeviceHandle);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10BindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ VIRTIO_1_0_DEV *Device;\r
+ EFI_STATUS Status;\r
+ PCI_TYPE00 Pci;\r
+ UINT64 SetAttributes;\r
+\r
+ Device = AllocateZeroPool (sizeof *Device);\r
+ if (Device == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Device->Signature = VIRTIO_1_0_SIGNATURE;\r
+ CopyMem (&Device->VirtIo, &mVirtIoTemplate, sizeof mVirtIoTemplate);\r
+\r
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ (VOID **)&Device->PciIo, This->DriverBindingHandle,\r
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeDevice;\r
+ }\r
+\r
+ Status = Device->PciIo->Pci.Read (Device->PciIo, EfiPciIoWidthUint32, 0,\r
+ sizeof Pci / sizeof (UINT32), &Pci);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ClosePciIo;\r
+ }\r
+\r
+ Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040;\r
+\r
+ Status = ParseCapabilities (Device, Pci.Device.CapabilityPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ClosePciIo;\r
+ }\r
+\r
+ Status = Device->PciIo->Attributes (Device->PciIo,\r
+ EfiPciIoAttributeOperationGet, 0,\r
+ &Device->OriginalPciAttributes);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ClosePciIo;\r
+ }\r
+\r
+ SetAttributes = 0;\r
+ UpdateAttributes (&Device->CommonConfig, &SetAttributes);\r
+ UpdateAttributes (&Device->NotifyConfig, &SetAttributes);\r
+ UpdateAttributes (&Device->SpecificConfig, &SetAttributes);\r
+ Status = Device->PciIo->Attributes (Device->PciIo,\r
+ EfiPciIoAttributeOperationEnable, SetAttributes,\r
+ NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ClosePciIo;\r
+ }\r
+\r
+ Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,\r
+ &Device->VirtIo);\r
+ if (EFI_ERROR (Status)) {\r
+ goto RestorePciAttributes;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+RestorePciAttributes:\r
+ Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
+ Device->OriginalPciAttributes, NULL);\r
+\r
+ClosePciIo:\r
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle, DeviceHandle);\r
+\r
+FreeDevice:\r
+ FreePool (Device);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10BindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
+ VIRTIO_1_0_DEV *Device;\r
+\r
+ Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
+ (VOID **)&VirtIo, This->DriverBindingHandle,\r
+ DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Device = VIRTIO_1_0_FROM_VIRTIO_DEVICE (VirtIo);\r
+\r
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid, &Device->VirtIo);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
+ Device->OriginalPciAttributes, NULL);\r
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle, DeviceHandle);\r
+ FreePool (Device);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {\r
+ &Virtio10BindingSupported,\r
+ &Virtio10BindingStart,\r
+ &Virtio10BindingStop,\r
+ 0x10, // Version\r
+ NULL, // ImageHandle, to be overwritten\r
+ NULL // DriverBindingHandle, to be overwritten\r
+};\r
+\r
+\r
+//\r
+// EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL\r
+// implementations\r
+//\r
+\r
+STATIC\r
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
+ { "eng;en", L"Virtio 1.0 PCI Driver" },\r
+ { NULL, NULL }\r
+};\r
+\r
+STATIC\r
+EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10GetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mDriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN)(This == &mComponentName) // Iso639Language\r
+ );\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10GetDeviceName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_HANDLE ChildHandle,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+STATIC\r
+EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
+ &Virtio10GetDriverName,\r
+ &Virtio10GetDeviceName,\r
+ "eng"\r
+};\r
+\r
+STATIC\r
+EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &Virtio10GetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &Virtio10GetDeviceName,\r
+ "en"\r
+};\r
+\r
+\r
+//\r
+// Entry point of this driver\r
+//\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+Virtio10EntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &mDriverBinding,\r
+ ImageHandle,\r
+ &mComponentName,\r
+ &mComponentName2\r
+ );\r
+}\r