diff options
Diffstat (limited to 'OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c')
-rw-r--r-- | OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c | 1347 |
1 files changed, 680 insertions, 667 deletions
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c index b120d5f2c..2647bd391 100644 --- a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c +++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c @@ -1,667 +1,680 @@ -/** @file - - This driver produces Virtio Device Protocol instances for Virtio PCI devices. - - Copyright (C) 2012, Red Hat, Inc. - Copyright (c) 2012, Intel Corporation. All rights reserved.<BR> - Copyright (C) 2013, ARM Ltd. - - This program and the accompanying materials are licensed and made available - under the terms and conditions of the BSD License which accompanies this - distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT - WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include <IndustryStandard/Pci.h> -#include <Library/BaseMemoryLib.h> -#include <Library/DebugLib.h> -#include <Library/MemoryAllocationLib.h> -#include <Library/UefiBootServicesTableLib.h> -#include <Library/UefiLib.h> - -#include "VirtioPciDevice.h" - -STATIC VIRTIO_DEVICE_PROTOCOL mDeviceProtocolTemplate = { - 0, // Revision - 0, // SubSystemDeviceId - VirtioPciGetDeviceFeatures, // GetDeviceFeatures - VirtioPciSetGuestFeatures, // SetGuestFeatures - VirtioPciGetQueueAddress, // GetQueueAddress - VirtioPciSetQueueAddress, // SetQueueAddress - VirtioPciSetQueueSel, // SetQueueSel - VirtioPciSetQueueNotify, // SetQueueNotify - VirtioPciSetQueueAlignment, // SetQueueAlignment - VirtioPciSetPageSize, // SetPageSize - VirtioPciGetQueueSize, // GetQueueNumMax - VirtioPciSetQueueSize, // SetQueueNum - VirtioPciGetDeviceStatus, // GetDeviceStatus - VirtioPciSetDeviceStatus, // SetDeviceStatus - VirtioPciDeviceWrite, // WriteDevice - VirtioPciDeviceRead // ReadDevice -}; - -/** - - Read a word from Region 0 of the device specified by PciIo. - - Region 0 must be an iomem region. This is an internal function for the PCI - implementation of the protocol. - - @param[in] Dev Virtio PCI device. - - @param[in] FieldOffset Source offset. - - @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. - - @param[in] BufferSize Number of bytes available in the target buffer. Must - equal FieldSize. - - @param[out] Buffer Target buffer. - - - @return Status code returned by PciIo->Io.Read(). - -**/ -EFI_STATUS -EFIAPI -VirtioPciIoRead ( - IN VIRTIO_PCI_DEVICE *Dev, - IN UINTN FieldOffset, - IN UINTN FieldSize, - IN UINTN BufferSize, - OUT VOID *Buffer - ) -{ - UINTN Count; - EFI_PCI_IO_PROTOCOL_WIDTH Width; - EFI_PCI_IO_PROTOCOL *PciIo; - - // The BufferSize must be a multiple of FieldSize - ASSERT ((BufferSize % FieldSize) == 0); - - PciIo = Dev->PciIo; - Count = BufferSize / FieldSize; - - switch (FieldSize) { - case 1: - Width = EfiPciIoWidthUint8; - break; - - case 2: - Width = EfiPciIoWidthUint16; - break; - - case 8: - // The 64bit PCI I/O is broken down into two 32bit reads to prevent - // any alignment or width issues. - // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write(): - // - // The I/O operations are carried out exactly as requested. The caller - // is responsible for any alignment and I/O width issues which the - // bus, device, platform, or type of I/O might require. For example on - // some platforms, width requests of EfiPciIoWidthUint64 do not work - Count = Count * 2; - // fall through - - case 4: - Width = EfiPciIoWidthUint32; - break; - - default: - ASSERT (FALSE); - return EFI_INVALID_PARAMETER; - } - - return PciIo->Io.Read ( - PciIo, - Width, - PCI_BAR_IDX0, - FieldOffset, - Count, - Buffer - ); -} - -/** - - Write a word into Region 0 of the device specified by PciIo. - - Region 0 must be an iomem region. This is an internal function for the PCI - implementation of the protocol. - - @param[in] Dev Virtio PCI device. - - @param[in] FieldOffset Destination offset. - - @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. - - @param[in] Value Little endian value to write, converted to UINT64. - The least significant FieldSize bytes will be used. - - - @return Status code returned by PciIo->Io.Write(). - -**/ -EFI_STATUS -EFIAPI -VirtioPciIoWrite ( - IN VIRTIO_PCI_DEVICE *Dev, - IN UINTN FieldOffset, - IN UINTN FieldSize, - IN UINT64 Value - ) -{ - UINTN Count; - EFI_PCI_IO_PROTOCOL_WIDTH Width; - EFI_PCI_IO_PROTOCOL *PciIo; - - PciIo = Dev->PciIo; - Count = 1; - - switch (FieldSize) { - case 1: - Width = EfiPciIoWidthUint8; - break; - - case 2: - Width = EfiPciIoWidthUint16; - break; - - case 8: - // The 64bit PCI I/O is broken down into two 32bit writes to prevent - // any alignment or width issues. - // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write(): - // - // The I/O operations are carried out exactly as requested. The caller - // is responsible for any alignment and I/O width issues which the - // bus, device, platform, or type of I/O might require. For example on - // some platforms, width requests of EfiPciIoWidthUint64 do not work - Count = Count * 2; - // fall through - - case 4: - Width = EfiPciIoWidthUint32; - break; - - default: - ASSERT (FALSE); - return EFI_INVALID_PARAMETER; - } - - return PciIo->Io.Write ( - PciIo, - Width, - PCI_BAR_IDX0, - FieldOffset, - Count, - &Value - ); -} - -/** - - Device probe function for this driver. - - The DXE core calls this function for any given device in order to see if the - driver can drive the device. - - @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object - incorporating this driver (independently of - any device). - - @param[in] DeviceHandle The device to probe. - - @param[in] RemainingDevicePath Relevant only for bus drivers, ignored. - - - @retval EFI_SUCCESS The driver supports the device being probed. - - @retval EFI_UNSUPPORTED Based on virtio-pci discovery, we do not support - the device. - - @return Error codes from the OpenProtocol() boot service or - the PciIo protocol. - -**/ -STATIC -EFI_STATUS -EFIAPI -VirtioPciDeviceBindingSupported ( - 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; - - // - // Attempt to open the device with the PciIo set of interfaces. On success, - // the protocol is "instantiated" for the PCI device. Covers duplicate open - // attempts (EFI_ALREADY_STARTED). - // - Status = gBS->OpenProtocol ( - DeviceHandle, // candidate device - &gEfiPciIoProtocolGuid, // for generic PCI access - (VOID **)&PciIo, // handle to instantiate - This->DriverBindingHandle, // requestor driver identity - DeviceHandle, // ControllerHandle, according to - // the UEFI Driver Model - EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to - // the device; to be released - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Read entire PCI configuration header for more extensive check ahead. - // - Status = PciIo->Pci.Read ( - PciIo, // (protocol, device) - // handle - EfiPciIoWidthUint32, // access width & copy - // mode - 0, // Offset - sizeof Pci / sizeof (UINT32), // Count - &Pci // target buffer - ); - - if (Status == EFI_SUCCESS) { - // - // virtio-0.9.5, 2.1 PCI Discovery - // - if ((Pci.Hdr.VendorId == VIRTIO_VENDOR_ID) && - (Pci.Hdr.DeviceId >= 0x1000) && - (Pci.Hdr.DeviceId <= 0x103F) && - (Pci.Hdr.RevisionID == 0x00)) { - Status = EFI_SUCCESS; - } else { - Status = EFI_UNSUPPORTED; - } - } - - // - // We needed PCI IO access only transitorily, to see whether we support the - // device or not. - // - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, - This->DriverBindingHandle, DeviceHandle); - - return Status; -} - -/** - - Initialize the VirtIo PCI Device - - @param[in, out] Dev The driver instance to configure. The caller is - responsible for Device->PciIo's validity (ie. working IO - access to the underlying virtio-pci device). - - @retval EFI_SUCCESS Setup complete. - - @retval EFI_UNSUPPORTED The underlying IO device doesn't support the - provided address offset and read size. - - @return Error codes from PciIo->Pci.Read(). - -**/ -STATIC -EFI_STATUS -EFIAPI -VirtioPciInit ( - IN OUT VIRTIO_PCI_DEVICE *Device - ) -{ - EFI_STATUS Status; - EFI_PCI_IO_PROTOCOL *PciIo; - PCI_TYPE00 Pci; - - ASSERT (Device != NULL); - PciIo = Device->PciIo; - ASSERT (PciIo != NULL); - ASSERT (PciIo->Pci.Read != NULL); - - Status = PciIo->Pci.Read ( - PciIo, // (protocol, device) - // handle - EfiPciIoWidthUint32, // access width & copy - // mode - 0, // Offset - sizeof (Pci) / sizeof (UINT32), // Count - &Pci // target buffer - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // Copy protocol template - CopyMem (&Device->VirtioDevice, &mDeviceProtocolTemplate, - sizeof (VIRTIO_DEVICE_PROTOCOL)); - - // Initialize the protocol interface attributes - Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5); - Device->VirtioDevice.SubSystemDeviceId = Pci.Device.SubsystemID; - - // Note: We don't support the MSI-X capability. If we did, - // the offset would become 24 after enabling MSI-X. - Device->DeviceSpecificConfigurationOffset = - VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI; - - return EFI_SUCCESS; -} - -/** - - Uninitialize the internals of a virtio-pci device that has been successfully - set up with VirtioPciInit(). - - @param[in, out] Dev The device to clean up. - -**/ - -STATIC -VOID -EFIAPI -VirtioPciUninit ( - IN OUT VIRTIO_PCI_DEVICE *Device - ) -{ - // Note: This function mirrors VirtioPciInit() that does not allocate any - // resources - there's nothing to free here. -} - -/** - - After we've pronounced support for a specific device in - DriverBindingSupported(), we start managing said device (passed in by the - Driver Exeuction Environment) with the following service. - - See DriverBindingSupported() for specification references. - - @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object - incorporating this driver (independently of - any device). - - @param[in] DeviceHandle The supported device to drive. - - @param[in] RemainingDevicePath Relevant only for bus drivers, ignored. - - - @retval EFI_SUCCESS Driver instance has been created and - initialized for the virtio-pci device, it - is now accessible via VIRTIO_DEVICE_PROTOCOL. - - @retval EFI_OUT_OF_RESOURCES Memory allocation failed. - - @return Error codes from the OpenProtocol() boot - service, the PciIo protocol, VirtioPciInit(), - or the InstallProtocolInterface() boot service. - -**/ -STATIC -EFI_STATUS -EFIAPI -VirtioPciDeviceBindingStart ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE DeviceHandle, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath - ) -{ - VIRTIO_PCI_DEVICE *Device; - EFI_STATUS Status; - - Device = (VIRTIO_PCI_DEVICE *) AllocateZeroPool (sizeof *Device); - if (Device == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, - (VOID **)&Device->PciIo, This->DriverBindingHandle, - DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); - if (EFI_ERROR (Status)) { - goto FreeVirtioPci; - } - - // - // We must retain and ultimately restore the original PCI attributes of the - // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers / - // 18.3.2 Start() and Stop(). - // - // The third parameter ("Attributes", input) is ignored by the Get operation. - // The fourth parameter ("Result", output) is ignored by the Enable and Set - // operations. - // - // For virtio-pci we only need IO space access. - // - Status = Device->PciIo->Attributes (Device->PciIo, - EfiPciIoAttributeOperationGet, 0, &Device->OriginalPciAttributes); - if (EFI_ERROR (Status)) { - goto ClosePciIo; - } - - Status = Device->PciIo->Attributes (Device->PciIo, - EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_IO, NULL); - if (EFI_ERROR (Status)) { - goto ClosePciIo; - } - - // - // PCI IO access granted, configure protocol instance - // - - Status = VirtioPciInit (Device); - if (EFI_ERROR (Status)) { - goto RestorePciAttributes; - } - - // - // Setup complete, attempt to export the driver instance's VirtioDevice - // interface. - // - Device->Signature = VIRTIO_PCI_DEVICE_SIGNATURE; - Status = gBS->InstallProtocolInterface (&DeviceHandle, - &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE, - &Device->VirtioDevice); - if (EFI_ERROR (Status)) { - goto UninitDev; - } - - return EFI_SUCCESS; - -UninitDev: - VirtioPciUninit (Device); - -RestorePciAttributes: - Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet, - Device->OriginalPciAttributes, NULL); - -ClosePciIo: - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, - This->DriverBindingHandle, DeviceHandle); - -FreeVirtioPci: - FreePool (Device); - - return Status; -} - -/** - - Stop driving the Virtio PCI device - - @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object - incorporating this driver (independently of any - device). - - @param[in] DeviceHandle Stop driving this device. - - @param[in] NumberOfChildren Since this function belongs to a device driver - only (as opposed to a bus driver), the caller - environment sets NumberOfChildren to zero, and - we ignore it. - - @param[in] ChildHandleBuffer Ignored (corresponding to NumberOfChildren). - - @retval EFI_SUCCESS Driver instance has been stopped and the PCI - configuration attributes have been restored. - - @return Error codes from the OpenProtocol() or - CloseProtocol(), UninstallProtocolInterface() - boot services. - -**/ -STATIC -EFI_STATUS -EFIAPI -VirtioPciDeviceBindingStop ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE DeviceHandle, - IN UINTN NumberOfChildren, - IN EFI_HANDLE *ChildHandleBuffer - ) -{ - EFI_STATUS Status; - VIRTIO_DEVICE_PROTOCOL *VirtioDevice; - VIRTIO_PCI_DEVICE *Device; - - Status = gBS->OpenProtocol ( - DeviceHandle, // candidate device - &gVirtioDeviceProtocolGuid, // retrieve the VirtIo iface - (VOID **)&VirtioDevice, // target pointer - This->DriverBindingHandle, // requestor driver identity - DeviceHandle, // requesting lookup for dev. - EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added - ); - if (EFI_ERROR (Status)) { - return Status; - } - - Device = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice); - - // - // Handle Stop() requests for in-use driver instances gracefully. - // - Status = gBS->UninstallProtocolInterface (DeviceHandle, - &gVirtioDeviceProtocolGuid, &Device->VirtioDevice); - if (EFI_ERROR (Status)) { - return Status; - } - - VirtioPciUninit (Device); - - Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet, - Device->OriginalPciAttributes, NULL); - - Status = gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, - This->DriverBindingHandle, DeviceHandle); - - FreePool (Device); - - return Status; -} - - -// -// The static object that groups the Supported() (ie. probe), Start() and -// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata -// C, 10.1 EFI Driver Binding Protocol. -// -STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { - &VirtioPciDeviceBindingSupported, - &VirtioPciDeviceBindingStart, - &VirtioPciDeviceBindingStop, - 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers - NULL, // ImageHandle, to be overwritten by - // EfiLibInstallDriverBindingComponentName2() in VirtioPciEntryPoint() - NULL // DriverBindingHandle, ditto -}; - - -// -// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and -// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name -// in English, for display on standard console devices. This is recommended for -// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's -// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. -// -STATIC -EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { - { "eng;en", L"Virtio PCI Driver" }, - { NULL, NULL } -}; - -STATIC -EFI_COMPONENT_NAME_PROTOCOL gComponentName; - -EFI_STATUS -EFIAPI -VirtioPciGetDriverName ( - IN EFI_COMPONENT_NAME_PROTOCOL *This, - IN CHAR8 *Language, - OUT CHAR16 **DriverName - ) -{ - return LookupUnicodeString2 ( - Language, - This->SupportedLanguages, - mDriverNameTable, - DriverName, - (BOOLEAN)(This == &gComponentName) // Iso639Language - ); -} - -EFI_STATUS -EFIAPI -VirtioPciGetDeviceName ( - 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 gComponentName = { - &VirtioPciGetDriverName, - &VirtioPciGetDeviceName, - "eng" // SupportedLanguages, ISO 639-2 language codes -}; - -STATIC -EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { - (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioPciGetDriverName, - (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioPciGetDeviceName, - "en" // SupportedLanguages, RFC 4646 language codes -}; - - -// -// Entry point of this driver. -// -EFI_STATUS -EFIAPI -VirtioPciDeviceEntryPoint ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - return EfiLibInstallDriverBindingComponentName2 ( - ImageHandle, - SystemTable, - &gDriverBinding, - ImageHandle, - &gComponentName, - &gComponentName2 - ); -} +/** @file
+
+ This driver produces Virtio Device Protocol instances for Virtio PCI devices.
+
+ Copyright (C) 2012, Red Hat, Inc.
+ Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2013, ARM Ltd.
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Pci.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "VirtioPciDevice.h"
+
+STATIC VIRTIO_DEVICE_PROTOCOL mDeviceProtocolTemplate = {
+ 0, // Revision
+ 0, // SubSystemDeviceId
+ VirtioPciGetDeviceFeatures, // GetDeviceFeatures
+ VirtioPciSetGuestFeatures, // SetGuestFeatures
+ VirtioPciGetQueueAddress, // GetQueueAddress
+ VirtioPciSetQueueAddress, // SetQueueAddress
+ VirtioPciSetQueueSel, // SetQueueSel
+ VirtioPciSetQueueNotify, // SetQueueNotify
+ VirtioPciSetQueueAlignment, // SetQueueAlignment
+ VirtioPciSetPageSize, // SetPageSize
+ VirtioPciGetQueueSize, // GetQueueNumMax
+ VirtioPciSetQueueSize, // SetQueueNum
+ VirtioPciGetDeviceStatus, // GetDeviceStatus
+ VirtioPciSetDeviceStatus, // SetDeviceStatus
+ VirtioPciDeviceWrite, // WriteDevice
+ VirtioPciDeviceRead // ReadDevice
+};
+
+/**
+
+ Read a word from Region 0 of the device specified by PciIo.
+
+ Region 0 must be an iomem region. This is an internal function for the PCI
+ implementation of the protocol.
+
+ @param[in] Dev Virtio PCI device.
+
+ @param[in] FieldOffset Source offset.
+
+ @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.
+
+ @param[in] BufferSize Number of bytes available in the target buffer. Must
+ equal FieldSize.
+
+ @param[out] Buffer Target buffer.
+
+
+ @return Status code returned by PciIo->Io.Read().
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioPciIoRead (
+ IN VIRTIO_PCI_DEVICE *Dev,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ UINTN Count;
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ ASSERT (FieldSize == BufferSize);
+
+ PciIo = Dev->PciIo;
+ Count = 1;
+
+ switch (FieldSize) {
+ case 1:
+ Width = EfiPciIoWidthUint8;
+ break;
+
+ case 2:
+ Width = EfiPciIoWidthUint16;
+ break;
+
+ case 8:
+ //
+ // The 64bit PCI I/O is broken down into two 32bit reads to prevent
+ // any alignment or width issues.
+ // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write():
+ //
+ // The I/O operations are carried out exactly as requested. The caller
+ // is responsible for any alignment and I/O width issues which the
+ // bus, device, platform, or type of I/O might require. For example on
+ // some platforms, width requests of EfiPciIoWidthUint64 do not work.
+ //
+ Count = 2;
+
+ //
+ // fall through
+ //
+ case 4:
+ Width = EfiPciIoWidthUint32;
+ break;
+
+ default:
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return PciIo->Io.Read (
+ PciIo,
+ Width,
+ PCI_BAR_IDX0,
+ FieldOffset,
+ Count,
+ Buffer
+ );
+}
+
+/**
+
+ Write a word into Region 0 of the device specified by PciIo.
+
+ Region 0 must be an iomem region. This is an internal function for the PCI
+ implementation of the protocol.
+
+ @param[in] Dev Virtio PCI device.
+
+ @param[in] FieldOffset Destination offset.
+
+ @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.
+
+ @param[in] Value Little endian value to write, converted to UINT64.
+ The least significant FieldSize bytes will be used.
+
+
+ @return Status code returned by PciIo->Io.Write().
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioPciIoWrite (
+ IN VIRTIO_PCI_DEVICE *Dev,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINT64 Value
+ )
+{
+ UINTN Count;
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+
+ PciIo = Dev->PciIo;
+ Count = 1;
+
+ switch (FieldSize) {
+ case 1:
+ Width = EfiPciIoWidthUint8;
+ break;
+
+ case 2:
+ Width = EfiPciIoWidthUint16;
+ break;
+
+ case 8:
+ //
+ // The 64bit PCI I/O is broken down into two 32bit writes to prevent
+ // any alignment or width issues.
+ // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write():
+ //
+ // The I/O operations are carried out exactly as requested. The caller
+ // is responsible for any alignment and I/O width issues which the
+ // bus, device, platform, or type of I/O might require. For example on
+ // some platforms, width requests of EfiPciIoWidthUint64 do not work
+ //
+ Count = Count * 2;
+
+ //
+ // fall through
+ //
+ case 4:
+ Width = EfiPciIoWidthUint32;
+ break;
+
+ default:
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return PciIo->Io.Write (
+ PciIo,
+ Width,
+ PCI_BAR_IDX0,
+ FieldOffset,
+ Count,
+ &Value
+ );
+}
+
+/**
+
+ Device probe function for this driver.
+
+ The DXE core calls this function for any given device in order to see if the
+ driver can drive the device.
+
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
+ incorporating this driver (independently of
+ any device).
+
+ @param[in] DeviceHandle The device to probe.
+
+ @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.
+
+
+ @retval EFI_SUCCESS The driver supports the device being probed.
+
+ @retval EFI_UNSUPPORTED Based on virtio-pci discovery, we do not support
+ the device.
+
+ @return Error codes from the OpenProtocol() boot service or
+ the PciIo protocol.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceBindingSupported (
+ 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;
+
+ //
+ // Attempt to open the device with the PciIo set of interfaces. On success,
+ // the protocol is "instantiated" for the PCI device. Covers duplicate open
+ // attempts (EFI_ALREADY_STARTED).
+ //
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gEfiPciIoProtocolGuid, // for generic PCI access
+ (VOID **)&PciIo, // handle to instantiate
+ This->DriverBindingHandle, // requestor driver identity
+ DeviceHandle, // ControllerHandle, according to
+ // the UEFI Driver Model
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to
+ // the device; to be released
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Read entire PCI configuration header for more extensive check ahead.
+ //
+ Status = PciIo->Pci.Read (
+ PciIo, // (protocol, device)
+ // handle
+ EfiPciIoWidthUint32, // access width & copy
+ // mode
+ 0, // Offset
+ sizeof Pci / sizeof (UINT32), // Count
+ &Pci // target buffer
+ );
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // virtio-0.9.5, 2.1 PCI Discovery
+ //
+ if ((Pci.Hdr.VendorId == VIRTIO_VENDOR_ID) &&
+ (Pci.Hdr.DeviceId >= 0x1000) &&
+ (Pci.Hdr.DeviceId <= 0x103F) &&
+ (Pci.Hdr.RevisionID == 0x00)) {
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_UNSUPPORTED;
+ }
+ }
+
+ //
+ // We needed PCI IO access only transitorily, to see whether we support the
+ // device or not.
+ //
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ return Status;
+}
+
+/**
+
+ Initialize the VirtIo PCI Device
+
+ @param[in, out] Dev The driver instance to configure. The caller is
+ responsible for Device->PciIo's validity (ie. working IO
+ access to the underlying virtio-pci device).
+
+ @retval EFI_SUCCESS Setup complete.
+
+ @retval EFI_UNSUPPORTED The underlying IO device doesn't support the
+ provided address offset and read size.
+
+ @return Error codes from PciIo->Pci.Read().
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioPciInit (
+ IN OUT VIRTIO_PCI_DEVICE *Device
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ ASSERT (Device != NULL);
+ PciIo = Device->PciIo;
+ ASSERT (PciIo != NULL);
+ ASSERT (PciIo->Pci.Read != NULL);
+
+ Status = PciIo->Pci.Read (
+ PciIo, // (protocol, device)
+ // handle
+ EfiPciIoWidthUint32, // access width & copy
+ // mode
+ 0, // Offset
+ sizeof (Pci) / sizeof (UINT32), // Count
+ &Pci // target buffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Copy protocol template
+ //
+ CopyMem (&Device->VirtioDevice, &mDeviceProtocolTemplate,
+ sizeof (VIRTIO_DEVICE_PROTOCOL));
+
+ //
+ // Initialize the protocol interface attributes
+ //
+ Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5);
+ Device->VirtioDevice.SubSystemDeviceId = Pci.Device.SubsystemID;
+
+ //
+ // Note: We don't support the MSI-X capability. If we did,
+ // the offset would become 24 after enabling MSI-X.
+ //
+ Device->DeviceSpecificConfigurationOffset =
+ VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI;
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Uninitialize the internals of a virtio-pci device that has been successfully
+ set up with VirtioPciInit().
+
+ @param[in, out] Dev The device to clean up.
+
+**/
+
+STATIC
+VOID
+EFIAPI
+VirtioPciUninit (
+ IN OUT VIRTIO_PCI_DEVICE *Device
+ )
+{
+ // Note: This function mirrors VirtioPciInit() that does not allocate any
+ // resources - there's nothing to free here.
+}
+
+/**
+
+ After we've pronounced support for a specific device in
+ DriverBindingSupported(), we start managing said device (passed in by the
+ Driver Exeuction Environment) with the following service.
+
+ See DriverBindingSupported() for specification references.
+
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
+ incorporating this driver (independently of
+ any device).
+
+ @param[in] DeviceHandle The supported device to drive.
+
+ @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.
+
+
+ @retval EFI_SUCCESS Driver instance has been created and
+ initialized for the virtio-pci device, it
+ is now accessible via VIRTIO_DEVICE_PROTOCOL.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+
+ @return Error codes from the OpenProtocol() boot
+ service, the PciIo protocol, VirtioPciInit(),
+ or the InstallProtocolInterface() boot service.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ VIRTIO_PCI_DEVICE *Device;
+ EFI_STATUS Status;
+
+ Device = (VIRTIO_PCI_DEVICE *) AllocateZeroPool (sizeof *Device);
+ if (Device == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ (VOID **)&Device->PciIo, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ goto FreeVirtioPci;
+ }
+
+ //
+ // We must retain and ultimately restore the original PCI attributes of the
+ // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers /
+ // 18.3.2 Start() and Stop().
+ //
+ // The third parameter ("Attributes", input) is ignored by the Get operation.
+ // The fourth parameter ("Result", output) is ignored by the Enable and Set
+ // operations.
+ //
+ // For virtio-pci we only need IO space access.
+ //
+ Status = Device->PciIo->Attributes (Device->PciIo,
+ EfiPciIoAttributeOperationGet, 0, &Device->OriginalPciAttributes);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ Status = Device->PciIo->Attributes (Device->PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_IO, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ //
+ // PCI IO access granted, configure protocol instance
+ //
+
+ Status = VirtioPciInit (Device);
+ if (EFI_ERROR (Status)) {
+ goto RestorePciAttributes;
+ }
+
+ //
+ // Setup complete, attempt to export the driver instance's VirtioDevice
+ // interface.
+ //
+ Device->Signature = VIRTIO_PCI_DEVICE_SIGNATURE;
+ Status = gBS->InstallProtocolInterface (&DeviceHandle,
+ &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,
+ &Device->VirtioDevice);
+ if (EFI_ERROR (Status)) {
+ goto UninitDev;
+ }
+
+ return EFI_SUCCESS;
+
+UninitDev:
+ VirtioPciUninit (Device);
+
+RestorePciAttributes:
+ Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,
+ Device->OriginalPciAttributes, NULL);
+
+ClosePciIo:
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+FreeVirtioPci:
+ FreePool (Device);
+
+ return Status;
+}
+
+/**
+
+ Stop driving the Virtio PCI device
+
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
+ incorporating this driver (independently of any
+ device).
+
+ @param[in] DeviceHandle Stop driving this device.
+
+ @param[in] NumberOfChildren Since this function belongs to a device driver
+ only (as opposed to a bus driver), the caller
+ environment sets NumberOfChildren to zero, and
+ we ignore it.
+
+ @param[in] ChildHandleBuffer Ignored (corresponding to NumberOfChildren).
+
+ @retval EFI_SUCCESS Driver instance has been stopped and the PCI
+ configuration attributes have been restored.
+
+ @return Error codes from the OpenProtocol() or
+ CloseProtocol(), UninstallProtocolInterface()
+ boot services.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_DEVICE_PROTOCOL *VirtioDevice;
+ VIRTIO_PCI_DEVICE *Device;
+
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gVirtioDeviceProtocolGuid, // retrieve the VirtIo iface
+ (VOID **)&VirtioDevice, // target pointer
+ This->DriverBindingHandle, // requestor driver identity
+ DeviceHandle, // requesting lookup for dev.
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Device = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice);
+
+ //
+ // Handle Stop() requests for in-use driver instances gracefully.
+ //
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,
+ &gVirtioDeviceProtocolGuid, &Device->VirtioDevice);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ VirtioPciUninit (Device);
+
+ Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,
+ Device->OriginalPciAttributes, NULL);
+
+ Status = gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ FreePool (Device);
+
+ return Status;
+}
+
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+ &VirtioPciDeviceBindingSupported,
+ &VirtioPciDeviceBindingStart,
+ &VirtioPciDeviceBindingStop,
+ 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
+ NULL, // ImageHandle, to be overwritten by
+ // EfiLibInstallDriverBindingComponentName2() in VirtioPciEntryPoint()
+ NULL // DriverBindingHandle, ditto
+};
+
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+ { "eng;en", L"Virtio PCI Driver" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+
+EFI_STATUS
+EFIAPI
+VirtioPciGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gComponentName) // Iso639Language
+ );
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciGetDeviceName (
+ 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 gComponentName = {
+ &VirtioPciGetDriverName,
+ &VirtioPciGetDeviceName,
+ "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+STATIC
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioPciGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioPciGetDeviceName,
+ "en" // SupportedLanguages, RFC 4646 language codes
+};
+
+
+//
+// Entry point of this driver.
+//
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDriverBinding,
+ ImageHandle,
+ &gComponentName,
+ &gComponentName2
+ );
+}
|