]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/VirtioBlkDxe/VirtioBlk.c
OvmfPkg/Virtio: take RingBaseShift in SetQueueAddress()
[mirror_edk2.git] / OvmfPkg / VirtioBlkDxe / VirtioBlk.c
index 44a05cfb5f8be21a546ed48deb95605fed21ea95..bff15fe3add1692f828122e284abe38f0dc1eef7 100644 (file)
@@ -11,6 +11,8 @@
     synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.\r
 \r
   Copyright (C) 2012, Red Hat, Inc.\r
     synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.\r
 \r
   Copyright (C) 2012, Red Hat, Inc.\r
+  Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2017, AMD Inc, All rights reserved.<BR>\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
 \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
@@ -22,7 +24,6 @@
 \r
 **/\r
 \r
 \r
 **/\r
 \r
-#include <IndustryStandard/Pci.h>\r
 #include <IndustryStandard/VirtioBlk.h>\r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <IndustryStandard/VirtioBlk.h>\r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 /**\r
 \r
   Convenience macros to read and write region 0 IO space elements of the\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
+  virtio-blk 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
 \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
+  @param[in] Dev       Pointer to the VBLK_DEV structure whose VirtIo space\r
+                       we're accessing. Dev->VirtIo 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] Field     A field name from VBLK_HDR, identifying the virtio-blk\r
                        configuration item to access.\r
                        one of UINT8, UINT16, UINT32, UINT64.\r
 \r
 \r
                        one of UINT8, UINT16, UINT32, UINT64.\r
 \r
 \r
-  @return  Status code returned by VirtioWrite() / VirtioRead().\r
+  @return  Status code returned by Virtio->WriteDevice() /\r
+           Virtio->ReadDevice().\r
 \r
 **/\r
 \r
 \r
 **/\r
 \r
-#define VIRTIO_CFG_WRITE(Dev, Field, Value)  (VirtioWrite (             \\r
-                                                (Dev)->PciIo,           \\r
-                                                OFFSET_OF_VBLK (Field), \\r
-                                                SIZE_OF_VBLK (Field),   \\r
-                                                (Value)                 \\r
+#define VIRTIO_CFG_WRITE(Dev, Field, Value)  ((Dev)->VirtIo->WriteDevice ( \\r
+                                                (Dev)->VirtIo,             \\r
+                                                OFFSET_OF_VBLK (Field),    \\r
+                                                SIZE_OF_VBLK (Field),      \\r
+                                                (Value)                    \\r
                                                 ))\r
 \r
                                                 ))\r
 \r
-#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead (              \\r
-                                                (Dev)->PciIo,           \\r
-                                                OFFSET_OF_VBLK (Field), \\r
-                                                SIZE_OF_VBLK (Field),   \\r
-                                                sizeof *(Pointer),      \\r
-                                                (Pointer)               \\r
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice (  \\r
+                                                (Dev)->VirtIo,             \\r
+                                                OFFSET_OF_VBLK (Field),    \\r
+                                                SIZE_OF_VBLK (Field),      \\r
+                                                sizeof *(Pointer),         \\r
+                                                (Pointer)                  \\r
                                                 ))\r
 \r
 \r
                                                 ))\r
 \r
 \r
@@ -228,7 +230,7 @@ VerifyReadWriteRequest (
 \r
   @retval EFI_SUCCESS          Transfer complete.\r
 \r
 \r
   @retval EFI_SUCCESS          Transfer complete.\r
 \r
-  @retval EFI_DEVICE_ERROR     Failed to notify host side via PCI write, or\r
+  @retval EFI_DEVICE_ERROR     Failed to notify host side via VirtIo write, or\r
                                unable to parse host response, or host response\r
                                is not VIRTIO_BLK_S_OK.\r
 \r
                                unable to parse host response, or host response\r
                                is not VIRTIO_BLK_S_OK.\r
 \r
@@ -271,7 +273,7 @@ SynchronousRequest (
                    (BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) :\r
                    VIRTIO_BLK_T_IN;\r
   Request.IoPrio = 0;\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
+  Request.Sector = MultU64x32(Lba, BlockSize / 512);\r
 \r
   VirtioPrepare (&Dev->Ring, &Indices);\r
 \r
 \r
   VirtioPrepare (&Dev->Ring, &Indices);\r
 \r
@@ -323,7 +325,8 @@ SynchronousRequest (
   //\r
   // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
   //\r
   //\r
   // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
   //\r
-  if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS &&\r
+  if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices,\r
+        NULL) == EFI_SUCCESS &&\r
       HostStatus == VIRTIO_BLK_S_OK) {\r
     return EFI_SUCCESS;\r
   }\r
       HostStatus == VIRTIO_BLK_S_OK) {\r
     return EFI_SUCCESS;\r
   }\r
@@ -499,11 +502,6 @@ VirtioBlkFlushBlocks (
       underlying device\r
     - 9 Driver Binding Protocol -- for exporting ourselves\r
 \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
   @param[in]  This                The EFI_DRIVER_BINDING_PROTOCOL object\r
                                   incorporating this driver (independently of\r
                                   any device).\r
@@ -515,11 +513,11 @@ VirtioBlkFlushBlocks (
 \r
   @retval EFI_SUCCESS      The driver supports the device being probed.\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
+  @retval EFI_UNSUPPORTED  Based on virtio-blk discovery, we do not support\r
                            the device.\r
 \r
   @return                  Error codes from the OpenProtocol() boot service or\r
                            the device.\r
 \r
   @return                  Error codes from the OpenProtocol() boot service or\r
-                           the PciIo protocol.\r
+                           the VirtIo protocol.\r
 \r
 **/\r
 \r
 \r
 **/\r
 \r
@@ -532,56 +530,36 @@ VirtioBlkDriverBindingSupported (
   )\r
 {\r
   EFI_STATUS          Status;\r
   )\r
 {\r
   EFI_STATUS          Status;\r
-  EFI_PCI_IO_PROTOCOL *PciIo;\r
-  PCI_TYPE00          Pci;\r
+  VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
 \r
   //\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
+  // Attempt to open the device with the VirtIo set of interfaces. On success,\r
+  // the protocol is "instantiated" for the VirtIo device. Covers duplicate\r
+  // open attempts (EFI_ALREADY_STARTED).\r
   //\r
   Status = gBS->OpenProtocol (\r
                   DeviceHandle,               // candidate device\r
   //\r
   Status = gBS->OpenProtocol (\r
                   DeviceHandle,               // candidate device\r
-                  &gEfiPciIoProtocolGuid,     // for generic PCI access\r
-                  (VOID **)&PciIo,            // handle to instantiate\r
+                  &gVirtioDeviceProtocolGuid, // for generic VirtIo access\r
+                  (VOID **)&VirtIo,           // handle to instantiate\r
                   This->DriverBindingHandle,  // requestor driver identity\r
                   DeviceHandle,               // ControllerHandle, according to\r
                                               // the UEFI Driver Model\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
+                  EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to\r
                                               // the device; to be released\r
                   );\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \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
+  if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_BLOCK_DEVICE) {\r
+    Status = EFI_UNSUPPORTED;\r
   }\r
 \r
   //\r
   }\r
 \r
   //\r
-  // We needed PCI IO access only transitorily, to see whether we support the\r
+  // We needed VirtIo access only transitorily, to see whether we support the\r
   // device or not.\r
   //\r
   // device or not.\r
   //\r
-  gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+  gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
          This->DriverBindingHandle, DeviceHandle);\r
   return Status;\r
 }\r
          This->DriverBindingHandle, DeviceHandle);\r
   return Status;\r
 }\r
@@ -593,8 +571,8 @@ VirtioBlkDriverBindingSupported (
   device.\r
 \r
   @param[in out] Dev  The driver instance to configure. The caller is\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
+                      responsible for Dev->VirtIo's validity (ie. working IO\r
+                      access to the underlying virtio-blk device).\r
 \r
   @retval EFI_SUCCESS      Setup complete.\r
 \r
 \r
   @retval EFI_SUCCESS      Setup complete.\r
 \r
@@ -616,28 +594,43 @@ VirtioBlkInit (
   UINT8      NextDevStat;\r
   EFI_STATUS Status;\r
 \r
   UINT8      NextDevStat;\r
   EFI_STATUS Status;\r
 \r
-  UINT32     Features;\r
+  UINT64     Features;\r
   UINT64     NumSectors;\r
   UINT32     BlockSize;\r
   UINT64     NumSectors;\r
   UINT32     BlockSize;\r
+  UINT8      PhysicalBlockExp;\r
+  UINT8      AlignmentOffset;\r
+  UINT32     OptIoSize;\r
   UINT16     QueueSize;\r
 \r
   UINT16     QueueSize;\r
 \r
+  PhysicalBlockExp = 0;\r
+  AlignmentOffset = 0;\r
+  OptIoSize = 0;\r
+\r
   //\r
   // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.\r
   //\r
   NextDevStat = 0;             // step 1 -- reset device\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, Generic.VhdrDeviceStatus, NextDevStat);\r
+  Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
 \r
   NextDevStat |= VSTAT_ACK;    // step 2 -- acknowledge device presence\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, Generic.VhdrDeviceStatus, NextDevStat);\r
+  Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
 \r
   NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it\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, Generic.VhdrDeviceStatus, NextDevStat);\r
+  Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Failed;\r
+  }\r
+\r
+  //\r
+  // Set Page Size - MMIO VirtIo Specific\r
+  //\r
+  Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
@@ -645,11 +638,12 @@ VirtioBlkInit (
   //\r
   // step 4a -- retrieve and validate features\r
   //\r
   //\r
   // step 4a -- retrieve and validate features\r
   //\r
-  Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);\r
+  Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
-  Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors);\r
+\r
+  Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
@@ -659,12 +653,12 @@ VirtioBlkInit (
   }\r
 \r
   if (Features & VIRTIO_BLK_F_BLK_SIZE) {\r
   }\r
 \r
   if (Features & VIRTIO_BLK_F_BLK_SIZE) {\r
-    Status = VIRTIO_CFG_READ (Dev, VhdrBlkSize, &BlockSize);\r
+    Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize);\r
     if (EFI_ERROR (Status)) {\r
       goto Failed;\r
     }\r
     if (BlockSize == 0 || BlockSize % 512 != 0 ||\r
     if (EFI_ERROR (Status)) {\r
       goto Failed;\r
     }\r
     if (BlockSize == 0 || BlockSize % 512 != 0 ||\r
-        NumSectors % (BlockSize / 512) != 0) {\r
+        ModU64x32 (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
       // We can only handle a logical block consisting of whole sectors,\r
       // and only a disk composed of whole logical blocks.\r
@@ -677,14 +671,50 @@ VirtioBlkInit (
     BlockSize = 512;\r
   }\r
 \r
     BlockSize = 512;\r
   }\r
 \r
+  if (Features & VIRTIO_BLK_F_TOPOLOGY) {\r
+    Status = VIRTIO_CFG_READ (Dev, Topology.PhysicalBlockExp,\r
+               &PhysicalBlockExp);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Failed;\r
+    }\r
+    if (PhysicalBlockExp >= 32) {\r
+      Status = EFI_UNSUPPORTED;\r
+      goto Failed;\r
+    }\r
+\r
+    Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Failed;\r
+    }\r
+\r
+    Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Failed;\r
+    }\r
+  }\r
+\r
+  Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO |\r
+              VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1;\r
+\r
+  //\r
+  // In virtio-1.0, feature negotiation is expected to complete before queue\r
+  // discovery, and the device can also reject the selected set of features.\r
+  //\r
+  if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+    Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Failed;\r
+    }\r
+  }\r
+\r
   //\r
   // step 4b -- allocate virtqueue\r
   //\r
   //\r
   // step 4b -- allocate virtqueue\r
   //\r
-  Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect, 0);\r
+  Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
-  Status = VIRTIO_CFG_READ (Dev, Generic.VhdrQueueSize, &QueueSize);\r
+  Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
@@ -693,45 +723,57 @@ VirtioBlkInit (
     goto Failed;\r
   }\r
 \r
     goto Failed;\r
   }\r
 \r
-  Status = VirtioRingInit (QueueSize, &Dev->Ring);\r
+  Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring);\r
   if (EFI_ERROR (Status)) {\r
     goto Failed;\r
   }\r
 \r
   //\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
+  // Additional steps for MMIO: align the queue appropriately, and set the\r
+  // size. If anything fails from here on, we must release the ring resources.\r
   //\r
   //\r
-  Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueAddress,\r
-             (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);\r
+  Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ReleaseQueue;\r
+  }\r
+\r
+  Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
   if (EFI_ERROR (Status)) {\r
     goto ReleaseQueue;\r
   }\r
 \r
   //\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
+  // step 4c -- Report GPFN (guest-physical frame number) of queue.\r
   //\r
   //\r
-  Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits, 0);\r
+  Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, &Dev->Ring, 0);\r
   if (EFI_ERROR (Status)) {\r
     goto ReleaseQueue;\r
   }\r
 \r
   if (EFI_ERROR (Status)) {\r
     goto ReleaseQueue;\r
   }\r
 \r
+\r
+  //\r
+  // step 5 -- Report understood features.\r
+  //\r
+  if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+    Features &= ~(UINT64)VIRTIO_F_VERSION_1;\r
+    Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ReleaseQueue;\r
+    }\r
+  }\r
+\r
   //\r
   // step 6 -- initialization complete\r
   //\r
   NextDevStat |= VSTAT_DRIVER_OK;\r
   //\r
   // step 6 -- initialization complete\r
   //\r
   NextDevStat |= VSTAT_DRIVER_OK;\r
-  Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+  Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
   if (EFI_ERROR (Status)) {\r
     goto ReleaseQueue;\r
   }\r
 \r
   //\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
+  // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI\r
+  // Block I/O Protocol.\r
   //\r
   Dev->BlockIo.Revision              = 0;\r
   Dev->BlockIo.Media                 = &Dev->BlockIoMedia;\r
   //\r
   Dev->BlockIo.Revision              = 0;\r
   Dev->BlockIo.Media                 = &Dev->BlockIoMedia;\r
@@ -743,23 +785,42 @@ VirtioBlkInit (
   Dev->BlockIoMedia.RemovableMedia   = FALSE;\r
   Dev->BlockIoMedia.MediaPresent     = TRUE;\r
   Dev->BlockIoMedia.LogicalPartition = FALSE;\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.ReadOnly         = (BOOLEAN) ((Features & VIRTIO_BLK_F_RO) != 0);\r
+  Dev->BlockIoMedia.WriteCaching     = (BOOLEAN) ((Features & VIRTIO_BLK_F_FLUSH) != 0);\r
   Dev->BlockIoMedia.BlockSize        = BlockSize;\r
   Dev->BlockIoMedia.IoAlign          = 0;\r
   Dev->BlockIoMedia.BlockSize        = BlockSize;\r
   Dev->BlockIoMedia.IoAlign          = 0;\r
-  Dev->BlockIoMedia.LastBlock        = NumSectors / (BlockSize / 512) - 1;\r
+  Dev->BlockIoMedia.LastBlock        = DivU64x32 (NumSectors,\r
+                                         BlockSize / 512) - 1;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a: LbaSize=0x%x[B] NumBlocks=0x%Lx[Lba]\n",\r
+    __FUNCTION__, Dev->BlockIoMedia.BlockSize,\r
+    Dev->BlockIoMedia.LastBlock + 1));\r
+\r
+  if (Features & VIRTIO_BLK_F_TOPOLOGY) {\r
+    Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3;\r
+\r
+    Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset;\r
+    Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp;\r
+    Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize;\r
+\r
+    DEBUG ((DEBUG_INFO, "%a: FirstAligned=0x%Lx[Lba] PhysBlkSize=0x%x[Lba]\n",\r
+      __FUNCTION__, Dev->BlockIoMedia.LowestAlignedLba,\r
+      Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock));\r
+    DEBUG ((DEBUG_INFO, "%a: OptimalTransferLengthGranularity=0x%x[Lba]\n",\r
+      __FUNCTION__, Dev->BlockIoMedia.OptimalTransferLengthGranularity));\r
+  }\r
   return EFI_SUCCESS;\r
 \r
 ReleaseQueue:\r
   return EFI_SUCCESS;\r
 \r
 ReleaseQueue:\r
-  VirtioRingUninit (&Dev->Ring);\r
+  VirtioRingUninit (Dev->VirtIo, &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
 \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
+  // Status. VirtIo access failure here should not mask the original error.\r
   //\r
   NextDevStat |= VSTAT_FAILED;\r
   //\r
   NextDevStat |= VSTAT_FAILED;\r
-  VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+  Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
 \r
   return Status; // reached only via Failed above\r
 }\r
 \r
   return Status; // reached only via Failed above\r
 }\r
@@ -786,20 +847,51 @@ VirtioBlkUninit (
   // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from\r
   // the old comms area.\r
   //\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, Generic.VhdrDeviceStatus, 0);\r
+  Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
 \r
 \r
-  VirtioRingUninit (&Dev->Ring);\r
+  VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
 \r
   SetMem (&Dev->BlockIo,      sizeof Dev->BlockIo,      0x00);\r
   SetMem (&Dev->BlockIoMedia, sizeof Dev->BlockIoMedia, 0x00);\r
 }\r
 \r
 \r
 \r
   SetMem (&Dev->BlockIo,      sizeof Dev->BlockIo,      0x00);\r
   SetMem (&Dev->BlockIoMedia, sizeof Dev->BlockIoMedia, 0x00);\r
 }\r
 \r
 \r
+/**\r
+\r
+  Event notification function enqueued by ExitBootServices().\r
+\r
+  @param[in] Event    Event whose notification function is being invoked.\r
+\r
+  @param[in] Context  Pointer to the VBLK_DEV structure.\r
+\r
+**/\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioBlkExitBoot (\r
+  IN  EFI_EVENT Event,\r
+  IN  VOID      *Context\r
+  )\r
+{\r
+  VBLK_DEV *Dev;\r
+\r
+  //\r
+  // Reset the device. This causes the hypervisor to forget about the virtio\r
+  // ring.\r
+  //\r
+  // We allocated said ring in EfiBootServicesData type memory, and code\r
+  // executing after ExitBootServices() is permitted to overwrite it.\r
+  //\r
+  Dev = Context;\r
+  Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\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
 /**\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
+  Driver Execution Environment) with the following service.\r
 \r
   See DriverBindingSupported() for specification references.\r
 \r
 \r
   See DriverBindingSupported() for specification references.\r
 \r
@@ -813,13 +905,13 @@ VirtioBlkUninit (
 \r
 \r
   @retval EFI_SUCCESS           Driver instance has been created and\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
+                                initialized  for the virtio-blk device, it\r
+                                is now accessible 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
 \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
+                                service, the VirtIo protocol, VirtioBlkInit(),\r
                                 or the InstallProtocolInterface() boot service.\r
 \r
 **/\r
                                 or the InstallProtocolInterface() boot service.\r
 \r
 **/\r
@@ -840,43 +932,25 @@ VirtioBlkDriverBindingStart (
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
-                  (VOID **)&Dev->PciIo, This->DriverBindingHandle,\r
+  Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
+                  (VOID **)&Dev->VirtIo, This->DriverBindingHandle,\r
                   DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
   if (EFI_ERROR (Status)) {\r
     goto FreeVirtioBlk;\r
   }\r
 \r
   //\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
+  // VirtIo access granted, configure virtio-blk device.\r
   //\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
+  Status = VirtioBlkInit (Dev);\r
   if (EFI_ERROR (Status)) {\r
   if (EFI_ERROR (Status)) {\r
-    goto ClosePciIo;\r
+    goto CloseVirtIo;\r
   }\r
 \r
   }\r
 \r
-  //\r
-  // PCI IO access granted, configure virtio-blk device.\r
-  //\r
-  Status = VirtioBlkInit (Dev);\r
+  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,\r
+                  &VirtioBlkExitBoot, Dev, &Dev->ExitBoot);\r
   if (EFI_ERROR (Status)) {\r
   if (EFI_ERROR (Status)) {\r
-    goto RestorePciAttributes;\r
+    goto UninitDev;\r
   }\r
 \r
   //\r
   }\r
 \r
   //\r
@@ -887,20 +961,19 @@ VirtioBlkDriverBindingStart (
                   &gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE,\r
                   &Dev->BlockIo);\r
   if (EFI_ERROR (Status)) {\r
                   &gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE,\r
                   &Dev->BlockIo);\r
   if (EFI_ERROR (Status)) {\r
-    goto UninitDev;\r
+    goto CloseExitBoot;\r
   }\r
 \r
   return EFI_SUCCESS;\r
 \r
   }\r
 \r
   return EFI_SUCCESS;\r
 \r
+CloseExitBoot:\r
+  gBS->CloseEvent (Dev->ExitBoot);\r
+\r
 UninitDev:\r
   VirtioBlkUninit (Dev);\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
+CloseVirtIo:\r
+  gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
          This->DriverBindingHandle, DeviceHandle);\r
 \r
 FreeVirtioBlk:\r
          This->DriverBindingHandle, DeviceHandle);\r
 \r
 FreeVirtioBlk:\r
@@ -942,25 +1015,38 @@ VirtioBlkDriverBindingStop (
   IN EFI_HANDLE                  *ChildHandleBuffer\r
   )\r
 {\r
   IN EFI_HANDLE                  *ChildHandleBuffer\r
   )\r
 {\r
-  VBLK_DEV   *Dev;\r
-  EFI_STATUS Status;\r
+  EFI_STATUS            Status;\r
+  EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+  VBLK_DEV              *Dev;\r
 \r
 \r
-  Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+  Status = gBS->OpenProtocol (\r
+                  DeviceHandle,                  // candidate device\r
+                  &gEfiBlockIoProtocolGuid,      // retrieve the BlockIo iface\r
+                  (VOID **)&BlockIo,             // target pointer\r
+                  This->DriverBindingHandle,     // requestor driver identity\r
+                  DeviceHandle,                  // requesting lookup for dev.\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Dev = VIRTIO_BLK_FROM_BLOCK_IO (BlockIo);\r
 \r
   //\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
+  // Handle Stop() requests for in-use driver instances gracefully.\r
   //\r
   Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
                   &gEfiBlockIoProtocolGuid, &Dev->BlockIo);\r
   //\r
   Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
                   &gEfiBlockIoProtocolGuid, &Dev->BlockIo);\r
-  ASSERT (Status == EFI_SUCCESS);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
 \r
-  VirtioBlkUninit (Dev);\r
+  gBS->CloseEvent (Dev->ExitBoot);\r
 \r
 \r
-  Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
-                Dev->OriginalPciAttributes, NULL);\r
+  VirtioBlkUninit (Dev);\r
 \r
 \r
-  gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+  gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
          This->DriverBindingHandle, DeviceHandle);\r
 \r
   FreePool (Dev);\r
          This->DriverBindingHandle, DeviceHandle);\r
 \r
   FreePool (Dev);\r
@@ -997,13 +1083,13 @@ STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
 // for unambiguous identification.\r
 //\r
 \r
 // for unambiguous identification.\r
 //\r
 \r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+STATIC\r
 EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
   { "eng;en", L"Virtio Block Driver" },\r
   { NULL,     NULL                   }\r
 };\r
 \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
+STATIC\r
 EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
 \r
 EFI_STATUS\r
 EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
 \r
 EFI_STATUS\r
@@ -1036,14 +1122,14 @@ VirtioBlkGetDeviceName (
   return EFI_UNSUPPORTED;\r
 }\r
 \r
   return EFI_UNSUPPORTED;\r
 }\r
 \r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+STATIC\r
 EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
   &VirtioBlkGetDriverName,\r
   &VirtioBlkGetDeviceName,\r
   "eng" // SupportedLanguages, ISO 639-2 language codes\r
 };\r
 \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
+STATIC\r
 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
   (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &VirtioBlkGetDriverName,\r
   (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,\r
 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
   (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &VirtioBlkGetDriverName,\r
   (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,\r