[edk2] [PATCH 4/4] MdeModulePkg/AtaAtapiPassThru: unmap common buffers at ExitBootServices()

Laszlo Ersek posted 4 patches 7 years, 3 months ago
[edk2] [PATCH 4/4] MdeModulePkg/AtaAtapiPassThru: unmap common buffers at ExitBootServices()
Posted by Laszlo Ersek 7 years, 3 months ago
The AtaAtapiPassThru() driver maps three system memory regions for Bus
Master Common Buffer operation on the following call path, if the
controller being bound has PCI_CLASS_MASS_STORAGE_SATADPA class code:

  AtaAtapiPassThruStart()
    EnumerateAttachedDevice()
      AhciModeInitialization()
        AhciCreateTransferDescriptor()

The regions are unmapped when the controller is unbound, in
AtaAtapiPassThruStop().

The common buffers should also be de-programmed when we exit the boot
services and the OS gains ownership of the system memory. Introduce an
ExitBootServices() notification function that

- resets the controller so that it forgets the device addresses for the
  common buffers,

- unmaps the common buffers so that device-to-RAM address translations (in
  a potential IOMMU or another translator) are torn down.

ExitBootServices() notification functions must not alter the UEFI memory
map, thus call only PciIo->Unmap(), and not PciIo->FreeBuffer(). (Unlike
AtaAtapiPassThruStop() does.)

Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Star Zeng <star.zeng@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h         | 18 ++++++
 MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.h |  5 ++
 MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.c | 67 +++++++++++++++++++-
 3 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h
index 809bcc307fc4..d085597fbdfe 100644
--- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h
+++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h
@@ -367,5 +367,23 @@ AhciStopCommand (
   IN  UINT64                    Timeout
   );
 
+/**
+  Do AHCI HBA reset.
+
+  @param  PciIo              The PCI IO protocol instance.
+  @param  Timeout            The timeout value of reset, uses 100ns as a unit.
+
+  @retval EFI_DEVICE_ERROR   AHCI controller is failed to complete hardware
+                             reset.
+  @retval EFI_TIMEOUT        The reset operation is time out.
+  @retval EFI_SUCCESS        AHCI controller is reset successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciReset (
+  IN  EFI_PCI_IO_PROTOCOL       *PciIo,
+  IN  UINT64                    Timeout
+  );
 #endif
 
diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.h b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.h
index 4f327dc30b60..e51c66f392d2 100644
--- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.h
+++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.h
@@ -120,6 +120,11 @@ typedef struct {
   //
   EFI_EVENT                         TimerEvent;
   LIST_ENTRY                        NonBlockingTaskList;
+
+  //
+  // For resetting AHCI and unmapping CommonBuffer DMA at ExitBootServices().
+  //
+  EFI_EVENT                         ExitBootEvent;
 } ATA_ATAPI_PASS_THRU_INSTANCE;
 
 //
diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.c
index 795443ef74f6..6972349b2b8f 100644
--- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.c
+++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.c
@@ -103,7 +103,8 @@ ATA_ATAPI_PASS_THRU_INSTANCE gAtaAtapiPassThruInstanceTemplate = {
   {                   // NonBlocking TaskList
     NULL,
     NULL
-  }
+  },
+  NULL,               // ExitBootEvent
 };
 
 ATAPI_DEVICE_PATH    mAtapiDevicePathTemplate = {
@@ -477,6 +478,50 @@ InitializeAtaAtapiPassThru (
   return Status;
 }
 
+/**
+  If operating in AHCI mode, then reset the HBA and unmap CommonBuffer Bus
+  Master DMA when exiting the boot services.
+
+  @param[in] Event    Event for which this notification function is being
+                      called.
+  @param[in] Context  Pointer to the ATA_ATAPI_PASS_THRU_INSTANCE that
+                      represents the HBA.
+**/
+VOID
+EFIAPI
+AtaPassThruExitBootServices (
+  IN EFI_EVENT Event,
+  IN VOID      *Context
+  )
+{
+  ATA_ATAPI_PASS_THRU_INSTANCE *Instance;
+  EFI_PCI_IO_PROTOCOL          *PciIo;
+  EFI_AHCI_REGISTERS           *AhciRegisters;
+
+  Instance = Context;
+
+  if (Instance->Mode == EfiAtaAhciMode) {
+    PciIo = Instance->PciIo;
+    AhciRegisters = &Instance->AhciRegisters;
+    //
+    // De-program common buffer references from the HBA by resetting the HBA.
+    //
+    AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT);
+    //
+    // Unmap long-lived common buffers.
+    //
+    if (AhciRegisters->AhciRFis != NULL) {
+      PciIo->Unmap (PciIo, AhciRegisters->MapRFis);
+    }
+    if (AhciRegisters->AhciCmdList != NULL) {
+      PciIo->Unmap (PciIo, AhciRegisters->MapCmdList);
+    }
+    if (AhciRegisters->AhciCommandTable != NULL) {
+      PciIo->Unmap (PciIo, AhciRegisters->MapCommandTable);
+    }
+  }
+}
+
 /**
   Tests to see if this driver supports a given controller. If a child device is provided,
   it further tests to see if this driver supports creating a handle for the specified child device.
@@ -755,6 +800,17 @@ AtaAtapiPassThruStart (
   InitializeListHead(&Instance->DeviceList);
   InitializeListHead(&Instance->NonBlockingTaskList);
 
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                  TPL_CALLBACK,
+                  AtaPassThruExitBootServices,
+                  Instance,
+                  &Instance->ExitBootEvent
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ErrorExit;
+  }
+
   Instance->TimerEvent = NULL;
 
   Status = gBS->CreateEvent (
@@ -808,6 +864,10 @@ ErrorExit:
     gBS->CloseEvent (Instance->TimerEvent);
   }
 
+  if ((Instance != NULL) && (Instance->ExitBootEvent != NULL)) {
+    gBS->CloseEvent (Instance->ExitBootEvent);
+  }
+
   //
   // Remove all inserted ATA devices.
   //
@@ -912,6 +972,11 @@ AtaAtapiPassThruStop (
   //
   DestroyDeviceInfoList (Instance);
 
+  if (Instance->ExitBootEvent != NULL) {
+    gBS->CloseEvent (Instance->ExitBootEvent);
+    Instance->ExitBootEvent = NULL;
+  }
+
   //
   // If the current working mode is AHCI mode, then pre-allocated resource
   // for AHCI initialization should be released.
-- 
2.14.1.3.gb7cf6e02401b

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel