summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c1150
1 files changed, 1150 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c
new file mode 100644
index 00000000..c2deaef9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/Virtio10Dxe/Virtio10.c
@@ -0,0 +1,1150 @@
+/** @file
+ A non-transitional driver for VirtIo 1.0 PCI devices.
+
+ Copyright (C) 2016, Red Hat, Inc.
+ Copyright (C) 2017, AMD Inc, All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Virtio.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/VirtioDevice.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PciCapLib.h>
+#include <Library/PciCapPciIoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "Virtio10.h"
+
+
+//
+// Utility functions
+//
+
+/**
+ Transfer data between the caller and a register in a virtio-1.0 register
+ block.
+
+ @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents
+ the device.
+
+ @param[in] Config The "fat pointer" structure that identifies the
+ register block to access.
+
+ @param[in] Write TRUE if the register should be written, FALSE if
+ the register should be read.
+
+ @param[in] FieldOffset The offset of the register within the register
+ block.
+
+ @param[in] FieldSize The size of the register within the register
+ block. Can be one of 1, 2, 4 and 8. Accesses to
+ 8-byte registers are broken up into two 4-byte
+ accesses.
+
+ @param[in,out] Buffer When Write is TRUE, the register is written with
+ data from Buffer. When Write is FALSE, the caller
+ receives the register value into Buffer.
+
+ @retval EFI_SUCCESS Register access successful.
+
+ @retval EFI_INVALID_PARAMETER The register block pointed-to by Config
+ doesn't exist; or FieldOffset and FieldSize
+ would overflow the register block; or
+ FieldSize is invalid.
+
+ @return Error codes from
+ EFI_PCI_IO_PROTOCOL.(Io|Mem).(Read|Write)
+ member functions.
+**/
+STATIC
+EFI_STATUS
+Virtio10Transfer (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN VIRTIO_1_0_CONFIG *Config,
+ IN BOOLEAN Write,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN OUT VOID *Buffer
+ )
+{
+ UINTN Count;
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+ EFI_PCI_IO_PROTOCOL_ACCESS *BarType;
+ EFI_PCI_IO_PROTOCOL_IO_MEM Access;
+
+ if (!Config->Exists ||
+ FieldSize > Config->Length ||
+ FieldOffset > Config->Length - FieldSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Count = 1;
+ switch (FieldSize) {
+ case 1:
+ Width = EfiPciIoWidthUint8;
+ break;
+
+ case 2:
+ Width = EfiPciIoWidthUint16;
+ break;
+
+ case 8:
+ Count = 2;
+ //
+ // fall through
+ //
+
+ case 4:
+ Width = EfiPciIoWidthUint32;
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BarType = (Config->BarType == Virtio10BarTypeMem) ? &PciIo->Mem : &PciIo->Io;
+ Access = Write ? BarType->Write : BarType->Read;
+
+ return Access (PciIo, Width, Config->Bar, Config->Offset + FieldOffset,
+ Count, Buffer);
+}
+
+
+/**
+ Determine if a PCI BAR is IO or MMIO.
+
+ @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents the
+ device.
+
+ @param[in] BarIndex The number of the BAR whose type the caller is
+ interested in.
+
+ @param[out] BarType On output, a VIRTIO_1_0_BAR_TYPE value that gives the
+ type of the BAR.
+
+ @retval EFI_SUCCESS The BAR type has been recognized and stored in
+ BarType.
+
+ @retval EFI_UNSUPPORTED The BAR type couldn't be identified.
+
+ @return Error codes from
+ EFI_PCI_IO_PROTOCOL.GetBarAttributes().
+**/
+STATIC
+EFI_STATUS
+GetBarType (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 BarIndex,
+ OUT VIRTIO_1_0_BAR_TYPE *BarType
+ )
+{
+ EFI_STATUS Status;
+ VOID *Resources;
+
+ Status = PciIo->GetBarAttributes (PciIo, BarIndex, NULL, &Resources);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EFI_UNSUPPORTED;
+
+ if (*(UINT8 *)Resources == ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR) {
+ EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
+
+ Descriptor = Resources;
+ switch (Descriptor->ResType) {
+ case ACPI_ADDRESS_SPACE_TYPE_MEM:
+ *BarType = Virtio10BarTypeMem;
+ Status = EFI_SUCCESS;
+ break;
+
+ case ACPI_ADDRESS_SPACE_TYPE_IO:
+ *BarType = Virtio10BarTypeIo;
+ Status = EFI_SUCCESS;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ FreePool (Resources);
+ return Status;
+}
+
+
+/*
+ Traverse the PCI capabilities list of a virtio-1.0 device, and capture the
+ locations of the interesting virtio-1.0 register blocks.
+
+ @param[in,out] Device The VIRTIO_1_0_DEV structure that identifies
+ the device. On input, the caller is responsible
+ that the Device->PciIo member be live, and that
+ the CommonConfig, NotifyConfig,
+ NotifyOffsetMultiplier and SpecificConfig
+ members be zeroed. On output, said members
+ will have been updated from the PCI
+ capabilities found.
+
+ @retval EFI_SUCCESS Traversal successful.
+
+ @return Error codes from PciCapPciIoLib, PciCapLib, and the
+ GetBarType() helper function.
+*/
+STATIC
+EFI_STATUS
+ParseCapabilities (
+ IN OUT VIRTIO_1_0_DEV *Device
+ )
+{
+ EFI_STATUS Status;
+ PCI_CAP_DEV *PciDevice;
+ PCI_CAP_LIST *CapList;
+ UINT16 VendorInstance;
+ PCI_CAP *VendorCap;
+
+ Status = PciCapPciIoDeviceInit (Device->PciIo, &PciDevice);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = PciCapListInit (PciDevice, &CapList);
+ if (EFI_ERROR (Status)) {
+ goto UninitPciDevice;
+ }
+
+ for (VendorInstance = 0;
+ !EFI_ERROR (PciCapListFindCap (CapList, PciCapNormal,
+ EFI_PCI_CAPABILITY_ID_VENDOR, VendorInstance,
+ &VendorCap));
+ VendorInstance++) {
+ UINT8 CapLen;
+ VIRTIO_PCI_CAP VirtIoCap;
+ VIRTIO_1_0_CONFIG *ParsedConfig;
+
+ //
+ // Big enough to accommodate a VIRTIO_PCI_CAP structure?
+ //
+ Status = PciCapRead (PciDevice, VendorCap,
+ OFFSET_OF (EFI_PCI_CAPABILITY_VENDOR_HDR, Length), &CapLen,
+ sizeof CapLen);
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+ if (CapLen < sizeof VirtIoCap) {
+ //
+ // Too small, move to next.
+ //
+ continue;
+ }
+
+ //
+ // Read interesting part of capability.
+ //
+ Status = PciCapRead (PciDevice, VendorCap, 0, &VirtIoCap, sizeof VirtIoCap);
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+
+ switch (VirtIoCap.ConfigType) {
+ case VIRTIO_PCI_CAP_COMMON_CFG:
+ ParsedConfig = &Device->CommonConfig;
+ break;
+ case VIRTIO_PCI_CAP_NOTIFY_CFG:
+ ParsedConfig = &Device->NotifyConfig;
+ break;
+ case VIRTIO_PCI_CAP_DEVICE_CFG:
+ ParsedConfig = &Device->SpecificConfig;
+ break;
+ default:
+ //
+ // Capability is not interesting.
+ //
+ continue;
+ }
+
+ //
+ // Save the location of the register block into ParsedConfig.
+ //
+ Status = GetBarType (Device->PciIo, VirtIoCap.Bar, &ParsedConfig->BarType);
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+ ParsedConfig->Bar = VirtIoCap.Bar;
+ ParsedConfig->Offset = VirtIoCap.Offset;
+ ParsedConfig->Length = VirtIoCap.Length;
+
+ if (VirtIoCap.ConfigType == VIRTIO_PCI_CAP_NOTIFY_CFG) {
+ //
+ // This capability has an additional field called NotifyOffsetMultiplier;
+ // parse it too.
+ //
+ if (CapLen < sizeof VirtIoCap + sizeof Device->NotifyOffsetMultiplier) {
+ //
+ // Too small, move to next.
+ //
+ continue;
+ }
+
+ Status = PciCapRead (PciDevice, VendorCap, sizeof VirtIoCap,
+ &Device->NotifyOffsetMultiplier,
+ sizeof Device->NotifyOffsetMultiplier);
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+ }
+
+ //
+ // Capability parsed successfully.
+ //
+ ParsedConfig->Exists = TRUE;
+ }
+
+ ASSERT_EFI_ERROR (Status);
+
+UninitCapList:
+ PciCapListUninit (CapList);
+
+UninitPciDevice:
+ PciCapPciIoDeviceUninit (PciDevice);
+
+ return Status;
+}
+
+
+/**
+ Accumulate the BAR type of a virtio-1.0 register block into a UINT64
+ attribute map, such that the latter is suitable for enabling IO / MMIO
+ decoding with EFI_PCI_IO_PROTOCOL.Attributes().
+
+ @param[in] Config The "fat pointer" structure that identifies the
+ register block. It is allowed for the register
+ block not to exist.
+
+ @param[in,out] Attributes On output, if the register block exists,
+ EFI_PCI_IO_ATTRIBUTE_MEMORY or
+ EFI_PCI_IO_ATTRIBUTE_IO is OR-ed into Attributes,
+ according to the register block's BAR type.
+**/
+STATIC
+VOID
+UpdateAttributes (
+ IN VIRTIO_1_0_CONFIG *Config,
+ IN OUT UINT64 *Attributes
+ )
+{
+ if (Config->Exists) {
+ *Attributes |= (Config->BarType == Virtio10BarTypeMem) ?
+ EFI_PCI_IO_ATTRIBUTE_MEMORY:
+ EFI_PCI_IO_ATTRIBUTE_IO;
+ }
+}
+
+
+//
+// VIRTIO_DEVICE_PROTOCOL member functions
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10GetDeviceFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT64 *DeviceFeatures
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ UINT32 Selector;
+ UINT32 Features32[2];
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ for (Selector = 0; Selector < 2; ++Selector) {
+ EFI_STATUS Status;
+
+ //
+ // Select the low or high half of the features.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeatureSelect),
+ sizeof Selector, &Selector);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Fetch that half.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeature),
+ sizeof Features32[Selector], &Features32[Selector]);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ *DeviceFeatures = LShiftU64 (Features32[1], 32) | Features32[0];
+ return EFI_SUCCESS;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetGuestFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT64 Features
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ UINT32 Selector;
+ UINT32 Features32[2];
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Features32[0] = (UINT32)Features;
+ Features32[1] = (UINT32)RShiftU64 (Features, 32);
+
+ for (Selector = 0; Selector < 2; ++Selector) {
+ EFI_STATUS Status;
+
+ //
+ // Select the low or high half of the features.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeatureSelect),
+ sizeof Selector, &Selector);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Write that half.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeature),
+ sizeof Features32[Selector], &Features32[Selector]);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetQueueAddress (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VRING *Ring,
+ IN UINT64 RingBaseShift
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+ UINT64 Address;
+ UINT16 Enable;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Address = (UINTN)Ring->Desc;
+ Address += RingBaseShift;
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueDesc),
+ sizeof Address, &Address);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Address = (UINTN)Ring->Avail.Flags;
+ Address += RingBaseShift;
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueAvail),
+ sizeof Address, &Address);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Address = (UINTN)Ring->Used.Flags;
+ Address += RingBaseShift;
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueUsed),
+ sizeof Address, &Address);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Enable = 1;
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueEnable),
+ sizeof Enable, &Enable);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetQueueSel (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 Index
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),
+ sizeof Index, &Index);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetQueueNotify (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 Index
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+ UINT16 SavedQueueSelect;
+ UINT16 NotifyOffset;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ //
+ // Read NotifyOffset first. NotifyOffset is queue specific, so we have
+ // to stash & restore the current queue selector around it.
+ //
+ // So, start with saving the current queue selector.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),
+ sizeof SavedQueueSelect, &SavedQueueSelect);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Select the requested queue.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),
+ sizeof Index, &Index);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Read the QueueNotifyOff field.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueNotifyOff),
+ sizeof NotifyOffset, &NotifyOffset);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Re-select the original queue.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),
+ sizeof SavedQueueSelect, &SavedQueueSelect);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // We can now kick the queue.
+ //
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->NotifyConfig, TRUE,
+ NotifyOffset * Dev->NotifyOffsetMultiplier,
+ sizeof Index, &Index);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetQueueAlign (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT32 Alignment
+ )
+{
+ return (Alignment == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetPageSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT32 PageSize
+ )
+{
+ return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10GetQueueNumMax (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT16 *QueueNumMax
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSize),
+ sizeof *QueueNumMax, QueueNumMax);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetQueueNum (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 QueueSize
+ )
+{
+ EFI_STATUS Status;
+ UINT16 CurrentSize;
+
+ //
+ // This member function is required for VirtIo MMIO, and a no-op in
+ // VirtIo PCI 0.9.5. In VirtIo 1.0, drivers can theoretically use this
+ // member to reduce memory consumption, but none of our drivers do. So
+ // just check that they set the size that is already in effect.
+ //
+ Status = Virtio10GetQueueNumMax (This, &CurrentSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ return (CurrentSize == QueueSize) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10GetDeviceStatus (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT8 *DeviceStatus
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),
+ sizeof *DeviceStatus, DeviceStatus);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10SetDeviceStatus (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT8 DeviceStatus
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,
+ OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),
+ sizeof DeviceStatus, &DeviceStatus);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10WriteDevice (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINT64 Value
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, TRUE,
+ FieldOffset, FieldSize, &Value);
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10ReadDevice (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ if (FieldSize != BufferSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, FALSE,
+ FieldOffset, FieldSize, Buffer);
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10AllocateSharedPages (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN Pages,
+ IN OUT VOID **HostAddress
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Dev->PciIo->AllocateBuffer (
+ Dev->PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Pages,
+ HostAddress,
+ EFI_PCI_ATTRIBUTE_MEMORY_CACHED
+ );
+ return Status;
+}
+
+STATIC
+VOID
+EFIAPI
+Virtio10FreeSharedPages (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ )
+{
+ VIRTIO_1_0_DEV *Dev;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Dev->PciIo->FreeBuffer (
+ Dev->PciIo,
+ Pages,
+ HostAddress
+ );
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10MapSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VIRTIO_MAP_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_1_0_DEV *Dev;
+ EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ //
+ // Map VIRTIO_MAP_OPERATION to EFI_PCI_IO_PROTOCOL_OPERATION
+ //
+ switch (Operation) {
+ case VirtioOperationBusMasterRead:
+ PciIoOperation = EfiPciIoOperationBusMasterRead;
+ break;
+ case VirtioOperationBusMasterWrite:
+ PciIoOperation = EfiPciIoOperationBusMasterWrite;
+ break;
+ case VirtioOperationBusMasterCommonBuffer:
+ PciIoOperation = EfiPciIoOperationBusMasterCommonBuffer;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Dev->PciIo->Map (
+ Dev->PciIo,
+ PciIoOperation,
+ HostAddress,
+ NumberOfBytes,
+ DeviceAddress,
+ Mapping
+ );
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10UnmapSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_1_0_DEV *Dev;
+
+ Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);
+
+ Status = Dev->PciIo->Unmap (
+ Dev->PciIo,
+ Mapping
+ );
+
+ return Status;
+}
+
+STATIC CONST VIRTIO_DEVICE_PROTOCOL mVirtIoTemplate = {
+ VIRTIO_SPEC_REVISION (1, 0, 0),
+ 0, // SubSystemDeviceId, filled in dynamically
+ Virtio10GetDeviceFeatures,
+ Virtio10SetGuestFeatures,
+ Virtio10SetQueueAddress,
+ Virtio10SetQueueSel,
+ Virtio10SetQueueNotify,
+ Virtio10SetQueueAlign,
+ Virtio10SetPageSize,
+ Virtio10GetQueueNumMax,
+ Virtio10SetQueueNum,
+ Virtio10GetDeviceStatus,
+ Virtio10SetDeviceStatus,
+ Virtio10WriteDevice,
+ Virtio10ReadDevice,
+ Virtio10AllocateSharedPages,
+ Virtio10FreeSharedPages,
+ Virtio10MapSharedBuffer,
+ Virtio10UnmapSharedBuffer
+};
+
+
+//
+// EFI_DRIVER_BINDING_PROTOCOL member functions
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10BindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ (VOID **)&PciIo, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0,
+ sizeof Pci / sizeof (UINT32), &Pci);
+ if (EFI_ERROR (Status)) {
+ goto CloseProtocol;
+ }
+
+ Status = EFI_UNSUPPORTED;
+ //
+ // Recognize non-transitional modern devices. Also, we'll have to parse the
+ // PCI capability list, so make sure the CapabilityPtr field will be valid.
+ //
+ if (Pci.Hdr.VendorId == VIRTIO_VENDOR_ID &&
+ Pci.Hdr.DeviceId >= 0x1040 &&
+ Pci.Hdr.DeviceId <= 0x107F &&
+ Pci.Hdr.RevisionID >= 0x01 &&
+ Pci.Device.SubsystemID >= 0x40 &&
+ (Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {
+ //
+ // The virtio-vga device is special. It can be driven both as a VGA device
+ // with a linear framebuffer, and through its underlying, modern,
+ // virtio-gpu-pci device, which has no linear framebuffer itself. For
+ // compatibility with guest OSes that insist on inheriting a linear
+ // framebuffer from the firmware, we should leave virtio-vga to
+ // QemuVideoDxe, and support only virtio-gpu-pci here.
+ //
+ // Both virtio-vga and virtio-gpu-pci have DeviceId 0x1050, but only the
+ // former has device class PCI_CLASS_DISPLAY_VGA.
+ //
+ if (Pci.Hdr.DeviceId != 0x1050 || !IS_PCI_VGA (&Pci)) {
+ Status = EFI_SUCCESS;
+ }
+ }
+
+CloseProtocol:
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10BindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ VIRTIO_1_0_DEV *Device;
+ EFI_STATUS Status;
+ PCI_TYPE00 Pci;
+ UINT64 SetAttributes;
+
+ Device = AllocateZeroPool (sizeof *Device);
+ if (Device == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Device->Signature = VIRTIO_1_0_SIGNATURE;
+ CopyMem (&Device->VirtIo, &mVirtIoTemplate, sizeof mVirtIoTemplate);
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ (VOID **)&Device->PciIo, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ goto FreeDevice;
+ }
+
+ Status = Device->PciIo->Pci.Read (Device->PciIo, EfiPciIoWidthUint32, 0,
+ sizeof Pci / sizeof (UINT32), &Pci);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040;
+
+ Status = ParseCapabilities (Device);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ Status = Device->PciIo->Attributes (Device->PciIo,
+ EfiPciIoAttributeOperationGet, 0,
+ &Device->OriginalPciAttributes);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ SetAttributes = (EFI_PCI_IO_ATTRIBUTE_BUS_MASTER |
+ EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE);
+ UpdateAttributes (&Device->CommonConfig, &SetAttributes);
+ UpdateAttributes (&Device->NotifyConfig, &SetAttributes);
+ UpdateAttributes (&Device->SpecificConfig, &SetAttributes);
+ Status = Device->PciIo->Attributes (Device->PciIo,
+ EfiPciIoAttributeOperationEnable, SetAttributes,
+ NULL);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ Status = gBS->InstallProtocolInterface (&DeviceHandle,
+ &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,
+ &Device->VirtIo);
+ if (EFI_ERROR (Status)) {
+ goto RestorePciAttributes;
+ }
+
+ return EFI_SUCCESS;
+
+RestorePciAttributes:
+ Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,
+ Device->OriginalPciAttributes, NULL);
+
+ClosePciIo:
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+FreeDevice:
+ FreePool (Device);
+
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10BindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;
+ VIRTIO_1_0_DEV *Device;
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ (VOID **)&VirtIo, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Device = VIRTIO_1_0_FROM_VIRTIO_DEVICE (VirtIo);
+
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,
+ &gVirtioDeviceProtocolGuid, &Device->VirtIo);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,
+ Device->OriginalPciAttributes, NULL);
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+ FreePool (Device);
+
+ return EFI_SUCCESS;
+}
+
+
+STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
+ &Virtio10BindingSupported,
+ &Virtio10BindingStart,
+ &Virtio10BindingStop,
+ 0x10, // Version
+ NULL, // ImageHandle, to be overwritten
+ NULL // DriverBindingHandle, to be overwritten
+};
+
+
+//
+// EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL
+// implementations
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+ { "eng;en", L"Virtio 1.0 PCI Driver" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL mComponentName;
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10GetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &mComponentName) // Iso639Language
+ );
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+Virtio10GetDeviceName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
+ &Virtio10GetDriverName,
+ &Virtio10GetDeviceName,
+ "eng"
+};
+
+STATIC
+EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &Virtio10GetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &Virtio10GetDeviceName,
+ "en"
+};
+
+
+//
+// Entry point of this driver
+//
+
+EFI_STATUS
+EFIAPI
+Virtio10EntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &mDriverBinding,
+ ImageHandle,
+ &mComponentName,
+ &mComponentName2
+ );
+}