Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.mar...@arm.com>
---
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c      |  652 +++++++++++++++++++++
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h      |  267 +++++++++
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf |   43 ++
 OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c   |  288 +++++++++
 4 files changed, 1250 insertions(+), 0 deletions(-)
 create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c
 create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h
 create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
 create mode 100644 OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c

diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c
new file mode 100644
index 0000000..816506d
--- /dev/null
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.c
@@ -0,0 +1,652 @@
+/** @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,                                    // SubSystemDeviceId
+  VirtioPciGetDeviceFeatures,           // GetDeviceFeatures
+  VirtioPciGetGuestFeatures,            // GetGuestFeatures
+  VirtioPciSetGuestFeatures,            // SetGuestFeatures
+  VirtioPciGetQueueAddress,             // GetQueueAddress
+  VirtioPciSetQueueAddress,             // SetQueueAddress
+  VirtioPciSetQueueSel,                 // SetQueueSel
+  VirtioPciSetQueueNotify,              // SetQueueNotify
+  VirtioPciSetQueueAlignment,           // SetQueueAlignment
+  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
+  driver-specific VIRTIO_CFG_READ() macros.
+
+  @param[in] PciIo        Source 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.
+
+  @param[in] PciIo        Target 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 = 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-blk PCI discovery, we do not support
+                           the device.
+
+  @return                  Error codes from the OpenProtocol() boot service or
+                           the PciIo protocol.
+
+**/
+
+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 == 0x1AF4) &&
+        (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-blk PCI device).
+
+  @retval EFI_SUCCESS      Setup complete.
+
+  @retval EFI_UNSUPPORTED  The driver is unable to work with the virtio ring or
+                           virtio-blk attributes the host provides.
+
+  @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;
+  }
+  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 = 20;
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+  Uninitialize the internals of a virtio-blk 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
+  )
+{
+  //
+  // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
+  // VirtioPciSetDeviceStatus() returns, the host will have learned to stay
+  // away from the old comms area.
+  //
+  VirtioPciSetDeviceStatus (&Device->VirtioDevice, 0);
+}
+
+/**
+
+  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-blk PCI device, it
+                                is now accessibla via EFI_BLOCK_IO_PROTOCOL.
+
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+
+  @return                       Error codes from the OpenProtocol() boot
+                                service, the PciIo protocol, VirtioBlkInit(),
+                                or the InstallProtocolInterface() boot service.
+
+**/
+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-blk 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 virtio-blk device.
+  //
+
+  // Copy protocol template
+  CopyMem (&Device->VirtioDevice, &mDeviceProtocolTemplate,
+      sizeof (VIRTIO_DEVICE_PROTOCOL));
+
+  Status = VirtioPciInit (Device);
+  if (EFI_ERROR (Status)) {
+    goto RestorePciAttributes;
+  }
+
+  //
+  // Setup complete, attempt to export the driver instance's BlockIo 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).
+
+**/
+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 BlockIo 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
+           );
+}
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h
new file mode 100644
index 0000000..da5881f
--- /dev/null
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDevice.h
@@ -0,0 +1,267 @@
+/** @file
+
+  Internal definitions for the VirtIo PCI Device driver
+
+  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.
+
+**/
+
+#ifndef _VIRTIO_PCI_DEVICE_DXE_H_
+#define _VIRTIO_PCI_DEVICE_DXE_H_
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/VirtioDevice.h>
+
+#include <IndustryStandard/Virtio.h>
+
+#define OFFSET_OF_VNET(Field)         OFFSET_OF (VIRTIO_NET_CONFIG, Field)
+#define SIZE_OF_VNET(Field)           (sizeof ((VIRTIO_NET_CONFIG *) 0)->Field)
+#define VIRTIO_PCI_DEVICE_SIGNATURE   SIGNATURE_32 ('V', 'P', 'C', 'I')
+
+#define VIRTIO_CFG_WRITE(Dev, Offset, Size, Value) \
+    VirtioPciIoWrite (Dev, Offset, Size, (Value))
+#define VIRTIO_CFG_READ(Dev, Offset, Size, Buf) \
+    VirtioPciIoRead (Dev, Offset, Size, Size, Buf)
+
+typedef struct {
+  UINT32                 Signature;
+  VIRTIO_DEVICE_PROTOCOL VirtioDevice;
+  EFI_PCI_IO_PROTOCOL   *PciIo;
+  UINT64                 OriginalPciAttributes;
+  UINT32                 DeviceSpecificConfigurationOffset;
+} VIRTIO_PCI_DEVICE;
+
+#define VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE(Device) \
+    CR (Device, VIRTIO_PCI_DEVICE, VirtioDevice, VIRTIO_PCI_DEVICE_SIGNATURE)
+
+
+/**
+
+  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-blk PCI discovery, we do not support
+                           the device.
+
+  @return                  Error codes from the OpenProtocol() boot service or
+                           the PciIo protocol.
+
+**/
+
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  DeviceHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  );
+
+
+/**
+
+  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-blk PCI device, it
+                                is now accessibla via EFI_BLOCK_IO_PROTOCOL.
+
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+
+  @return                       Error codes from the OpenProtocol() boot
+                                service, the PciIo protocol, VirtioBlkInit(),
+                                or the InstallProtocolInterface() boot service.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  DeviceHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  );
+
+
+/**
+
+  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).
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  DeviceHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciIoRead (
+  IN  VIRTIO_PCI_DEVICE         *Dev,
+  IN  UINTN                      FieldOffset,
+  IN  UINTN                      FieldSize,
+  IN  UINTN                      BufferSize,
+  OUT VOID                       *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciIoWrite (
+  IN  VIRTIO_PCI_DEVICE         *Dev,
+  IN UINTN                       FieldOffset,
+  IN UINTN                       FieldSize,
+  IN UINT64                      Value
+  );
+
+/********************************************
+ * PCI Functions for VIRTIO_DEVICE_PROTOCOL
+ *******************************************/
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceRead (
+  IN  VIRTIO_DEVICE_PROTOCOL     *This,
+  IN  UINTN                      FieldOffset,
+  IN  UINTN                      FieldSize,
+  IN  UINTN                      BufferSize,
+  OUT VOID                       *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciDeviceWrite (
+  IN VIRTIO_DEVICE_PROTOCOL      *This,
+  IN UINTN                       FieldOffset,
+  IN UINTN                       FieldSize,
+  IN UINT64                      Value
+  );
+
+UINT32
+EFIAPI
+VirtioPciGetDeviceFeatures (
+  VIRTIO_DEVICE_PROTOCOL         *This
+  );
+
+UINT32
+EFIAPI
+VirtioPciGetGuestFeatures (
+  VIRTIO_DEVICE_PROTOCOL         *This
+  );
+
+UINT32
+EFIAPI
+VirtioPciGetQueueAddress (
+  VIRTIO_DEVICE_PROTOCOL         *This
+  );
+
+UINT16
+EFIAPI
+VirtioPciGetQueueSize (
+  VIRTIO_DEVICE_PROTOCOL         *This
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueAlignment (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT32                         Alignment
+  );
+
+UINT8
+EFIAPI
+VirtioPciGetDeviceStatus (
+  VIRTIO_DEVICE_PROTOCOL         *This
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetGuestFeatures (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT32                         Features
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueAddress (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT32                         Address
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueSel (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT16                         Sel
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueNotify (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT16                         Index
+  );
+
+VOID
+EFIAPI
+VirtioPciSetQueueSize (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT16                         Size
+  );
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetDeviceStatus (
+  VIRTIO_DEVICE_PROTOCOL         *This,
+  UINT8                          DeviceStatus
+  );
+
+#endif // _VIRTIO_PCI_DEVICE_DXE_H_
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
new file mode 100644
index 0000000..98c5226
--- /dev/null
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
@@ -0,0 +1,43 @@
+## @file
+# This driver produces the VirtIo Device Protocol instances for VirtIo PCI
+# Device
+#
+# 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.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = VirtioPciDeviceDxe
+  FILE_GUID                      = 83dd3b39-7caf-4fac-a542-e050b767e3a7
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = VirtioPciDeviceEntryPoint
+
+[Sources]
+  VirtioPciDevice.c
+  VirtioPciFunctions.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiPciIoProtocolGuid    ## TO_START
+  gVirtioDeviceProtocolGuid
diff --git a/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c 
b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c
new file mode 100644
index 0000000..15c4348
--- /dev/null
+++ b/OvmfPkg/VirtioPciDeviceDxe/VirtioPciFunctions.c
@@ -0,0 +1,288 @@
+/** @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 <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include "VirtioPciDevice.h"
+
+#define VIRTIO_FEATURE_BITS_OFFSET              0
+#define VIRTIO_GUEST_FEATURE_BITS_OFFSET        4
+#define VIRTIO_QUEUE_ADDRESS_OFFSET             8
+#define VIRTIO_QUEUE_SIZE_OFFSET                12
+#define VIRTIO_QUEUE_SELECT_OFFSET              14
+#define VIRTIO_QUEUE_NOTIFY_OFFSET              16
+#define VIRTIO_QUEUE_DEVICE_STATUS_OFFSET       18
+#define VIRTIO_QUEUE_DEVICE_ISR_OFFSET          19
+
+/**
+
+  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
+  driver-specific VIRTIO_CFG_READ() macros.
+
+  @param[in] PciIo        Source 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
+VirtioPciDeviceRead (
+  IN  VIRTIO_DEVICE_PROTOCOL    *This,
+  IN  UINTN                     FieldOffset,
+  IN  UINTN                     FieldSize,
+  IN  UINTN                     BufferSize,
+  OUT VOID                      *Buffer
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VirtioPciIoRead (Dev,
+      Dev->DeviceSpecificConfigurationOffset + FieldOffset,
+      FieldSize, BufferSize, Buffer);
+}
+
+/**
+
+  Write a word into Region 0 of the device specified by PciIo.
+
+  @param[in] PciIo        Target 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
+VirtioPciDeviceWrite (
+  IN VIRTIO_DEVICE_PROTOCOL *This,
+  IN UINTN                  FieldOffset,
+  IN UINTN                  FieldSize,
+  IN UINT64                 Value
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VIRTIO_CFG_WRITE (Dev,
+      Dev->DeviceSpecificConfigurationOffset + FieldOffset, FieldSize, Value);
+}
+
+UINT32
+EFIAPI
+VirtioPciGetDeviceFeatures (
+  VIRTIO_DEVICE_PROTOCOL *This
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+  UINT32                    FeatureBits;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  VIRTIO_CFG_READ (Dev, VIRTIO_FEATURE_BITS_OFFSET, sizeof (UINT32),
+      &FeatureBits);
+
+  return FeatureBits;
+}
+
+UINT32
+EFIAPI
+VirtioPciGetGuestFeatures (
+  VIRTIO_DEVICE_PROTOCOL *This
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+  UINT32                    FeatureBits;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  VIRTIO_CFG_READ (Dev, VIRTIO_GUEST_FEATURE_BITS_OFFSET, sizeof (UINT32),
+      &FeatureBits);
+
+  return FeatureBits;
+}
+
+UINT32
+EFIAPI
+VirtioPciGetQueueAddress (
+  VIRTIO_DEVICE_PROTOCOL *This
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+  UINT32                    QueueAddress;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  VIRTIO_CFG_READ (Dev, VIRTIO_QUEUE_ADDRESS_OFFSET, sizeof (UINT32),
+      &QueueAddress);
+
+  return QueueAddress;
+}
+
+UINT16
+EFIAPI
+VirtioPciGetQueueSize (
+  VIRTIO_DEVICE_PROTOCOL *This
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+  UINT16                    QueueSize;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  VIRTIO_CFG_READ (Dev, VIRTIO_QUEUE_SIZE_OFFSET, sizeof (UINT16), &QueueSize);
+  return QueueSize;
+}
+
+UINT8
+EFIAPI
+VirtioPciGetDeviceStatus (
+  VIRTIO_DEVICE_PROTOCOL *This
+  )
+{
+  VIRTIO_PCI_DEVICE         *Dev;
+  UINT8                     DeviceStatus;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  VIRTIO_CFG_READ (Dev, VIRTIO_QUEUE_DEVICE_STATUS_OFFSET, sizeof (UINT8),
+      &DeviceStatus);
+
+  return DeviceStatus;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetGuestFeatures (
+  VIRTIO_DEVICE_PROTOCOL    *This,
+  UINT32                    Features
+  )
+{
+  VIRTIO_PCI_DEVICE *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VIRTIO_CFG_WRITE (Dev, VIRTIO_GUEST_FEATURE_BITS_OFFSET,
+      sizeof (UINT32), Features);
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueAddress (
+  VIRTIO_DEVICE_PROTOCOL    *This,
+  UINT32                    Address
+  )
+{
+  VIRTIO_PCI_DEVICE *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_ADDRESS_OFFSET, sizeof (UINT32),
+      Address);
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueSel (
+  VIRTIO_DEVICE_PROTOCOL    *This,
+  UINT16                    Sel
+  )
+{
+  VIRTIO_PCI_DEVICE *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_SELECT_OFFSET, sizeof (UINT16),
+      Sel);
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueAlignment (
+  VIRTIO_DEVICE_PROTOCOL *This,
+  UINT32                  Alignment
+  )
+{
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetQueueNotify (
+  VIRTIO_DEVICE_PROTOCOL *This,
+  UINT16                 Index
+  )
+{
+  VIRTIO_PCI_DEVICE *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_NOTIFY_OFFSET, sizeof (UINT16),
+      Index);
+}
+
+VOID
+EFIAPI
+VirtioPciSetQueueSize (
+  VIRTIO_DEVICE_PROTOCOL *This,
+  UINT16                 Size
+  )
+{
+  //
+  // This function is only applicable in Virtio-MMIO.
+  // (The QueueSize field is read-only in Virtio proper (PCI))
+  //
+}
+
+EFI_STATUS
+EFIAPI
+VirtioPciSetDeviceStatus (
+  VIRTIO_DEVICE_PROTOCOL *This,
+  UINT8                  DeviceStatus
+  )
+{
+  VIRTIO_PCI_DEVICE *Dev;
+
+  Dev = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+  return VIRTIO_CFG_WRITE (Dev, VIRTIO_QUEUE_DEVICE_STATUS_OFFSET,
+      sizeof (UINT8), DeviceStatus);
+}
-- 
1.7.0.4


------------------------------------------------------------------------------
LIMITED TIME SALE - Full Year of Microsoft Training For Just $49.99!
1,500+ hours of tutorials including VisualStudio 2012, Windows 8, SharePoint
2013, SQL 2012, MVC 4, more. BEST VALUE: New Multi-Library Power Pack includes
Mobile, Cloud, Java, and UX Design. Lowest price ever! Ends 9/20/13. 
http://pubads.g.doubleclick.net/gampad/clk?id=58041151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to