]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: introduce virtio-blk driver
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 8 Oct 2012 07:32:59 +0000 (07:32 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 8 Oct 2012 07:32:59 +0000 (07:32 +0000)
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13798 6f19259b-4bc3-4df7-8a09-765794883524

OvmfPkg/OvmfPkgIa32.dsc
OvmfPkg/OvmfPkgIa32.fdf
OvmfPkg/OvmfPkgIa32X64.dsc
OvmfPkg/OvmfPkgIa32X64.fdf
OvmfPkg/OvmfPkgX64.dsc
OvmfPkg/OvmfPkgX64.fdf
OvmfPkg/VirtioBlkDxe/Virtio.h [new file with mode: 0644]
OvmfPkg/VirtioBlkDxe/VirtioBlk.c [new file with mode: 0644]
OvmfPkg/VirtioBlkDxe/VirtioBlk.h [new file with mode: 0644]
OvmfPkg/VirtioBlkDxe/VirtioBlk.inf [new file with mode: 0644]

index e1cf6f4e2532a8e39c16ce697aea7a1c883049c7..16037b37721d24647f98e691cf65d0cf6bd11338 100644 (file)
   }\r
 \r
   OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf\r
+  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf\r
   OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {\r
     <LibraryClasses>\r
       PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf\r
index 563b1bb650ecfbe6d6da28afaa829f9fa67ff447..be5e6d8deb44d28cbe215e13a93e6ee80aa22b7e 100644 (file)
@@ -180,6 +180,7 @@ INF  MdeModulePkg/Universal/Metronome/Metronome.inf
 INF  PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf\r
 \r
 INF  OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf\r
+INF  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf\r
 INF  OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf\r
 INF  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf\r
 \r
index 40fd5e97b24e16cc9e1fc5999bf1127b8a27a8d0..f73f079d07fcbc029e64983a87196f944da0d367 100644 (file)
   }\r
 \r
   OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf\r
+  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf\r
   OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {\r
     <LibraryClasses>\r
       PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf\r
index 4721368824881433041ca66ef69c7c2b0ca553c2..07faec49fe9a0f4a974dfeb5bbec0d5c624645d0 100644 (file)
@@ -180,6 +180,7 @@ INF  MdeModulePkg/Universal/Metronome/Metronome.inf
 INF  PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf\r
 \r
 INF  OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf\r
+INF  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf\r
 INF  OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf\r
 INF  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf\r
 \r
index c61d41dccbbebebf0f8f87cd599cce9524b85477..97d4ec8a9368176e832ef86a2c4598955f3c87ba 100644 (file)
   }\r
 \r
   OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf\r
+  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf\r
   OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {\r
     <LibraryClasses>\r
       PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf\r
index d74043dfc78c19a39bd13a44c841971626f095fe..615bc0a1c2f41445a1d20091b63f33e6f968573a 100644 (file)
@@ -180,6 +180,7 @@ INF  MdeModulePkg/Universal/Metronome/Metronome.inf
 INF  PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf\r
 \r
 INF  OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf\r
+INF  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf\r
 INF  OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf\r
 INF  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf\r
 \r
diff --git a/OvmfPkg/VirtioBlkDxe/Virtio.h b/OvmfPkg/VirtioBlkDxe/Virtio.h
new file mode 100644 (file)
index 0000000..5e2e8f4
--- /dev/null
@@ -0,0 +1,176 @@
+/** @file\r
+\r
+  Type and macro definitions corresponding to the virtio-0.9.5 specification.\r
+\r
+  Copyright (C) 2012, Red Hat, Inc.\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution. The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Base.h>\r
+\r
+\r
+//\r
+// Data in the communication area is defined as packed and accessed as\r
+// volatile.\r
+//\r
+// Some structures contain arrays with dynamically determined size. In such\r
+// cases the array and its sibling fields are replaced with pointers.\r
+//\r
+// All indices (variables and fields named *Idx) are free-running and wrap\r
+// around after 0xFFFF. The queue size reported by the host is always an\r
+// integral power of 2, not greater than 32768. Actual array indices are\r
+// consistently calculated by taking the remainder of a given Idx object modulo\r
+// QueueSize. Since 0x10000 is an integral multiple of the QueueSize, UINT16\r
+// wraparound is a correct wraparound modulo QueueSize too (it doesn't offset\r
+// the remainder class).\r
+//\r
+// virtio-0.9.5, 2.3.4 Available Ring\r
+//\r
+#define VRING_AVAIL_F_NO_INTERRUPT BIT0\r
+\r
+typedef struct {\r
+  volatile UINT16 *Flags;\r
+  volatile UINT16 *Idx;\r
+\r
+  volatile UINT16 *Ring;      // QueueSize elements\r
+  volatile UINT16 *UsedEvent; // unused as per negotiation\r
+} VRING_AVAIL;\r
+\r
+\r
+//\r
+// virtio-0.9.5, 2.3.5 Used Ring\r
+//\r
+#define VRING_USED_F_NO_NOTIFY BIT0\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT32 Id;\r
+  UINT32 Len;\r
+} VRING_USED_ELEM;\r
+#pragma pack()\r
+\r
+typedef struct {\r
+  volatile UINT16          *Flags;\r
+  volatile UINT16          *Idx;\r
+  volatile VRING_USED_ELEM *UsedElem;   // QueueSize elements\r
+  volatile UINT16          *AvailEvent; // unused as per negotiation\r
+} VRING_USED;\r
+\r
+\r
+//\r
+// virtio-0.9.5, 2.3.2 Descriptor Table\r
+//\r
+#define VRING_DESC_F_NEXT     BIT0 // more descriptors in this request\r
+#define VRING_DESC_F_WRITE    BIT1 // buffer to be written *by the host*\r
+#define VRING_DESC_F_INDIRECT BIT2 // unused\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT64 Addr;\r
+  UINT32 Len;\r
+  UINT16 Flags;\r
+  UINT16 Next;\r
+} VRING_DESC;\r
+#pragma pack()\r
+\r
+typedef struct {\r
+  UINTN               NumPages;\r
+  VOID                *Base;     // deallocate only this field\r
+  volatile VRING_DESC *Desc;     // QueueSize elements\r
+  VRING_AVAIL         Avail;\r
+  VRING_USED          Used;\r
+  UINT16              QueueSize;\r
+} VRING;\r
+\r
+\r
+//\r
+// virtio-0.9.5, 2.2.2 Virtio Header -- no MSI-X\r
+// virtio-0.9.5, Appendix D\r
+//\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT32 VhdrDeviceFeatureBits;\r
+  UINT32 VhdrGuestFeatureBits;\r
+  UINT32 VhdrQueueAddress;\r
+  UINT16 VhdrQueueSize;\r
+  UINT16 VhdrQueueSelect;\r
+  UINT16 VhdrQueueNotify;\r
+  UINT8  VhdrDeviceStatus;\r
+  UINT8  VhdrISR;\r
+  UINT64 VhdrCapacity;\r
+  UINT32 VhdrSizeMax;\r
+  UINT32 VhdrSegMax;\r
+  UINT16 VhdrCylinders;\r
+  UINT8  VhdrHeads;\r
+  UINT8  VhdrSectors;\r
+  UINT32 VhdrBlkSize;\r
+} VBLK_HDR;\r
+#pragma pack()\r
+\r
+#define OFFSET_OF_VHDR(Field) ((UINTN)(UINT8 *)&((VBLK_HDR *) 0)->Field)\r
+#define SIZE_OF_VHDR(Field)   (sizeof ((VBLK_HDR *) 0)->Field)\r
+\r
+\r
+//\r
+// virtio-0.9.5, 2.2.2.1 Device Status\r
+//\r
+#define VSTAT_ACK       BIT0\r
+#define VSTAT_DRIVER    BIT1\r
+#define VSTAT_DRIVER_OK BIT2\r
+#define VSTAT_FAILED    BIT7\r
+\r
+//\r
+// virtio-0.9.5, Appendix B: Reserved (Device-Independent) Feature Bits\r
+//\r
+#define VIRTIO_F_NOTIFY_ON_EMPTY    BIT24\r
+#define VIRTIO_F_RING_INDIRECT_DESC BIT28\r
+#define VIRTIO_F_RING_EVENT_IDX     BIT29\r
+\r
+//\r
+// virtio-0.9.5, Appendix D: Block Device\r
+//\r
+#define VIRTIO_BLK_F_BARRIER  BIT0\r
+#define VIRTIO_BLK_F_SIZE_MAX BIT1\r
+#define VIRTIO_BLK_F_SEG_MAX  BIT2\r
+#define VIRTIO_BLK_F_GEOMETRY BIT4\r
+#define VIRTIO_BLK_F_RO       BIT5\r
+#define VIRTIO_BLK_F_BLK_SIZE BIT6 // treated as "logical block size" in\r
+                                   // practice; actual host side implementation\r
+                                   // negotiates "optimal" block size\r
+                                   // separately\r
+#define VIRTIO_BLK_F_SCSI     BIT7\r
+#define VIRTIO_BLK_F_FLUSH    BIT9 // identical to "write cache enabled"\r
+\r
+\r
+//\r
+// We keep the status byte separate from the rest of the virtio-blk request\r
+// header. See description of historical scattering at the end of Appendix D:\r
+// we're going to put the status byte in a separate VRING_DESC.\r
+//\r
+#pragma pack(1)\r
+typedef struct {\r
+  UINT32 Type;\r
+  UINT32 IoPrio;\r
+  UINT64 Sector;\r
+} VIRTIO_BLK_REQ;\r
+#pragma pack()\r
+\r
+#define VIRTIO_BLK_T_IN           0x00000000\r
+#define VIRTIO_BLK_T_OUT          0x00000001\r
+#define VIRTIO_BLK_T_SCSI_CMD     0x00000002\r
+#define VIRTIO_BLK_T_SCSI_CMD_OUT 0x00000003\r
+#define VIRTIO_BLK_T_FLUSH        0x00000004\r
+#define VIRTIO_BLK_T_FLUSH_OUT    0x00000005\r
+#define VIRTIO_BLK_T_BARRIER      BIT31\r
+\r
+#define VIRTIO_BLK_S_OK           0x00\r
+#define VIRTIO_BLK_S_IOERR        0x01\r
+#define VIRTIO_BLK_S_UNSUPP       0x02\r
diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
new file mode 100644 (file)
index 0000000..9969aab
--- /dev/null
@@ -0,0 +1,1432 @@
+/** @file\r
+\r
+  This driver produces Block I/O Protocol instances for virtio-blk devices.\r
+\r
+  The implementation is basic:\r
+\r
+  - No attach/detach (ie. removable media).\r
+\r
+  - Although the non-blocking interfaces of EFI_BLOCK_IO2_PROTOCOL could be a\r
+    good match for multiple in-flight virtio-blk requests, we stick to\r
+    synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.\r
+\r
+  Copyright (C) 2012, Red Hat, Inc.\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution. The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <IndustryStandard/Pci.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+\r
+#include "VirtioBlk.h"\r
+\r
+/**\r
+\r
+  Write a word into Region 0 of the device specified by PciIo.\r
+\r
+  Region 0 must be an iomem region. This is an internal function for the\r
+  VIRTIO_CFG_WRITE() macro below.\r
+\r
+  @param[in] PciIo        Target PCI device.\r
+\r
+  @param[in] FieldOffset  Destination offset.\r
+\r
+  @param[in] FieldSize    Destination field size, must be in { 1, 2, 4, 8 }.\r
+\r
+  @param[in] Value        Little endian value to write, converted to UINT64.\r
+                          The least significant FieldSize bytes will be used.\r
+\r
+\r
+  @return  Status code returned by PciIo->Io.Write().\r
+\r
+**/\r
+STATIC\r
+EFIAPI\r
+EFI_STATUS\r
+VirtioWrite (\r
+  IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+  IN UINTN               FieldOffset,\r
+  IN UINTN               FieldSize,\r
+  IN UINT64              Value\r
+  )\r
+{\r
+  UINTN                     Count;\r
+  EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
+\r
+  Count = 1;\r
+  switch (FieldSize) {\r
+    case 1:\r
+      Width = EfiPciIoWidthUint8;\r
+      break;\r
+\r
+    case 2:\r
+      Width = EfiPciIoWidthUint16;\r
+      break;\r
+\r
+    case 8:\r
+      Count = 2;\r
+      // fall through\r
+\r
+    case 4:\r
+      Width = EfiPciIoWidthUint32;\r
+      break;\r
+\r
+    default:\r
+      ASSERT (FALSE);\r
+  }\r
+\r
+  return PciIo->Io.Write (\r
+                     PciIo,\r
+                     Width,\r
+                     PCI_BAR_IDX0,\r
+                     FieldOffset,\r
+                     Count,\r
+                     &Value\r
+                     );\r
+}\r
+\r
+\r
+/**\r
+\r
+  Read a word from Region 0 of the device specified by PciIo.\r
+\r
+  Region 0 must be an iomem region. This is an internal function for the\r
+  VIRTIO_CFG_READ() macro below.\r
+\r
+  @param[in] PciIo        Source PCI device.\r
+\r
+  @param[in] FieldOffset  Source offset.\r
+\r
+  @param[in] FieldSize    Source field size, must be in { 1, 2, 4, 8 }.\r
+\r
+  @param[in] BufferSize   Number of bytes available in the target buffer. Must\r
+                          equal FieldSize.\r
+\r
+  @param[out] Buffer      Target buffer.\r
+\r
+\r
+  @return  Status code returned by PciIo->Io.Read().\r
+\r
+**/\r
+STATIC\r
+EFIAPI\r
+EFI_STATUS\r
+VirtioRead (\r
+  IN  EFI_PCI_IO_PROTOCOL *PciIo,\r
+  IN  UINTN               FieldOffset,\r
+  IN  UINTN               FieldSize,\r
+  IN  UINTN               BufferSize,\r
+  OUT VOID                *Buffer\r
+  )\r
+{\r
+  UINTN                     Count;\r
+  EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
+\r
+  ASSERT (FieldSize == BufferSize);\r
+\r
+  Count = 1;\r
+  switch (FieldSize) {\r
+    case 1:\r
+      Width = EfiPciIoWidthUint8;\r
+      break;\r
+\r
+    case 2:\r
+      Width = EfiPciIoWidthUint16;\r
+      break;\r
+\r
+    case 8:\r
+      Count = 2;\r
+      // fall through\r
+\r
+    case 4:\r
+      Width = EfiPciIoWidthUint32;\r
+      break;\r
+\r
+    default:\r
+      ASSERT (FALSE);\r
+  }\r
+\r
+  return PciIo->Io.Read (\r
+                     PciIo,\r
+                     Width,\r
+                     PCI_BAR_IDX0,\r
+                     FieldOffset,\r
+                     Count,\r
+                     Buffer\r
+                     );\r
+}\r
+\r
+\r
+/**\r
+\r
+  Convenience macros to read and write region 0 IO space elements of the\r
+  virtio-blk PCI device, for configuration purposes.\r
+\r
+  The following macros make it possible to specify only the "core parameters"\r
+  for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()\r
+  returns, the transaction will have been completed.\r
+\r
+  @param[in] Dev       Pointer to the VBLK_DEV structure whose PCI IO space\r
+                       we're accessing. Dev->PciIo must be valid.\r
+\r
+  @param[in] Field     A field name from VBLK_HDR, identifying the virtio-blk\r
+                       configuration item to access.\r
+\r
+  @param[in] Value     (VIRTIO_CFG_WRITE() only.) The value to write to the\r
+                       selected configuration item.\r
+\r
+  @param[out] Pointer  (VIRTIO_CFG_READ() only.) The object to receive the\r
+                       value read from the configuration item. Its type must be\r
+                       one of UINT8, UINT16, UINT32, UINT64.\r
+\r
+\r
+  @return  Status code returned by VirtioWrite() / VirtioRead().\r
+\r
+**/\r
+\r
+#define VIRTIO_CFG_WRITE(Dev, Field, Value)  (VirtioWrite (             \\r
+                                                (Dev)->PciIo,           \\r
+                                                OFFSET_OF_VHDR (Field), \\r
+                                                SIZE_OF_VHDR (Field),   \\r
+                                                (Value)                 \\r
+                                                ))\r
+\r
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead (              \\r
+                                                (Dev)->PciIo,           \\r
+                                                OFFSET_OF_VHDR (Field), \\r
+                                                SIZE_OF_VHDR (Field),   \\r
+                                                sizeof *(Pointer),      \\r
+                                                (Pointer)               \\r
+                                                ))\r
+\r
+\r
+//\r
+// UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol\r
+// Driver Writer's Guide for UEFI 2.3.1 v1.01,\r
+//   24.2 Block I/O Protocol Implementations\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReset (\r
+  IN EFI_BLOCK_IO_PROTOCOL *This,\r
+  IN BOOLEAN               ExtendedVerification\r
+  )\r
+{\r
+  //\r
+  // If we managed to initialize and install the driver, then the device is\r
+  // working correctly.\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+\r
+  Verify correctness of the read/write (not flush) request submitted to the\r
+  EFI_BLOCK_IO_PROTOCOL instance.\r
+\r
+  This function provides most verification steps described in:\r
+\r
+    UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol,\r
+    - EFI_BLOCK_IO_PROTOCOL.ReadBlocks()\r
+    - EFI_BLOCK_IO_PROTOCOL.WriteBlocks()\r
+\r
+    Driver Writer's Guide for UEFI 2.3.1 v1.01,\r
+    - 24.2.2. ReadBlocks() and ReadBlocksEx() Implementation\r
+    - 24.2.3 WriteBlocks() and WriteBlockEx() Implementation\r
+\r
+  Request sizes are limited to 1 GB (checked). This is not a practical\r
+  limitation, just conformance to virtio-0.9.5, 2.3.2 Descriptor Table: "no\r
+  descriptor chain may be more than 2^32 bytes long in total".\r
+\r
+  Some Media characteristics are hardcoded in VirtioBlkInit() below (like\r
+  non-removable media, no restriction on buffer alignment etc); we rely on\r
+  those here without explicit mention.\r
+\r
+  @param[in] Media               The EFI_BLOCK_IO_MEDIA characteristics for\r
+                                 this driver instance, extracted from the\r
+                                 underlying virtio-blk device at initialization\r
+                                 time. We validate the request against this set\r
+                                 of attributes.\r
+\r
+\r
+  @param[in] Lba                 Logical Block Address: number of logical\r
+                                 blocks to skip from the beginning of the\r
+                                 device.\r
+\r
+  @param[in] PositiveBufferSize  Size of buffer to transfer, in bytes. The\r
+                                 caller is responsible to ensure this parameter\r
+                                 is positive.\r
+\r
+  @param[in] RequestIsWrite      TRUE iff data transfer goes from guest to\r
+                                 device.\r
+\r
+\r
+  @@return                       Validation result to be forwarded outwards by\r
+                                 ReadBlocks() and WriteBlocks, as required by\r
+                                 the specs above.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VerifyReadWriteRequest (\r
+  IN  EFI_BLOCK_IO_MEDIA *Media,\r
+  IN  EFI_LBA            Lba,\r
+  IN  UINTN              PositiveBufferSize,\r
+  IN  BOOLEAN            RequestIsWrite\r
+  )\r
+{\r
+  UINTN BlockCount;\r
+\r
+  ASSERT (PositiveBufferSize > 0);\r
+\r
+  if (PositiveBufferSize > SIZE_1GB ||\r
+      PositiveBufferSize % Media->BlockSize > 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+  BlockCount = PositiveBufferSize / Media->BlockSize;\r
+\r
+  //\r
+  // Avoid unsigned wraparound on either side in the second comparison.\r
+  //\r
+  if (Lba > Media->LastBlock || BlockCount - 1 > Media->LastBlock - Lba) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (RequestIsWrite && Media->ReadOnly) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Append a contiguous buffer for transmission / reception via the virtio ring.\r
+\r
+  This function implements the following sections from virtio-0.9.5:\r
+  - 2.4.1.1 Placing Buffers into the Descriptor Table\r
+  - 2.4.1.2 Updating the Available Ring\r
+\r
+  Free space is taken as granted, since this driver supports only synchronous\r
+  requests and host side status is processed in lock-step with request\r
+  submission. VirtioBlkInit() verifies the ring size in advance.\r
+\r
+  @param[in out] Ring           The virtio ring to append the buffer to, as a\r
+                                descriptor.\r
+\r
+  @param [in] BufferPhysAddr    (Guest pseudo-physical) start address of the\r
+                                transmit / receive buffer\r
+\r
+  @param [in] BufferSize        Number of bytes to transmit or receive.\r
+\r
+  @param [in] Flags             A bitmask of VRING_DESC_F_* flags. The caller\r
+                                computes this mask dependent on further buffers\r
+                                to append and transfer direction.\r
+                                VRING_DESC_F_INDIRECT is unsupported. The\r
+                                VRING_DESC.Next field is always set, but the\r
+                                host only interprets it dependent on\r
+                                VRING_DESC_F_NEXT.\r
+\r
+  @param [in] HeadIdx           The index identifying the head buffer (first\r
+                                buffer appended) belonging to this same\r
+                                request.\r
+\r
+  @param [in out] NextAvailIdx  On input, the index identifying the next\r
+                                descriptor available to carry the buffer. On\r
+                                output, incremented by one, modulo 2^16.\r
+\r
+**/\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AppendDesc (\r
+  IN OUT          VRING  *Ring,\r
+  IN              UINTN  BufferPhysAddr,\r
+  IN              UINT32 BufferSize,\r
+  IN              UINT16 Flags,\r
+  IN              UINT16 HeadIdx,\r
+  IN OUT          UINT16 *NextAvailIdx\r
+  )\r
+{\r
+  volatile VRING_DESC *Desc;\r
+\r
+  Desc        = &Ring->Desc[*NextAvailIdx % Ring->QueueSize];\r
+  Desc->Addr  = BufferPhysAddr;\r
+  Desc->Len   = BufferSize;\r
+  Desc->Flags = Flags;\r
+  Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] =\r
+    HeadIdx % Ring->QueueSize;\r
+  Desc->Next  = *NextAvailIdx % Ring->QueueSize;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Format a read / write / flush request as three consecutive virtio\r
+  descriptors, push them to the host, and poll for the response.\r
+\r
+  This is the main workhorse function. Two use cases are supported, read/write\r
+  and flush. The function may only be called after the request parameters have\r
+  been verified by\r
+  - specific checks in ReadBlocks() / WriteBlocks() / FlushBlocks(), and\r
+  - VerifyReadWriteRequest() (for read/write only).\r
+\r
+  Parameters handled commonly:\r
+\r
+    @param[in] Dev             The virtio-blk device the request is targeted\r
+                               at.\r
+\r
+  Flush request:\r
+\r
+    @param[in] Lba             Must be zero.\r
+\r
+    @param[in] BufferSize      Must be zero.\r
+\r
+    @param[in out] Buffer      Ignored by the function.\r
+\r
+    @param[in] RequestIsWrite  Must be TRUE.\r
+\r
+  Read/Write request:\r
+\r
+    @param[in] Lba             Logical Block Address: number of logical blocks\r
+                               to skip from the beginning of the device.\r
+\r
+    @param[in] BufferSize      Size of buffer to transfer, in bytes. The caller\r
+                               is responsible to ensure this parameter is\r
+                               positive.\r
+\r
+    @param[in out] Buffer      The guest side area to read data from the device\r
+                               into, or write data to the device from.\r
+\r
+    @param[in] RequestIsWrite  TRUE iff data transfer goes from guest to\r
+                               device.\r
+\r
+  Return values are common to both use cases, and are appropriate to be\r
+  forwarded by the EFI_BLOCK_IO_PROTOCOL functions (ReadBlocks(),\r
+  WriteBlocks(), FlushBlocks()).\r
+\r
+\r
+  @retval EFI_SUCCESS          Transfer complete.\r
+\r
+  @retval EFI_DEVICE_ERROR     Failed to notify host side via PCI write, or\r
+                               unable to parse host response, or host response\r
+                               is not VIRTIO_BLK_S_OK.\r
+\r
+**/\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+SynchronousRequest (\r
+  IN              VBLK_DEV *Dev,\r
+  IN              EFI_LBA  Lba,\r
+  IN              UINTN    BufferSize,\r
+  IN OUT volatile VOID     *Buffer,\r
+  IN              BOOLEAN  RequestIsWrite\r
+  )\r
+{\r
+  UINT32                  BlockSize;\r
+  volatile VIRTIO_BLK_REQ Request;\r
+  volatile UINT8          HostStatus;\r
+  UINT16                  FirstAvailIdx;\r
+  UINT16                  NextAvailIdx;\r
+  UINTN                   PollPeriodUsecs;\r
+\r
+  BlockSize = Dev->BlockIoMedia.BlockSize;\r
+\r
+  //\r
+  // ensured by VirtioBlkInit()\r
+  //\r
+  ASSERT (BlockSize > 0);\r
+  ASSERT (BlockSize % 512 == 0);\r
+\r
+  //\r
+  // ensured by contract above, plus VerifyReadWriteRequest()\r
+  //\r
+  ASSERT (BufferSize % BlockSize == 0);\r
+\r
+  //\r
+  // Prepare virtio-blk request header, setting zero size for flush.\r
+  // IO Priority is homogeneously 0.\r
+  //\r
+  Request.Type   = RequestIsWrite ?\r
+                   (BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) :\r
+                   VIRTIO_BLK_T_IN;\r
+  Request.IoPrio = 0;\r
+  Request.Sector = Lba * (BlockSize / 512);\r
+\r
+  //\r
+  // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.\r
+  // We're going to poll the answer, the host should not send an interrupt.\r
+  //\r
+  *Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
+\r
+  //\r
+  // preset a host status for ourselves that we do not accept as success\r
+  //\r
+  HostStatus = VIRTIO_BLK_S_IOERR;\r
+\r
+  //\r
+  // ensured by VirtioBlkInit() -- this predicate, in combination with the\r
+  // lock-step progress, ensures we don't have to track free descriptors.\r
+  //\r
+  ASSERT (Dev->Ring.QueueSize >= 3);\r
+\r
+  //\r
+  // Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\r
+  //\r
+  FirstAvailIdx = *Dev->Ring.Avail.Idx;\r
+  NextAvailIdx  = FirstAvailIdx;\r
+\r
+  //\r
+  // virtio-blk header in first desc\r
+  //\r
+  AppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, VRING_DESC_F_NEXT,\r
+    FirstAvailIdx, &NextAvailIdx);\r
+\r
+  //\r
+  // data buffer for read/write in second desc\r
+  //\r
+  if (BufferSize > 0) {\r
+    //\r
+    // From virtio-0.9.5, 2.3.2 Descriptor Table:\r
+    // "no descriptor chain may be more than 2^32 bytes long in total".\r
+    //\r
+    // The predicate is ensured by the call contract above (for flush), or\r
+    // VerifyReadWriteRequest() (for read/write). It also implies that\r
+    // converting BufferSize to UINT32 will not truncate it.\r
+    //\r
+    ASSERT (BufferSize <= SIZE_1GB);\r
+\r
+    //\r
+    // VRING_DESC_F_WRITE is interpreted from the host's point of view.\r
+    //\r
+    AppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,\r
+      VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),\r
+      FirstAvailIdx, &NextAvailIdx);\r
+  }\r
+\r
+  //\r
+  // host status in last (second or third) desc\r
+  //\r
+  AppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,\r
+    VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx);\r
+\r
+  //\r
+  // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
+  //\r
+  MemoryFence();\r
+  *Dev->Ring.Avail.Idx = NextAvailIdx;\r
+\r
+  //\r
+  // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are\r
+  // OK. virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
+  //\r
+  MemoryFence();\r
+  if (EFI_ERROR (VIRTIO_CFG_WRITE (Dev, VhdrQueueNotify, 0))) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device\r
+  // Wait until the host processes and acknowledges our 3-part descriptor\r
+  // chain. The condition we use for polling is greatly simplified and relies\r
+  // on synchronous, the lock-step progress.\r
+  //\r
+  // Keep slowing down until we reach a poll period of slightly above 1 ms.\r
+  //\r
+  PollPeriodUsecs = 1;\r
+  MemoryFence();\r
+  while (*Dev->Ring.Used.Idx != NextAvailIdx) {\r
+    gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay\r
+\r
+    if (PollPeriodUsecs < 1024) {\r
+      PollPeriodUsecs *= 2;\r
+    }\r
+    MemoryFence();\r
+  }\r
+\r
+  if (HostStatus == VIRTIO_BLK_S_OK) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+\r
+/**\r
+\r
+  ReadBlocks() operation for virtio-blk.\r
+\r
+  See\r
+  - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol, EFI_BLOCK_IO_PROTOCOL.ReadBlocks().\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.2. ReadBlocks() and\r
+    ReadBlocksEx() Implementation.\r
+\r
+  Parameter checks and conformant return values are implemented in\r
+  VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+  A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+  successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReadBlocks (\r
+  IN  EFI_BLOCK_IO_PROTOCOL *This,\r
+  IN  UINT32                MediaId,\r
+  IN  EFI_LBA               Lba,\r
+  IN  UINTN                 BufferSize,\r
+  OUT VOID                  *Buffer\r
+  )\r
+{\r
+  VBLK_DEV   *Dev;\r
+  EFI_STATUS Status;\r
+\r
+  if (BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+  Status = VerifyReadWriteRequest (\r
+             &Dev->BlockIoMedia,\r
+             Lba,\r
+             BufferSize,\r
+             FALSE               // RequestIsWrite\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return SynchronousRequest (\r
+           Dev,\r
+           Lba,\r
+           BufferSize,\r
+           Buffer,\r
+           FALSE       // RequestIsWrite\r
+           );\r
+}\r
+\r
+/**\r
+\r
+  WriteBlocks() operation for virtio-blk.\r
+\r
+  See\r
+  - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol, EFI_BLOCK_IO_PROTOCOL.WriteBlocks().\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.3 WriteBlocks() and\r
+    WriteBlockEx() Implementation.\r
+\r
+  Parameter checks and conformant return values are implemented in\r
+  VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+  A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+  successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkWriteBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL *This,\r
+  IN UINT32                MediaId,\r
+  IN EFI_LBA               Lba,\r
+  IN UINTN                 BufferSize,\r
+  IN VOID                  *Buffer\r
+  )\r
+{\r
+  VBLK_DEV   *Dev;\r
+  EFI_STATUS Status;\r
+\r
+  if (BufferSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+  Status = VerifyReadWriteRequest (\r
+             &Dev->BlockIoMedia,\r
+             Lba,\r
+             BufferSize,\r
+             TRUE                // RequestIsWrite\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  return SynchronousRequest (\r
+           Dev,\r
+           Lba,\r
+           BufferSize,\r
+           Buffer,\r
+           TRUE        // RequestIsWrite\r
+           );\r
+}\r
+\r
+\r
+/**\r
+\r
+  FlushBlocks() operation for virtio-blk.\r
+\r
+  See\r
+  - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol, EFI_BLOCK_IO_PROTOCOL.FlushBlocks().\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.4 FlushBlocks() and\r
+    FlushBlocksEx() Implementation.\r
+\r
+  If the underlying virtio-blk device doesn't support flushing (ie.\r
+  write-caching), then this function should not be called by higher layers,\r
+  according to EFI_BLOCK_IO_MEDIA characteristics set in VirtioBlkInit().\r
+  Should they do nonetheless, we do nothing, successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkFlushBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL *This\r
+  )\r
+{\r
+  VBLK_DEV *Dev;\r
+\r
+  Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+  return Dev->BlockIoMedia.WriteCaching ?\r
+           SynchronousRequest (\r
+             Dev,\r
+             0,    // Lba\r
+             0,    // BufferSize\r
+             NULL, // Buffer\r
+             TRUE  // RequestIsWrite\r
+             ) :\r
+           EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Device probe function for this driver.\r
+\r
+  The DXE core calls this function for any given device in order to see if the\r
+  driver can drive the device.\r
+\r
+  Specs relevant in the general sense:\r
+\r
+  - UEFI Spec 2.3.1 + Errata C:\r
+    - 6.3 Protocol Handler Services -- for accessing the underlying device\r
+    - 10.1 EFI Driver Binding Protocol -- for exporting ourselves\r
+\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01:\r
+    - 5.1.3.4 OpenProtocol() and CloseProtocol() -- for accessing the\r
+      underlying device\r
+    - 9 Driver Binding Protocol -- for exporting ourselves\r
+\r
+  Specs relevant in the specific sense:\r
+  - UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design\r
+    Guidelines, 18.3 PCI drivers.\r
+\r
+  @param[in]  This                The EFI_DRIVER_BINDING_PROTOCOL object\r
+                                  incorporating this driver (independently of\r
+                                  any device).\r
+\r
+  @param[in] DeviceHandle         The device to probe.\r
+\r
+  @param[in] RemainingDevicePath  Relevant only for bus drivers, ignored.\r
+\r
+\r
+  @retval EFI_SUCCESS      The driver supports the device being probed.\r
+\r
+  @retval EFI_UNSUPPORTED  Based on virtio-blk PCI discovery, we do not support\r
+                           the device.\r
+\r
+  @return                  Error codes from the OpenProtocol() boot service or\r
+                           the PciIo protocol.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                  DeviceHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_PCI_IO_PROTOCOL *PciIo;\r
+  PCI_TYPE00          Pci;\r
+\r
+  //\r
+  // Attempt to open the device with the PciIo set of interfaces. On success,\r
+  // the protocol is "instantiated" for the PCI device. Covers duplicate open\r
+  // attempts (EFI_ALREADY_STARTED).\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  DeviceHandle,               // candidate device\r
+                  &gEfiPciIoProtocolGuid,     // for generic PCI access\r
+                  (VOID **)&PciIo,            // handle to instantiate\r
+                  This->DriverBindingHandle,  // requestor driver identity\r
+                  DeviceHandle,               // ControllerHandle, according to\r
+                                              // the UEFI Driver Model\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to\r
+                                              // the device; to be released\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Read entire PCI configuration header for more extensive check ahead.\r
+  //\r
+  Status = PciIo->Pci.Read (\r
+                        PciIo,                        // (protocol, device)\r
+                                                      // handle\r
+                        EfiPciIoWidthUint32,          // access width & copy\r
+                                                      // mode\r
+                        0,                            // Offset\r
+                        sizeof Pci / sizeof (UINT32), // Count\r
+                        &Pci                          // target buffer\r
+                        );\r
+\r
+  if (Status == EFI_SUCCESS) {\r
+    //\r
+    // virtio-0.9.5, 2.1 PCI Discovery\r
+    //\r
+    Status = (Pci.Hdr.VendorId == 0x1AF4 &&\r
+              Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F &&\r
+              Pci.Hdr.RevisionID == 0x00 &&\r
+              Pci.Device.SubsystemID == 0x02) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // We needed PCI IO access only transitorily, to see whether we support the\r
+  // device or not.\r
+  //\r
+  gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+         This->DriverBindingHandle, DeviceHandle);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Configure a virtio ring.\r
+\r
+  This function sets up internal storage (the guest-host communication area)\r
+  and lays out several "navigation" (ie. no-ownership) pointers to parts of\r
+  that storage.\r
+\r
+  Relevant sections from the virtio-0.9.5 spec:\r
+  - 1.1 Virtqueues,\r
+  - 2.3 Virtqueue Configuration.\r
+\r
+  @param[in]                    The number of descriptors to allocate for the\r
+                                virtio ring, as requested by the host.\r
+\r
+  @param[out] Ring              The virtio ring to set up.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  AllocatePages() failed to allocate contiguous\r
+                                pages for the requested QueueSize. Fields of\r
+                                Ring have indeterminate value.\r
+\r
+  @retval EFI_SUCCESS           Allocation and setup successful. Ring->Base\r
+                                (and nothing else) is responsible for\r
+                                deallocation.\r
+\r
+**/\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioRingInit (\r
+  IN  UINT16 QueueSize,\r
+  OUT VRING  *Ring\r
+  )\r
+{\r
+  UINTN          RingSize;\r
+  volatile UINT8 *RingPagesPtr;\r
+\r
+  RingSize = ALIGN_VALUE (\r
+               sizeof *Ring->Desc            * QueueSize +\r
+               sizeof *Ring->Avail.Flags                 +\r
+               sizeof *Ring->Avail.Idx                   +\r
+               sizeof *Ring->Avail.Ring      * QueueSize +\r
+               sizeof *Ring->Avail.UsedEvent,\r
+               EFI_PAGE_SIZE);\r
+\r
+  RingSize += ALIGN_VALUE (\r
+                sizeof *Ring->Used.Flags                  +\r
+                sizeof *Ring->Used.Idx                    +\r
+                sizeof *Ring->Used.UsedElem   * QueueSize +\r
+                sizeof *Ring->Used.AvailEvent,\r
+                EFI_PAGE_SIZE);\r
+\r
+  Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);\r
+  Ring->Base = AllocatePages (Ring->NumPages);\r
+  if (Ring->Base == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  SetMem (Ring->Base, RingSize, 0x00);\r
+  RingPagesPtr = Ring->Base;\r
+\r
+  Ring->Desc = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Desc * QueueSize;\r
+\r
+  Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Avail.Flags;\r
+\r
+  Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Avail.Idx;\r
+\r
+  Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;\r
+\r
+  Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Avail.UsedEvent;\r
+\r
+  RingPagesPtr = (volatile UINT8 *) Ring->Base +\r
+                 ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,\r
+                   EFI_PAGE_SIZE);\r
+\r
+  Ring->Used.Flags = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Used.Flags;\r
+\r
+  Ring->Used.Idx = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Used.Idx;\r
+\r
+  Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;\r
+\r
+  Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;\r
+  RingPagesPtr += sizeof *Ring->Used.AvailEvent;\r
+\r
+  Ring->QueueSize = QueueSize;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Tear down the internal resources of a configured virtio ring.\r
+\r
+  The caller is responsible to stop the host from using this ring before\r
+  invoking this function: the VSTAT_DRIVER_OK bit must be clear in\r
+  VhdrDeviceStatus.\r
+\r
+  @param[out] Ring  The virtio ring to clean up.\r
+\r
+**/\r
+\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioRingUninit (\r
+  IN OUT VRING *Ring\r
+  )\r
+{\r
+  FreePages (Ring->Base, Ring->NumPages);\r
+  SetMem (Ring, sizeof *Ring, 0x00);\r
+}\r
+\r
+\r
+/**\r
+\r
+  Set up all BlockIo and virtio-blk aspects of this driver for the specified\r
+  device.\r
+\r
+  @param[in out] Dev  The driver instance to configure. The caller is\r
+                      responsible for Dev->PciIo's validity (ie. working IO\r
+                      access to the underlying virtio-blk PCI device).\r
+\r
+  @retval EFI_SUCCESS      Setup complete.\r
+\r
+  @retval EFI_UNSUPPORTED  The driver is unable to work with the virtio ring or\r
+                           virtio-blk attributes the host provides.\r
+\r
+  @return                  Error codes from VirtioRingInit() or\r
+                           VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE().\r
+\r
+**/\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkInit (\r
+  IN OUT VBLK_DEV *Dev\r
+  )\r
+{\r
+  UINT8      NextDevStat;\r
+  EFI_STATUS Status;\r
+\r
+  UINT32     Features;\r
+  UINT64     NumSectors;\r
+  UINT32     BlockSize;\r
+  UINT16     QueueSize;\r
+\r
+  //\r
+  // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.\r
+  //\r
+  NextDevStat = 0;             // step 1 -- reset device\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+\r
+  NextDevStat |= VSTAT_ACK;    // step 2 -- acknowledge device presence\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+\r
+  NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+\r
+  //\r
+  // step 4a -- retrieve and validate features\r
+  //\r
+  Status = VIRTIO_CFG_READ (Dev, VhdrDeviceFeatureBits, &Features);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+  Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+  if (NumSectors == 0) {\r
+    Status = EFI_UNSUPPORTED;\r
+    goto Failed;\r
+  }\r
+\r
+  if (Features & VIRTIO_BLK_F_BLK_SIZE) {\r
+    Status = VIRTIO_CFG_READ (Dev, VhdrBlkSize, &BlockSize);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Failed;\r
+    }\r
+    if (BlockSize == 0 || BlockSize % 512 != 0 ||\r
+        NumSectors % (BlockSize / 512) != 0) {\r
+      //\r
+      // We can only handle a logical block consisting of whole sectors,\r
+      // and only a disk composed of whole logical blocks.\r
+      //\r
+      Status = EFI_UNSUPPORTED;\r
+      goto Failed;\r
+    }\r
+  }\r
+  else {\r
+    BlockSize = 512;\r
+  }\r
+\r
+  //\r
+  // step 4b -- allocate virtqueue\r
+  //\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrQueueSelect, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+  Status = VIRTIO_CFG_READ (Dev, VhdrQueueSize, &QueueSize);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+  if (QueueSize < 3) { // SynchronousRequest() uses at most three descriptors\r
+    Status = EFI_UNSUPPORTED;\r
+    goto Failed;\r
+  }\r
+\r
+  Status = VirtioRingInit (QueueSize, &Dev->Ring);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+\r
+  //\r
+  // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything\r
+  // fails from here on, we must release the ring resources.\r
+  //\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrQueueAddress,\r
+             (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ReleaseQueue;\r
+  }\r
+\r
+  //\r
+  // step 5 -- Report understood features. There are no virtio-blk specific\r
+  // features to negotiate in virtio-0.9.5, plus we do not want any of the\r
+  // device-independent (known or unknown) VIRTIO_F_* capabilities (see\r
+  // Appendix B).\r
+  //\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrGuestFeatureBits, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ReleaseQueue;\r
+  }\r
+\r
+  //\r
+  // step 6 -- initialization complete\r
+  //\r
+  NextDevStat |= VSTAT_DRIVER_OK;\r
+  Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ReleaseQueue;\r
+  }\r
+\r
+  //\r
+  // Populate the exported interface's attributes; see UEFI spec v2.3.1 +\r
+  // Errata C, 12.8 EFI Block I/O Protocol. We stick to the lowest possible\r
+  // EFI_BLOCK_IO_PROTOCOL revision for now.\r
+  //\r
+  Dev->BlockIo.Revision              = 0;\r
+  Dev->BlockIo.Media                 = &Dev->BlockIoMedia;\r
+  Dev->BlockIo.Reset                 = &VirtioBlkReset;\r
+  Dev->BlockIo.ReadBlocks            = &VirtioBlkReadBlocks;\r
+  Dev->BlockIo.WriteBlocks           = &VirtioBlkWriteBlocks;\r
+  Dev->BlockIo.FlushBlocks           = &VirtioBlkFlushBlocks;\r
+  Dev->BlockIoMedia.MediaId          = 0;\r
+  Dev->BlockIoMedia.RemovableMedia   = FALSE;\r
+  Dev->BlockIoMedia.MediaPresent     = TRUE;\r
+  Dev->BlockIoMedia.LogicalPartition = FALSE;\r
+  Dev->BlockIoMedia.ReadOnly         = !!(Features & VIRTIO_BLK_F_RO);\r
+  Dev->BlockIoMedia.WriteCaching     = !!(Features & VIRTIO_BLK_F_FLUSH);\r
+  Dev->BlockIoMedia.BlockSize        = BlockSize;\r
+  Dev->BlockIoMedia.IoAlign          = 0;\r
+  Dev->BlockIoMedia.LastBlock        = NumSectors / (BlockSize / 512) - 1;\r
+  return EFI_SUCCESS;\r
+\r
+ReleaseQueue:\r
+  VirtioRingUninit (&Dev->Ring);\r
+\r
+Failed:\r
+  //\r
+  // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device\r
+  // Status. PCI IO access failure here should not mask the original error.\r
+  //\r
+  NextDevStat |= VSTAT_FAILED;\r
+  VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+\r
+  return Status; // reached only via Failed above\r
+}\r
+\r
+\r
+/**\r
+\r
+  Uninitialize the internals of a virtio-blk device that has been successfully\r
+  set up with VirtioBlkInit().\r
+\r
+  @param[in out]  Dev  The device to clean up.\r
+\r
+**/\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioBlkUninit (\r
+  IN OUT VBLK_DEV *Dev\r
+  )\r
+{\r
+  //\r
+  // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When\r
+  // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from\r
+  // the old comms area.\r
+  //\r
+  VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, 0);\r
+\r
+  VirtioRingUninit (&Dev->Ring);\r
+\r
+  SetMem (&Dev->BlockIo,      sizeof Dev->BlockIo,      0x00);\r
+  SetMem (&Dev->BlockIoMedia, sizeof Dev->BlockIoMedia, 0x00);\r
+}\r
+\r
+\r
+/**\r
+\r
+  After we've pronounced support for a specific device in\r
+  DriverBindingSupported(), we start managing said device (passed in by the\r
+  Driver Exeuction Environment) with the following service.\r
+\r
+  See DriverBindingSupported() for specification references.\r
+\r
+  @param[in]  This                The EFI_DRIVER_BINDING_PROTOCOL object\r
+                                  incorporating this driver (independently of\r
+                                  any device).\r
+\r
+  @param[in] DeviceHandle         The supported device to drive.\r
+\r
+  @param[in] RemainingDevicePath  Relevant only for bus drivers, ignored.\r
+\r
+\r
+  @retval EFI_SUCCESS           Driver instance has been created and\r
+                                initialized  for the virtio-blk PCI device, it\r
+                                is now accessibla via EFI_BLOCK_IO_PROTOCOL.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.\r
+\r
+  @return                       Error codes from the OpenProtocol() boot\r
+                                service, the PciIo protocol, VirtioBlkInit(),\r
+                                or the InstallProtocolInterface() boot service.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                  DeviceHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath\r
+  )\r
+{\r
+  VBLK_DEV   *Dev;\r
+  EFI_STATUS Status;\r
+\r
+  Dev = (VBLK_DEV *) AllocateZeroPool (sizeof *Dev);\r
+  if (Dev == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+                  (VOID **)&Dev->PciIo, This->DriverBindingHandle,\r
+                  DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeVirtioBlk;\r
+  }\r
+\r
+  //\r
+  // We must retain and ultimately restore the original PCI attributes of the\r
+  // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers /\r
+  // 18.3.2 Start() and Stop().\r
+  //\r
+  // The third parameter ("Attributes", input) is ignored by the Get operation.\r
+  // The fourth parameter ("Result", output) is ignored by the Enable and Set\r
+  // operations.\r
+  //\r
+  // For virtio-blk we only need IO space access.\r
+  //\r
+  Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet,\r
+                         0, &Dev->OriginalPciAttributes);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ClosePciIo;\r
+  }\r
+\r
+  Status = Dev->PciIo->Attributes (Dev->PciIo,\r
+                         EfiPciIoAttributeOperationEnable,\r
+                         EFI_PCI_IO_ATTRIBUTE_IO, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ClosePciIo;\r
+  }\r
+\r
+  //\r
+  // PCI IO access granted, configure virtio-blk device.\r
+  //\r
+  Status = VirtioBlkInit (Dev);\r
+  if (EFI_ERROR (Status)) {\r
+    goto RestorePciAttributes;\r
+  }\r
+\r
+  //\r
+  // Setup complete, attempt to export the driver instance's BlockIo interface.\r
+  //\r
+  Dev->Signature = VBLK_SIG;\r
+  Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
+                  &gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE,\r
+                  &Dev->BlockIo);\r
+  if (EFI_ERROR (Status)) {\r
+    goto UninitDev;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+UninitDev:\r
+  VirtioBlkUninit (Dev);\r
+\r
+RestorePciAttributes:\r
+  Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
+                Dev->OriginalPciAttributes, NULL);\r
+\r
+ClosePciIo:\r
+  gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+         This->DriverBindingHandle, DeviceHandle);\r
+\r
+FreeVirtioBlk:\r
+  FreePool (Dev);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+\r
+  Stop driving a virtio-blk device and remove its BlockIo interface.\r
+\r
+  This function replays the success path of DriverBindingStart() in reverse.\r
+  The host side virtio-blk device is reset, so that the OS boot loader or the\r
+  OS may reinitialize it.\r
+\r
+  @param[in] This               The EFI_DRIVER_BINDING_PROTOCOL object\r
+                                incorporating this driver (independently of any\r
+                                device).\r
+\r
+  @param[in] DeviceHandle       Stop driving this device.\r
+\r
+  @param[in] NumberOfChildren   Since this function belongs to a device driver\r
+                                only (as opposed to a bus driver), the caller\r
+                                environment sets NumberOfChildren to zero, and\r
+                                we ignore it.\r
+\r
+  @param[in] ChildHandleBuffer  Ignored (corresponding to NumberOfChildren).\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                  DeviceHandle,\r
+  IN UINTN                       NumberOfChildren,\r
+  IN EFI_HANDLE                  *ChildHandleBuffer\r
+  )\r
+{\r
+  VBLK_DEV   *Dev;\r
+  EFI_STATUS Status;\r
+\r
+  Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+\r
+  //\r
+  // If DriverBindingStop() is called with the driver instance still in use,\r
+  // or any of the parameters are invalid, we've caught a bug.\r
+  //\r
+  Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
+                  &gEfiBlockIoProtocolGuid, &Dev->BlockIo);\r
+  ASSERT (Status == EFI_SUCCESS);\r
+\r
+  VirtioBlkUninit (Dev);\r
+\r
+  Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
+                Dev->OriginalPciAttributes, NULL);\r
+\r
+  gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+         This->DriverBindingHandle, DeviceHandle);\r
+\r
+  FreePool (Dev);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+//\r
+// The static object that groups the Supported() (ie. probe), Start() and\r
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata\r
+// C, 10.1 EFI Driver Binding Protocol.\r
+//\r
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
+  &VirtioBlkDriverBindingSupported,\r
+  &VirtioBlkDriverBindingStart,\r
+  &VirtioBlkDriverBindingStop,\r
+  0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers\r
+  NULL, // ImageHandle, to be overwritten by\r
+        // EfiLibInstallDriverBindingComponentName2() in VirtioBlkEntryPoint()\r
+  NULL  // DriverBindingHandle, ditto\r
+};\r
+\r
+\r
+//\r
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
+// in English, for display on standard console devices. This is recommended for\r
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
+//\r
+// Device type names ("Virtio Block Device") are not formatted because the\r
+// driver supports only that device type. Therefore the driver name suffices\r
+// for unambiguous identification.\r
+//\r
+\r
+STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
+  { "eng;en", L"Virtio Block Driver" },\r
+  { NULL,     NULL                   }\r
+};\r
+\r
+STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,\r
+  IN  CHAR8                       *Language,\r
+  OUT CHAR16                      **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mDriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN)(This == &gComponentName) // Iso639Language\r
+           );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDeviceName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,\r
+  IN  EFI_HANDLE                  DeviceHandle,\r
+  IN  EFI_HANDLE                  ChildHandle,\r
+  IN  CHAR8                       *Language,\r
+  OUT CHAR16                      **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
+  &VirtioBlkGetDriverName,\r
+  &VirtioBlkGetDeviceName,\r
+  "eng" // SupportedLanguages, ISO 639-2 language codes\r
+};\r
+\r
+STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &VirtioBlkGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,\r
+  "en" // SupportedLanguages, RFC 4646 language codes\r
+};\r
+\r
+\r
+//\r
+// Entry point of this driver.\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkEntryPoint (\r
+  IN EFI_HANDLE       ImageHandle,\r
+  IN EFI_SYSTEM_TABLE *SystemTable\r
+  )\r
+{\r
+  return EfiLibInstallDriverBindingComponentName2 (\r
+           ImageHandle,\r
+           SystemTable,\r
+           &gDriverBinding,\r
+           ImageHandle,\r
+           &gComponentName,\r
+           &gComponentName2\r
+           );\r
+}\r
+\r
diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.h b/OvmfPkg/VirtioBlkDxe/VirtioBlk.h
new file mode 100644 (file)
index 0000000..e846dab
--- /dev/null
@@ -0,0 +1,293 @@
+/** @file\r
+\r
+  Internal definitions for the virtio-blk driver, which produces Block I/O\r
+  Protocol instances for virtio-blk devices.\r
+\r
+  Copyright (C) 2012, Red Hat, Inc.\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution. The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Protocol/BlockIo.h>\r
+#include <Protocol/ComponentName.h>\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/PciIo.h>\r
+\r
+#include "Virtio.h"\r
+\r
+\r
+#define VBLK_SIG SIGNATURE_32 ('V', 'B', 'L', 'K')\r
+\r
+typedef struct {\r
+  //\r
+  // Parts of this structure are initialized / torn down in various functions\r
+  // at various call depths. The table to the right should make it easier to\r
+  // track them.\r
+  //\r
+  //                    field                     init function       init dpth\r
+  //                    ----------------------    ------------------  ---------\r
+  UINT32                Signature;             // DriverBindingStart  0\r
+  EFI_PCI_IO_PROTOCOL   *PciIo;                // DriverBindingStart  0\r
+  UINT64                OriginalPciAttributes; // DriverBindingStart  0\r
+  VRING                 Ring;                  // VirtioRingInit      2\r
+  EFI_BLOCK_IO_PROTOCOL BlockIo;               // VirtioBlkInit       1\r
+  EFI_BLOCK_IO_MEDIA    BlockIoMedia;          // VirtioBlkInit       1\r
+} VBLK_DEV;\r
+\r
+#define VIRTIO_BLK_FROM_BLOCK_IO(BlockIoPointer) \\r
+        CR (BlockIoPointer, VBLK_DEV, BlockIo, VBLK_SIG)\r
+\r
+\r
+/**\r
+\r
+  Device probe function for this driver.\r
+\r
+  The DXE core calls this function for any given device in order to see if the\r
+  driver can drive the device.\r
+\r
+  Specs relevant in the general sense:\r
+\r
+  - UEFI Spec 2.3.1 + Errata C:\r
+    - 6.3 Protocol Handler Services -- for accessing the underlying device\r
+    - 10.1 EFI Driver Binding Protocol -- for exporting ourselves\r
+\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01:\r
+    - 5.1.3.4 OpenProtocol() and CloseProtocol() -- for accessing the\r
+      underlying device\r
+    - 9 Driver Binding Protocol -- for exporting ourselves\r
+\r
+  Specs relevant in the specific sense:\r
+  - UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design\r
+    Guidelines, 18.3 PCI drivers.\r
+\r
+  @param[in]  This                The EFI_DRIVER_BINDING_PROTOCOL object\r
+                                  incorporating this driver (independently of\r
+                                  any device).\r
+\r
+  @param[in] DeviceHandle         The device to probe.\r
+\r
+  @param[in] RemainingDevicePath  Relevant only for bus drivers, ignored.\r
+\r
+\r
+  @retval EFI_SUCCESS      The driver supports the device being probed.\r
+\r
+  @retval EFI_UNSUPPORTED  Based on virtio-blk PCI discovery, we do not support\r
+                           the device.\r
+\r
+  @return                  Error codes from the OpenProtocol() boot service or\r
+                           the PciIo protocol.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                  DeviceHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath\r
+  );\r
+\r
+\r
+/**\r
+\r
+  After we've pronounced support for a specific device in\r
+  DriverBindingSupported(), we start managing said device (passed in by the\r
+  Driver Exeuction Environment) with the following service.\r
+\r
+  See DriverBindingSupported() for specification references.\r
+\r
+  @param[in]  This                The EFI_DRIVER_BINDING_PROTOCOL object\r
+                                  incorporating this driver (independently of\r
+                                  any device).\r
+\r
+  @param[in] DeviceHandle         The supported device to drive.\r
+\r
+  @param[in] RemainingDevicePath  Relevant only for bus drivers, ignored.\r
+\r
+\r
+  @retval EFI_SUCCESS           Driver instance has been created and\r
+                                initialized  for the virtio-blk PCI device, it\r
+                                is now accessibla via EFI_BLOCK_IO_PROTOCOL.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.\r
+\r
+  @return                       Error codes from the OpenProtocol() boot\r
+                                service, the PciIo protocol, VirtioBlkInit(),\r
+                                or the InstallProtocolInterface() boot service.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                  DeviceHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath\r
+  );\r
+\r
+\r
+/**\r
+\r
+  Stop driving a virtio-blk device and remove its BlockIo interface.\r
+\r
+  This function replays the success path of DriverBindingStart() in reverse.\r
+  The host side virtio-blk device is reset, so that the OS boot loader or the\r
+  OS may reinitialize it.\r
+\r
+  @param[in] This               The EFI_DRIVER_BINDING_PROTOCOL object\r
+                                incorporating this driver (independently of any\r
+                                device).\r
+\r
+  @param[in] DeviceHandle       Stop driving this device.\r
+\r
+  @param[in] NumberOfChildren   Since this function belongs to a device driver\r
+                                only (as opposed to a bus driver), the caller\r
+                                environment sets NumberOfChildren to zero, and\r
+                                we ignore it.\r
+\r
+  @param[in] ChildHandleBuffer  Ignored (corresponding to NumberOfChildren).\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+  IN EFI_HANDLE                  DeviceHandle,\r
+  IN UINTN                       NumberOfChildren,\r
+  IN EFI_HANDLE                  *ChildHandleBuffer\r
+  );\r
+\r
+\r
+//\r
+// UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol\r
+// Driver Writer's Guide for UEFI 2.3.1 v1.01,\r
+//   24.2 Block I/O Protocol Implementations\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReset (\r
+  IN EFI_BLOCK_IO_PROTOCOL *This,\r
+  IN BOOLEAN               ExtendedVerification\r
+  );\r
+\r
+\r
+/**\r
+\r
+  ReadBlocks() operation for virtio-blk.\r
+\r
+  See\r
+  - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol, EFI_BLOCK_IO_PROTOCOL.ReadBlocks().\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.2. ReadBlocks() and\r
+    ReadBlocksEx() Implementation.\r
+\r
+  Parameter checks and conformant return values are implemented in\r
+  VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+  A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+  successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReadBlocks (\r
+  IN  EFI_BLOCK_IO_PROTOCOL *This,\r
+  IN  UINT32                MediaId,\r
+  IN  EFI_LBA               Lba,\r
+  IN  UINTN                 BufferSize,\r
+  OUT VOID                  *Buffer\r
+  );\r
+\r
+\r
+/**\r
+\r
+  WriteBlocks() operation for virtio-blk.\r
+\r
+  See\r
+  - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol, EFI_BLOCK_IO_PROTOCOL.WriteBlocks().\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.3 WriteBlocks() and\r
+    WriteBlockEx() Implementation.\r
+\r
+  Parameter checks and conformant return values are implemented in\r
+  VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+  A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+  successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkWriteBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL *This,\r
+  IN UINT32                MediaId,\r
+  IN EFI_LBA               Lba,\r
+  IN UINTN                 BufferSize,\r
+  IN VOID                  *Buffer\r
+  );\r
+\r
+\r
+/**\r
+\r
+  FlushBlocks() operation for virtio-blk.\r
+\r
+  See\r
+  - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+    Protocol, EFI_BLOCK_IO_PROTOCOL.FlushBlocks().\r
+  - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.4 FlushBlocks() and\r
+    FlushBlocksEx() Implementation.\r
+\r
+  If the underlying virtio-blk device doesn't support flushing (ie.\r
+  write-caching), then this function should not be called by higher layers,\r
+  according to EFI_BLOCK_IO_MEDIA characteristics set in VirtioBlkInit().\r
+  Should they do nonetheless, we do nothing, successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkFlushBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL *This\r
+  );\r
+\r
+\r
+//\r
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
+// in English, for display on standard console devices. This is recommended for\r
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
+//\r
+// Device type names ("Virtio Block Device") are not formatted because the\r
+// driver supports only that device type. Therefore the driver name suffices\r
+// for unambiguous identification.\r
+//\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,\r
+  IN  CHAR8                       *Language,\r
+  OUT CHAR16                      **DriverName\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDeviceName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,\r
+  IN  EFI_HANDLE                  DeviceHandle,\r
+  IN  EFI_HANDLE                  ChildHandle,\r
+  IN  CHAR8                       *Language,\r
+  OUT CHAR16                      **ControllerName\r
+  );\r
diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.inf b/OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
new file mode 100644 (file)
index 0000000..26ad8f8
--- /dev/null
@@ -0,0 +1,40 @@
+## @file\r
+# This driver produces Block I/O Protocol instances for virtio-blk devices.\r
+#\r
+# Copyright (C) 2012, Red Hat, Inc.\r
+#\r
+# This program and the accompanying materials are licensed and made available\r
+# under the terms and conditions of the BSD License which accompanies this\r
+# distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = VirtioBlkDxe\r
+  FILE_GUID                      = 11D92DFB-3CA9-4F93-BA2E-4780ED3E03B5\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = VirtioBlkEntryPoint\r
+\r
+[Sources]\r
+  VirtioBlk.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseMemoryLib\r
+  DebugLib\r
+  MemoryAllocationLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiLib\r
+\r
+[Protocols]\r
+  gEfiBlockIoProtocolGuid  ## BY_START\r
+  gEfiPciIoProtocolGuid    ## TO_START\r