From 397408882da5346249ea5736bc07ed327226cb46 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:44 +0300
Subject: [PATCH 01/43] OvmfPkg/PvScsiDxe: Create empty driver

In preparation for support booting from PvScsi devices, create a
basic scaffolding for a driver.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-2-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 478c07d483d8f0b97df7e956457ec32bef92cf11)
---
 OvmfPkg/OvmfPkgIa32.dsc         |  8 ++++++++
 OvmfPkg/OvmfPkgIa32.fdf         |  3 +++
 OvmfPkg/OvmfPkgIa32X64.dsc      |  8 ++++++++
 OvmfPkg/OvmfPkgIa32X64.fdf      |  3 +++
 OvmfPkg/OvmfPkgX64.dsc          |  8 ++++++++
 OvmfPkg/OvmfPkgX64.fdf          |  3 +++
 OvmfPkg/PvScsiDxe/PvScsi.c      | 26 ++++++++++++++++++++++++++
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf | 27 +++++++++++++++++++++++++++
 8 files changed, 86 insertions(+)
 create mode 100644 OvmfPkg/PvScsiDxe/PvScsi.c
 create mode 100644 OvmfPkg/PvScsiDxe/PvScsiDxe.inf

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 9a60eb8fe2b0..82a9444bb92b 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -44,6 +44,11 @@ [Defines]
 
 !include NetworkPkg/NetworkDefines.dsc.inc
 
+  #
+  # Device drivers
+  #
+  DEFINE PVSCSI_ENABLE           = TRUE
+
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
   # one of the supported values, in place of any of the convenience macros, is
@@ -711,6 +716,9 @@ [Components]
   OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
   OvmfPkg/XenBusDxe/XenBusDxe.inf
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
+!if $(PVSCSI_ENABLE) == TRUE
+  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
   MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index 586bbff08585..d6b9c74e6cfe 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -227,6 +227,9 @@ [FV.DXEFV]
 INF  OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
 INF  OvmfPkg/XenBusDxe/XenBusDxe.inf
 INF  OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
+!if $(PVSCSI_ENABLE) == TRUE
+INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 1d1480b50b02..d34a51e86ba1 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -44,6 +44,11 @@ [Defines]
 
 !include NetworkPkg/NetworkDefines.dsc.inc
 
+  #
+  # Device drivers
+  #
+  DEFINE PVSCSI_ENABLE           = TRUE
+
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
   # one of the supported values, in place of any of the convenience macros, is
@@ -724,6 +729,9 @@ [Components.X64]
   OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
   OvmfPkg/XenBusDxe/XenBusDxe.inf
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
+!if $(PVSCSI_ENABLE) == TRUE
+  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
   MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index e49adc425fce..bce1bdb42d71 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -228,6 +228,9 @@ [FV.DXEFV]
 INF  OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
 INF  OvmfPkg/XenBusDxe/XenBusDxe.inf
 INF  OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
+!if $(PVSCSI_ENABLE) == TRUE
+INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index c287a436f8ec..55d975c4d3f9 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -44,6 +44,11 @@ [Defines]
 
 !include NetworkPkg/NetworkDefines.dsc.inc
 
+  #
+  # Device drivers
+  #
+  DEFINE PVSCSI_ENABLE           = TRUE
+
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
   # one of the supported values, in place of any of the convenience macros, is
@@ -722,6 +727,9 @@ [Components]
   OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
   OvmfPkg/XenBusDxe/XenBusDxe.inf
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
+!if $(PVSCSI_ENABLE) == TRUE
+  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
   MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index e49adc425fce..bce1bdb42d71 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -228,6 +228,9 @@ [FV.DXEFV]
 INF  OvmfPkg/XenIoPciDxe/XenIoPciDxe.inf
 INF  OvmfPkg/XenBusDxe/XenBusDxe.inf
 INF  OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
+!if $(PVSCSI_ENABLE) == TRUE
+INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
new file mode 100644
index 000000000000..1ae4de9869c1
--- /dev/null
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -0,0 +1,26 @@
+/** @file
+
+  This driver produces Extended SCSI Pass Thru Protocol instances for
+  pvscsi devices.
+
+  Copyright (C) 2020, Oracle and/or its affiliates.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiSpec.h>
+
+//
+// Entry Point
+//
+
+EFI_STATUS
+EFIAPI
+PvScsiEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
new file mode 100644
index 000000000000..093cc0171338
--- /dev/null
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -0,0 +1,27 @@
+## @file
+#
+# This driver produces Extended SCSI Pass Thru Protocol instances for
+# pvscsi devices.
+#
+# Copyright (C) 2020, Oracle and/or its affiliates.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = PvScsiDxe
+  FILE_GUID                      = 30346B14-1580-4781-879D-BA0C55AE9BB2
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PvScsiEntryPoint
+
+[Sources]
+  PvScsi.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
-- 
2.27.0


From aeb9ec41a3c52bc29dc937b9b8112ab14949bccc Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:45 +0300
Subject: [PATCH 02/43] OvmfPkg/PvScsiDxe: Install DriverBinding protocol

In order to probe and connect to the PvScsi device we need this
protocol. Currently it does nothing.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-3-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit ed08c5711339c870e8793319401e07526d7b816c)
---
 OvmfPkg/PvScsiDxe/PvScsi.c      | 66 ++++++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf |  1 +
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 1ae4de9869c1..77b28b326784 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -9,8 +9,65 @@
 
 **/
 
+#include <Library/UefiLib.h>
 #include <Uefi/UefiSpec.h>
 
+//
+// Higher versions will be used before lower, 0x10-0xffffffef is the version
+// range for IHV (Indie Hardware Vendors)
+//
+#define PVSCSI_BINDING_VERSION      0x10
+
+//
+// Driver Binding
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiDriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
+  &PvScsiDriverBindingSupported,
+  &PvScsiDriverBindingStart,
+  &PvScsiDriverBindingStop,
+  PVSCSI_BINDING_VERSION,
+  NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
+  NULL  // DriverBindingHandle, filled as well
+};
+
 //
 // Entry Point
 //
@@ -22,5 +79,12 @@ PvScsiEntryPoint (
   IN EFI_SYSTEM_TABLE *SystemTable
   )
 {
-  return EFI_UNSUPPORTED;
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &mPvScsiDriverBinding,
+           ImageHandle,
+           NULL, // TODO Component name
+           NULL  // TODO Component name
+           );
 }
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
index 093cc0171338..d1d0e963f96d 100644
--- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -25,3 +25,4 @@ [Packages]
 
 [LibraryClasses]
   UefiDriverEntryPoint
+  UefiLib
-- 
2.27.0


From c9b3b6d95caeb540ee42be256105b8735c54c7b1 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:46 +0300
Subject: [PATCH 03/43] OvmfPkg/PvScsiDxe: Report name of driver

Install Component Name protocols to have a nice display name for the
driver in places such as UEFI shell.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-4-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 419b30d642264bef34f9054a4adf64bf4f9e31aa)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 59 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 77b28b326784..51b03f709040 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -68,6 +68,61 @@ STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
   NULL  // DriverBindingHandle, filled as well
 };
 
+//
+// Component Name
+//
+
+STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+  { "eng;en", L"PVSCSI Host Driver" },
+  { NULL,     NULL                  }
+};
+
+STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &mComponentName) // Iso639Language
+           );
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiGetDeviceName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  DeviceHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
+  &PvScsiGetDriverName,
+  &PvScsiGetDeviceName,
+  "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &PvScsiGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,
+  "en" // SupportedLanguages, RFC 4646 language codes
+};
+
 //
 // Entry Point
 //
@@ -84,7 +139,7 @@ PvScsiEntryPoint (
            SystemTable,
            &mPvScsiDriverBinding,
            ImageHandle,
-           NULL, // TODO Component name
-           NULL  // TODO Component name
+           &mComponentName,
+           &mComponentName2
            );
 }
-- 
2.27.0


From bb19235ec75cb2a65903f3ca3d0e7124197b5fd4 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:47 +0300
Subject: [PATCH 04/43] OvmfPkg/PvScsiDxe: Probe PCI devices and look for
 PvScsi

PvScsiControllerSupported() is called on handles passed in
by the ConnectController() boot service and if the handle is the
PVSCSI controller, the function would return success. A success
return value will attach our driver to the device.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-5-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit a9f9d5cf5636d9757be542a2544729f343bb607e)
---
 OvmfPkg/Include/IndustryStandard/PvScsi.h | 21 ++++++++++
 OvmfPkg/PvScsiDxe/PvScsi.c                | 49 ++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf           |  5 +++
 3 files changed, 74 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/Include/IndustryStandard/PvScsi.h

diff --git a/OvmfPkg/Include/IndustryStandard/PvScsi.h b/OvmfPkg/Include/IndustryStandard/PvScsi.h
new file mode 100644
index 000000000000..004c0af84989
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/PvScsi.h
@@ -0,0 +1,21 @@
+/** @file
+
+  VMware PVSCSI Device specific type and macro definitions.
+
+  Copyright (C) 2020, Oracle and/or its affiliates.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __PVSCSI_H_
+#define __PVSCSI_H_
+
+//
+// Device offsets and constants
+//
+
+#define PCI_VENDOR_ID_VMWARE            (0x15ad)
+#define PCI_DEVICE_ID_VMWARE_PVSCSI     (0x07c0)
+
+#endif // __PVSCSI_H_
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 51b03f709040..9923a31d25d7 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -9,7 +9,11 @@
 
 **/
 
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/PvScsi.h>
+#include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
+#include <Protocol/PciIo.h>
 #include <Uefi/UefiSpec.h>
 
 //
@@ -31,7 +35,50 @@ PvScsiDriverBindingSupported (
   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&PciIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        0,
+                        sizeof (Pci) / sizeof (UINT32),
+                        &Pci
+                        );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||
+      (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  Status = EFI_SUCCESS;
+
+Done:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
+  return Status;
 }
 
 STATIC
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
index d1d0e963f96d..c1f0663832ed 100644
--- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -22,7 +22,12 @@ [Sources]
 
 [Packages]
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
+
+[Protocols]
+  gEfiPciIoProtocolGuid        ## TO_START
-- 
2.27.0


From 5722da4cac12d298221db65e2892fd516027f23d Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:48 +0300
Subject: [PATCH 05/43] OvmfPkg/PvScsiDxe: Install stubbed EXT_SCSI_PASS_THRU

Support dynamic insertion and removal of the protocol.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-6-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit e497432c2ceadc3924f96e49593c8a77044efea7)
---
 OvmfPkg/PvScsiDxe/PvScsi.c      | 209 +++++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsi.h      |  29 +++++
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf |   6 +-
 3 files changed, 241 insertions(+), 3 deletions(-)
 create mode 100644 OvmfPkg/PvScsiDxe/PvScsi.h

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 9923a31d25d7..04c08036b799 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -11,17 +11,156 @@
 
 #include <IndustryStandard/Pci.h>
 #include <IndustryStandard/PvScsi.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
 #include <Uefi/UefiSpec.h>
 
+#include "PvScsi.h"
+
 //
 // Higher versions will be used before lower, 0x10-0xffffffef is the version
 // range for IHV (Indie Hardware Vendors)
 //
 #define PVSCSI_BINDING_VERSION      0x10
 
+//
+// Ext SCSI Pass Thru
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiPassThru (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+  IN EFI_EVENT                                      Event    OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiGetNextTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN OUT UINT8                                      **Target,
+  IN OUT UINT64                                     *Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiBuildDevicePath (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL                   **DevicePath
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiGetTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN EFI_DEVICE_PATH_PROTOCOL                       *DevicePath,
+  OUT UINT8                                         **Target,
+  OUT UINT64                                        *Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiResetChannel (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiResetTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+PvScsiGetNextTarget (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN OUT UINT8                                      **Target
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+PvScsiInit (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  //
+  // Populate the exported interface's attributes
+  //
+  Dev->PassThru.Mode             = &Dev->PassThruMode;
+  Dev->PassThru.PassThru         = &PvScsiPassThru;
+  Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;
+  Dev->PassThru.BuildDevicePath  = &PvScsiBuildDevicePath;
+  Dev->PassThru.GetTargetLun     = &PvScsiGetTargetLun;
+  Dev->PassThru.ResetChannel     = &PvScsiResetChannel;
+  Dev->PassThru.ResetTargetLun   = &PvScsiResetTargetLun;
+  Dev->PassThru.GetNextTarget    = &PvScsiGetNextTarget;
+
+  //
+  // AdapterId is a target for which no handle will be created during bus scan.
+  // Prevent any conflict with real devices.
+  //
+  Dev->PassThruMode.AdapterId = MAX_UINT32;
+
+  //
+  // Set both physical and logical attributes for non-RAID SCSI channel
+  //
+  Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
+                                 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
+
+  //
+  // No restriction on transfer buffer alignment
+  //
+  Dev->PassThruMode.IoAlign = 0;
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+PvScsiUninit (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  // Currently nothing to do here
+}
+
 //
 // Driver Binding
 //
@@ -90,7 +229,42 @@ PvScsiDriverBindingStart (
   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  PVSCSI_DEV *Dev;
+  EFI_STATUS Status;
+
+  Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev));
+  if (Dev == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = PvScsiInit (Dev);
+  if (EFI_ERROR (Status)) {
+    goto FreePvScsi;
+  }
+
+  //
+  // Setup complete, attempt to export the driver instance's PassThru interface
+  //
+  Dev->Signature = PVSCSI_SIG;
+  Status = gBS->InstallProtocolInterface (
+                  &ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Dev->PassThru
+                  );
+  if (EFI_ERROR (Status)) {
+    goto UninitDev;
+  }
+
+  return EFI_SUCCESS;
+
+UninitDev:
+  PvScsiUninit (Dev);
+
+FreePvScsi:
+  FreePool (Dev);
+
+  return Status;
 }
 
 STATIC
@@ -103,7 +277,38 @@ PvScsiDriverBindingStop (
   IN EFI_HANDLE                  *ChildHandleBuffer
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS                      Status;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+  PVSCSI_DEV                      *Dev;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  (VOID **)&PassThru,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Dev = PVSCSI_FROM_PASS_THRU (PassThru);
+
+  Status = gBS->UninstallProtocolInterface (
+                  ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  &Dev->PassThru
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  PvScsiUninit (Dev);
+
+  FreePool (Dev);
+
+  return EFI_SUCCESS;
 }
 
 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
new file mode 100644
index 000000000000..3940b4c20019
--- /dev/null
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -0,0 +1,29 @@
+/** @file
+
+  Internal definitions for the PVSCSI driver, which produces Extended SCSI
+  Pass Thru Protocol instances for pvscsi devices.
+
+  Copyright (C) 2020, Oracle and/or its affiliates.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __PVSCSI_DXE_H_
+#define __PVSCSI_DXE_H_
+
+#include <Library/DebugLib.h>
+#include <Protocol/ScsiPassThruExt.h>
+
+#define PVSCSI_SIG SIGNATURE_32 ('P', 'S', 'C', 'S')
+
+typedef struct {
+  UINT32                          Signature;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
+  EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
+} PVSCSI_DEV;
+
+#define PVSCSI_FROM_PASS_THRU(PassThruPointer) \
+  CR (PassThruPointer, PVSCSI_DEV, PassThru, PVSCSI_SIG)
+
+#endif // __PVSCSI_DXE_H_
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
index c1f0663832ed..f4d452c6c3d2 100644
--- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -19,15 +19,19 @@ [Defines]
 
 [Sources]
   PvScsi.c
+  PvScsi.h
 
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
 
 [Protocols]
-  gEfiPciIoProtocolGuid        ## TO_START
+  gEfiExtScsiPassThruProtocolGuid   ## BY_START
+  gEfiPciIoProtocolGuid             ## TO_START
-- 
2.27.0


From 585f196933b0dd9e2f4bccad8a386415d9c1bf76 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:49 +0300
Subject: [PATCH 06/43] OvmfPkg/PvScsiDxe: Report the number of targets and
 LUNs

Implement EXT_SCSI_PASS_THRU.GetNextTarget() and
EXT_SCSI_PASS_THRU.GetNextTargetLun().

ScsiBusDxe scans all MaxTarget * MaxLun possible devices.
This can take unnecessarily long for large number of targets.
To deal with this, VirtioScsiDxe has defined PCDs to limit the
MaxTarget & MaxLun to desired values which gives sufficient
performance. It is very important in virtio-scsi as it can have
very big MaxTarget & MaxLun.
Even though a common PVSCSI device has a default MaxTarget=64 and
MaxLun=0, we implement similar mechanism as virtio-scsi for completeness.
This may be useful in the future when PVSCSI will have bigger values
for MaxTarget and MaxLun.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-7-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 7efce2e59c2016f0b2572d4b79845f14a8758475)
---
 OvmfPkg/OvmfPkg.dec             |   9 +++
 OvmfPkg/PvScsiDxe/PvScsi.c      | 122 +++++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsi.h      |   2 +
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf |   5 ++
 4 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index d5fee805ef4a..b0893c3591ee 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -121,6 +121,15 @@ [PcdsFixedAtBuild]
   gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxTargetLimit|31|UINT16|6
   gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxLunLimit|7|UINT32|7
 
+  ## Sets the *inclusive* number of targets and LUNs that PvScsi exposes for
+  #  scan by ScsiBusDxe.
+  #  As specified above for VirtioScsi, ScsiBusDxe scans all MaxTarget * MaxLun
+  #  possible devices, which can take extremely long. Thus, the below constants
+  #  are used so that scanning the number of devices given by their product
+  #  is still acceptably fast.
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit|64|UINT8|0x36
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit|0|UINT8|0x37
+
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 04c08036b799..7f51ada19a1a 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -11,6 +11,7 @@
 
 #include <IndustryStandard/Pci.h>
 #include <IndustryStandard/PvScsi.h>
+#include <Library/BaseMemoryLib.h>
 #include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
@@ -25,6 +26,30 @@
 //
 #define PVSCSI_BINDING_VERSION      0x10
 
+//
+// Ext SCSI Pass Thru utilities
+//
+
+/**
+  Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
+  EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
+**/
+STATIC
+BOOLEAN
+IsTargetInitialized (
+  IN UINT8                                          *Target
+  )
+{
+  UINTN Idx;
+
+  for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
+    if (Target[Idx] != 0xFF) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
 //
 // Ext SCSI Pass Thru
 //
@@ -52,7 +77,54 @@ PvScsiGetNextTargetLun (
   IN OUT UINT64                                     *Lun
   )
 {
-  return EFI_UNSUPPORTED;
+  UINT8      *TargetPtr;
+  UINT8      LastTarget;
+  PVSCSI_DEV *Dev;
+
+  if (Target == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // The Target input parameter is unnecessarily a pointer-to-pointer
+  //
+  TargetPtr = *Target;
+
+  //
+  // If target not initialized, return first target & LUN
+  //
+  if (!IsTargetInitialized (TargetPtr)) {
+    ZeroMem (TargetPtr, TARGET_MAX_BYTES);
+    *Lun = 0;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // We only use first byte of target identifer
+  //
+  LastTarget = *TargetPtr;
+
+  //
+  // Increment (target, LUN) pair if valid on input
+  //
+  Dev = PVSCSI_FROM_PASS_THRU (This);
+  if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (*Lun < Dev->MaxLun) {
+    ++*Lun;
+    return EFI_SUCCESS;
+  }
+
+  if (LastTarget < Dev->MaxTarget) {
+    *Lun = 0;
+    ++LastTarget;
+    *TargetPtr = LastTarget;
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_FOUND;
 }
 
 STATIC
@@ -111,7 +183,47 @@ PvScsiGetNextTarget (
   IN OUT UINT8                                      **Target
   )
 {
-  return EFI_UNSUPPORTED;
+  UINT8      *TargetPtr;
+  UINT8      LastTarget;
+  PVSCSI_DEV *Dev;
+
+  if (Target == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // The Target input parameter is unnecessarily a pointer-to-pointer
+  //
+  TargetPtr = *Target;
+
+  //
+  // If target not initialized, return first target
+  //
+  if (!IsTargetInitialized (TargetPtr)) {
+    ZeroMem (TargetPtr, TARGET_MAX_BYTES);
+    return EFI_SUCCESS;
+  }
+
+  //
+  // We only use first byte of target identifer
+  //
+  LastTarget = *TargetPtr;
+
+  //
+  // Increment target if valid on input
+  //
+  Dev = PVSCSI_FROM_PASS_THRU (This);
+  if (LastTarget > Dev->MaxTarget) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (LastTarget < Dev->MaxTarget) {
+    ++LastTarget;
+    *TargetPtr = LastTarget;
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_FOUND;
 }
 
 STATIC
@@ -120,6 +232,12 @@ PvScsiInit (
   IN OUT PVSCSI_DEV *Dev
   )
 {
+  //
+  // Init configuration
+  //
+  Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);
+  Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);
+
   //
   // Populate the exported interface's attributes
   //
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index 3940b4c20019..dd3e0c68e6da 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -19,6 +19,8 @@
 
 typedef struct {
   UINT32                          Signature;
+  UINT8                           MaxTarget;
+  UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
 } PVSCSI_DEV;
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
index f4d452c6c3d2..fcffc90d46c8 100644
--- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -26,6 +26,7 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  BaseMemoryLib
   DebugLib
   MemoryAllocationLib
   UefiBootServicesTableLib
@@ -35,3 +36,7 @@ [LibraryClasses]
 [Protocols]
   gEfiExtScsiPassThruProtocolGuid   ## BY_START
   gEfiPciIoProtocolGuid             ## TO_START
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit       ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit    ## CONSUMES
-- 
2.27.0


From aa567c940fbf25f8437c31d48a32ef8bffd32f1d Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:50 +0300
Subject: [PATCH 07/43] OvmfPkg/PvScsiDxe: Translate Target & LUN to/from
 DevicePath

Implement EXT_SCSI_PASS_THRU.BuildDevicePath() and
EXT_SCSI_PASS_THRU.GetTargetLun().

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-8-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 9c2d8281af9bc4a3c598994ba1fee96f49dfc8e9)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 61 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 7f51ada19a1a..76fc1eb910f2 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -137,7 +137,38 @@ PvScsiBuildDevicePath (
   IN OUT EFI_DEVICE_PATH_PROTOCOL                   **DevicePath
   )
 {
-  return EFI_UNSUPPORTED;
+  UINT8             TargetValue;
+  PVSCSI_DEV        *Dev;
+  SCSI_DEVICE_PATH  *ScsiDevicePath;
+
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // We only use first byte of target identifer
+  //
+  TargetValue = *Target;
+
+  Dev = PVSCSI_FROM_PASS_THRU (This);
+  if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun) {
+    return EFI_NOT_FOUND;
+  }
+
+  ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath));
+  if (ScsiDevicePath == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ScsiDevicePath->Header.Type      = MESSAGING_DEVICE_PATH;
+  ScsiDevicePath->Header.SubType   = MSG_SCSI_DP;
+  ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
+  ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
+  ScsiDevicePath->Pun              = TargetValue;
+  ScsiDevicePath->Lun              = (UINT16)Lun;
+
+  *DevicePath = &ScsiDevicePath->Header;
+  return EFI_SUCCESS;
 }
 
 STATIC
@@ -150,7 +181,33 @@ PvScsiGetTargetLun (
   OUT UINT64                                        *Lun
   )
 {
-  return EFI_UNSUPPORTED;
+  SCSI_DEVICE_PATH *ScsiDevicePath;
+  PVSCSI_DEV       *Dev;
+
+  if (DevicePath == NULL || Target == NULL || *Target == NULL || Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DevicePath->Type    != MESSAGING_DEVICE_PATH ||
+      DevicePath->SubType != MSG_SCSI_DP) {
+    return EFI_UNSUPPORTED;
+  }
+
+  ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
+  Dev = PVSCSI_FROM_PASS_THRU (This);
+  if (ScsiDevicePath->Pun > Dev->MaxTarget ||
+      ScsiDevicePath->Lun > Dev->MaxLun) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // We only use first byte of target identifer
+  //
+  **Target = (UINT8)ScsiDevicePath->Pun;
+  ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1);
+  *Lun = ScsiDevicePath->Lun;
+
+  return EFI_SUCCESS;
 }
 
 STATIC
-- 
2.27.0


From 13f8888e012eaa17151808e09b061e76d3f81d4a Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:51 +0300
Subject: [PATCH 08/43] OvmfPkg/PvScsiDxe: Open PciIo protocol for later use

This will give us an exclusive access to the PciIo of this device
after it was started and until it will be stopped.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-9-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit c08eaaaf373bafe71f8a506d55230f1a4874e9e2)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 29 ++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsi.h |  1 +
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 76fc1eb910f2..e0380d729b3c 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -412,11 +412,23 @@ PvScsiDriverBindingStart (
     return EFI_OUT_OF_RESOURCES;
   }
 
-  Status = PvScsiInit (Dev);
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&Dev->PciIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
   if (EFI_ERROR (Status)) {
     goto FreePvScsi;
   }
 
+  Status = PvScsiInit (Dev);
+  if (EFI_ERROR (Status)) {
+    goto ClosePciIo;
+  }
+
   //
   // Setup complete, attempt to export the driver instance's PassThru interface
   //
@@ -436,6 +448,14 @@ PvScsiDriverBindingStart (
 UninitDev:
   PvScsiUninit (Dev);
 
+ClosePciIo:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
 FreePvScsi:
   FreePool (Dev);
 
@@ -481,6 +501,13 @@ PvScsiDriverBindingStop (
 
   PvScsiUninit (Dev);
 
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
   FreePool (Dev);
 
   return EFI_SUCCESS;
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index dd3e0c68e6da..e1e5ae18ebf2 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -19,6 +19,7 @@
 
 typedef struct {
   UINT32                          Signature;
+  EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
-- 
2.27.0


From 4eb8af37fb838ecd95637bb64cdf79c752b11b41 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:52 +0300
Subject: [PATCH 09/43] OvmfPkg/PvScsiDxe: Backup/Restore PCI attributes on
 Init/UnInit

This commit doesn't change semantics.
It is done as a preparation for future commits which will modify
PCI attributes.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-10-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 45098e8a9aeefaf8405e4b772ffa2694efe507a0)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 54 +++++++++++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsi.h |  1 +
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index e0380d729b3c..5566b4cce467 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -283,18 +283,70 @@ PvScsiGetNextTarget (
   return EFI_NOT_FOUND;
 }
 
+STATIC
+EFI_STATUS
+PvScsiSetPciAttributes (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // Backup original PCI Attributes
+  //
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationGet,
+                         0,
+                         &Dev->OriginalPciAttributes
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // TODO: Change PCI Attributes
+  //
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+PvScsiRestorePciAttributes (
+  IN PVSCSI_DEV *Dev
+  )
+{
+  Dev->PciIo->Attributes (
+                Dev->PciIo,
+                EfiPciIoAttributeOperationSet,
+                Dev->OriginalPciAttributes,
+                NULL
+                );
+}
+
 STATIC
 EFI_STATUS
 PvScsiInit (
   IN OUT PVSCSI_DEV *Dev
   )
 {
+  EFI_STATUS Status;
+
   //
   // Init configuration
   //
   Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);
   Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);
 
+  //
+  // Set PCI Attributes
+  //
+  Status = PvScsiSetPciAttributes (Dev);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
   //
   // Populate the exported interface's attributes
   //
@@ -333,7 +385,7 @@ PvScsiUninit (
   IN OUT PVSCSI_DEV *Dev
   )
 {
-  // Currently nothing to do here
+  PvScsiRestorePciAttributes (Dev);
 }
 
 //
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index e1e5ae18ebf2..5f611dbbc98c 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -20,6 +20,7 @@
 typedef struct {
   UINT32                          Signature;
   EFI_PCI_IO_PROTOCOL             *PciIo;
+  UINT64                          OriginalPciAttributes;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
-- 
2.27.0


From f16e4c898de848bdc154c2d15143848a473f960c Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:53 +0300
Subject: [PATCH 10/43] OvmfPkg/PvScsiDxe: Enable MMIO-Space & Bus-Mastering in
 PCI attributes

Enable MMIO-Space & Bus-Mastering PCI attributes when device is started.
Note that original PCI attributes are restored when device is stopped.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-11-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 6672b3cff2e164093ebc120ef8304472b9a7d23f)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 5566b4cce467..531bed4e5ab7 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -305,8 +305,18 @@ PvScsiSetPciAttributes (
   }
 
   //
-  // TODO: Change PCI Attributes
+  // Enable MMIO-Space & Bus-Mastering
   //
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationEnable,
+                         (EFI_PCI_IO_ATTRIBUTE_MEMORY |
+                          EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
+                         NULL
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
 
   return EFI_SUCCESS;
 }
-- 
2.27.0


From 9e79a360afc579b87e1d88b75de116934c020711 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:54 +0300
Subject: [PATCH 11/43] OvmfPkg/PvScsiDxe: Define device interface structures
 and constants

These definitions will be used by the following commits to complete the
implementation of PVSCSI device driver.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Acked-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-12-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 447e5d39025a80ad2810559ccda43cdc73c12b16)
---
 OvmfPkg/Include/IndustryStandard/PvScsi.h | 165 ++++++++++++++++++++++
 1 file changed, 165 insertions(+)

diff --git a/OvmfPkg/Include/IndustryStandard/PvScsi.h b/OvmfPkg/Include/IndustryStandard/PvScsi.h
index 004c0af84989..a4d6634f3ba0 100644
--- a/OvmfPkg/Include/IndustryStandard/PvScsi.h
+++ b/OvmfPkg/Include/IndustryStandard/PvScsi.h
@@ -18,4 +18,169 @@
 #define PCI_VENDOR_ID_VMWARE            (0x15ad)
 #define PCI_DEVICE_ID_VMWARE_PVSCSI     (0x07c0)
 
+//
+// CDB (Command Descriptor Block) with size above this constant
+// should be considered out-of-band
+//
+#define PVSCSI_CDB_MAX_SIZE         (16)
+
+typedef enum {
+  PvScsiRegOffsetCommand           =    0x0,
+  PvScsiRegOffsetCommandData       =    0x4,
+  PvScsiRegOffsetCommandStatus     =    0x8,
+  PvScsiRegOffsetLastSts0          =  0x100,
+  PvScsiRegOffsetLastSts1          =  0x104,
+  PvScsiRegOffsetLastSts2          =  0x108,
+  PvScsiRegOffsetLastSts3          =  0x10c,
+  PvScsiRegOffsetIntrStatus        = 0x100c,
+  PvScsiRegOffsetIntrMask          = 0x2010,
+  PvScsiRegOffsetKickNonRwIo       = 0x3014,
+  PvScsiRegOffsetDebug             = 0x3018,
+  PvScsiRegOffsetKickRwIo          = 0x4018,
+} PVSCSI_BAR0_OFFSETS;
+
+//
+// Define Interrupt-Status register flags
+//
+#define PVSCSI_INTR_CMPL_0      BIT0
+#define PVSCSI_INTR_CMPL_1      BIT1
+#define PVSCSI_INTR_CMPL_MASK   (PVSCSI_INTR_CMPL_0 | PVSCSI_INTR_CMPL_1)
+
+typedef enum {
+  PvScsiCmdFirst               = 0,
+  PvScsiCmdAdapterReset        = 1,
+  PvScsiCmdIssueScsi           = 2,
+  PvScsiCmdSetupRings          = 3,
+  PvScsiCmdResetBus            = 4,
+  PvScsiCmdResetDevice         = 5,
+  PvScsiCmdAbortCmd            = 6,
+  PvScsiCmdConfig              = 7,
+  PvScsiCmdSetupMsgRing        = 8,
+  PvScsiCmdDeviceUnplug        = 9,
+  PvScsiCmdLast                = 10
+} PVSCSI_COMMANDS;
+
+#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES    (32)
+
+#pragma pack (1)
+typedef struct {
+  UINT32 ReqRingNumPages;
+  UINT32 CmpRingNumPages;
+  UINT64 RingsStatePPN;
+  UINT64 ReqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+  UINT64 CmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+} PVSCSI_CMD_DESC_SETUP_RINGS;
+#pragma pack ()
+
+#define PVSCSI_MAX_CMD_DATA_WORDS   \
+  (sizeof (PVSCSI_CMD_DESC_SETUP_RINGS) / sizeof (UINT32))
+
+#pragma pack (1)
+typedef struct {
+  UINT32 ReqProdIdx;
+  UINT32 ReqConsIdx;
+  UINT32 ReqNumEntriesLog2;
+
+  UINT32 CmpProdIdx;
+  UINT32 CmpConsIdx;
+  UINT32 CmpNumEntriesLog2;
+
+  UINT8  Pad[104];
+
+  UINT32 MsgProdIdx;
+  UINT32 MsgConsIdx;
+  UINT32 MsgNumEntriesLog2;
+} PVSCSI_RINGS_STATE;
+#pragma pack ()
+
+//
+// Define PVSCSI request descriptor tags
+//
+#define PVSCSI_SIMPLE_QUEUE_TAG            (0x20)
+
+//
+// Define PVSCSI request descriptor flags
+//
+#define PVSCSI_FLAG_CMD_WITH_SG_LIST       BIT0
+#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB    BIT1
+#define PVSCSI_FLAG_CMD_DIR_NONE           BIT2
+#define PVSCSI_FLAG_CMD_DIR_TOHOST         BIT3
+#define PVSCSI_FLAG_CMD_DIR_TODEVICE       BIT4
+
+#pragma pack (1)
+typedef struct {
+  UINT64 Context;
+  UINT64 DataAddr;
+  UINT64 DataLen;
+  UINT64 SenseAddr;
+  UINT32 SenseLen;
+  UINT32 Flags;
+  UINT8  Cdb[16];
+  UINT8  CdbLen;
+  UINT8  Lun[8];
+  UINT8  Tag;
+  UINT8  Bus;
+  UINT8  Target;
+  UINT8  VcpuHint;
+  UINT8  Unused[59];
+} PVSCSI_RING_REQ_DESC;
+#pragma pack ()
+
+//
+// Host adapter status/error codes
+//
+typedef enum {
+  PvScsiBtStatSuccess       = 0x00,  // CCB complete normally with no errors
+  PvScsiBtStatLinkedCommandCompleted         = 0x0a,
+  PvScsiBtStatLinkedCommandCompletedWithFlag = 0x0b,
+  PvScsiBtStatDataUnderrun  = 0x0c,
+  PvScsiBtStatSelTimeout    = 0x11,  // SCSI selection timeout
+  PvScsiBtStatDatarun       = 0x12,  // Data overrun/underrun
+  PvScsiBtStatBusFree       = 0x13,  // Unexpected bus free
+  PvScsiBtStatInvPhase      = 0x14,  //
+                                     // Invalid bus phase or sequence requested
+                                     // by target
+                                     //
+  PvScsiBtStatLunMismatch   = 0x17,  //
+                                     // Linked CCB has different LUN from first
+                                     // CCB
+                                     //
+  PvScsiBtStatSensFailed    = 0x1b,  // Auto request sense failed
+  PvScsiBtStatTagReject     = 0x1c,  //
+                                     // SCSI II tagged queueing message rejected
+                                     // by target
+                                     //
+  PvScsiBtStatBadMsg        = 0x1d,  //
+                                     // Unsupported message received by the host
+                                     // adapter
+                                     //
+  PvScsiBtStatHaHardware    = 0x20,  // Host adapter hardware failed
+  PvScsiBtStatNoResponse    = 0x21,  //
+                                     // Target did not respond to SCSI ATN sent
+                                     // a SCSI RST
+                                     //
+  PvScsiBtStatSentRst       = 0x22,  // Host adapter asserted a SCSI RST
+  PvScsiBtStatRecvRst       = 0x23,  // Other SCSI devices asserted a SCSI RST
+  PvScsiBtStatDisconnect    = 0x24,  //
+                                     // Target device reconnected improperly
+                                     // (w/o tag)
+                                     //
+  PvScsiBtStatBusReset      = 0x25,  // Host adapter issued BUS device reset
+  PvScsiBtStatAbortQueue    = 0x26,  // Abort queue generated
+  PvScsiBtStatHaSoftware    = 0x27,  // Host adapter software error
+  PvScsiBtStatHaTimeout     = 0x30,  // Host adapter hardware timeout error
+  PvScsiBtStatScsiParity    = 0x34,  // SCSI parity error detected
+} PVSCSI_HOST_BUS_ADAPTER_STATUS;
+
+#pragma pack (1)
+typedef struct {
+  UINT64 Context;
+  UINT64 DataLen;
+  UINT32 SenseLen;
+  UINT16 HostStatus;
+  UINT16 ScsiStatus;
+  UINT32 Pad[2];
+} PVSCSI_RING_CMP_DESC;
+#pragma pack ()
+
 #endif // __PVSCSI_H_
-- 
2.27.0


From 5fb1691240d789ce9383a5902761c2d60ab42abf Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:55 +0300
Subject: [PATCH 12/43] OvmfPkg/PvScsiDxe: Reset adapter on init

The following commits will complete the implementation of
device initialization.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-13-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 5269c26e07381dcc7e9de4fe1f2a5b7ed1e5c78a)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 114 +++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 531bed4e5ab7..cf75884350ee 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -30,6 +30,107 @@
 // Ext SCSI Pass Thru utilities
 //
 
+/**
+  Writes a 32-bit value into BAR0 using MMIO
+**/
+STATIC
+EFI_STATUS
+PvScsiMmioWrite32 (
+  IN CONST PVSCSI_DEV   *Dev,
+  IN UINT64             Offset,
+  IN UINT32             Value
+  )
+{
+  return Dev->PciIo->Mem.Write (
+                           Dev->PciIo,
+                           EfiPciIoWidthUint32,
+                           PCI_BAR_IDX0,
+                           Offset,
+                           1,   // Count
+                           &Value
+                           );
+}
+
+/**
+  Writes multiple words of data into BAR0 using MMIO
+**/
+STATIC
+EFI_STATUS
+PvScsiMmioWrite32Multiple (
+  IN CONST PVSCSI_DEV   *Dev,
+  IN UINT64             Offset,
+  IN UINTN              Count,
+  IN UINT32             *Words
+  )
+{
+  return Dev->PciIo->Mem.Write (
+                           Dev->PciIo,
+                           EfiPciIoWidthFifoUint32,
+                           PCI_BAR_IDX0,
+                           Offset,
+                           Count,
+                           Words
+                           );
+}
+
+/**
+  Send a PVSCSI command to device.
+
+  @param[in] Dev                    The pvscsi host device.
+  @param[in] Cmd                    The command to send to device.
+  @param[in] OPTIONAL DescWords     An optional command descriptor (If command
+                                    have a descriptor). The descriptor is
+                                    provided as an array of UINT32 words and
+                                    is must be 32-bit aligned.
+  @param[in] DescWordsCount         The number of words in command descriptor.
+                                    Caller must specify here 0 if DescWords
+                                    is not supplied (It is optional). In that
+                                    case, DescWords is ignored.
+
+  @return   Status codes returned by Dev->PciIo->Mem.Write().
+
+**/
+STATIC
+EFI_STATUS
+PvScsiWriteCmdDesc (
+  IN CONST PVSCSI_DEV   *Dev,
+  IN UINT32             Cmd,
+  IN UINT32             *DescWords      OPTIONAL,
+  IN UINTN              DescWordsCount
+  )
+{
+  EFI_STATUS Status;
+
+  if (DescWordsCount > PVSCSI_MAX_CMD_DATA_WORDS) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetCommand, Cmd);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (DescWordsCount > 0) {
+    return PvScsiMmioWrite32Multiple (
+             Dev,
+             PvScsiRegOffsetCommandData,
+             DescWordsCount,
+             DescWords
+             );
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+PvScsiResetAdapter (
+  IN CONST PVSCSI_DEV   *Dev
+  )
+{
+  return PvScsiWriteCmdDesc (Dev, PvScsiCmdAdapterReset, NULL, 0);
+}
+
 /**
   Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
   EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
@@ -357,6 +458,14 @@ PvScsiInit (
     return Status;
   }
 
+  //
+  // Reset adapter
+  //
+  Status = PvScsiResetAdapter (Dev);
+  if (EFI_ERROR (Status)) {
+    goto RestorePciAttributes;
+  }
+
   //
   // Populate the exported interface's attributes
   //
@@ -387,6 +496,11 @@ PvScsiInit (
   Dev->PassThruMode.IoAlign = 0;
 
   return EFI_SUCCESS;
+
+RestorePciAttributes:
+  PvScsiRestorePciAttributes (Dev);
+
+  return Status;
 }
 
 STATIC
-- 
2.27.0


From 2f8da920bbdb5f6d75ac8e65015890973f55495f Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:56 +0300
Subject: [PATCH 13/43] OvmfPkg/PvScsiDxe: Setup requests and completions rings

These rings are shared memory buffers between host and device in which
a cyclic buffer is managed to send request descriptors from host to
device and receive completion descriptors from device to host.

Note that because device may be constrained by IOMMU or guest may be run
under AMD SEV, we make sure to map these rings to device by using
PciIo->Map().

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-14-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit b654edec034a5e5dcc440f2f30fd2aa6c31aef3c)
---
 OvmfPkg/PvScsiDxe/PvScsi.c      | 219 ++++++++++++++++++++++++++++++++
 OvmfPkg/PvScsiDxe/PvScsi.h      |  17 +++
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf |   1 +
 3 files changed, 237 insertions(+)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index cf75884350ee..c7d367e83a2d 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -11,11 +11,13 @@
 
 #include <IndustryStandard/Pci.h>
 #include <IndustryStandard/PvScsi.h>
+#include <Library/BaseLib.h>
 #include <Library/BaseMemoryLib.h>
 #include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
+#include <Protocol/PciRootBridgeIo.h>
 #include <Uefi/UefiSpec.h>
 
 #include "PvScsi.h"
@@ -436,6 +438,207 @@ PvScsiRestorePciAttributes (
                 );
 }
 
+STATIC
+EFI_STATUS
+PvScsiAllocateSharedPages (
+  IN PVSCSI_DEV                     *Dev,
+  IN UINTN                          Pages,
+  OUT VOID                          **HostAddress,
+  OUT PVSCSI_DMA_DESC               *DmaDesc
+  )
+{
+  EFI_STATUS Status;
+  UINTN      NumberOfBytes;
+
+  Status = Dev->PciIo->AllocateBuffer (
+                         Dev->PciIo,
+                         AllocateAnyPages,
+                         EfiBootServicesData,
+                         Pages,
+                         HostAddress,
+                         EFI_PCI_ATTRIBUTE_MEMORY_CACHED
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);
+  Status = Dev->PciIo->Map (
+                         Dev->PciIo,
+                         EfiPciIoOperationBusMasterCommonBuffer,
+                         *HostAddress,
+                         &NumberOfBytes,
+                         &DmaDesc->DeviceAddress,
+                         &DmaDesc->Mapping
+                         );
+  if (EFI_ERROR (Status)) {
+    goto FreeBuffer;
+  }
+
+  if (NumberOfBytes != EFI_PAGES_TO_SIZE (Pages)) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Unmap;
+  }
+
+  return EFI_SUCCESS;
+
+Unmap:
+  Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping);
+
+FreeBuffer:
+  Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, *HostAddress);
+
+  return Status;
+}
+
+STATIC
+VOID
+PvScsiFreeSharedPages (
+  IN PVSCSI_DEV                     *Dev,
+  IN UINTN                          Pages,
+  IN VOID                           *HostAddress,
+  IN PVSCSI_DMA_DESC                *DmaDesc
+  )
+{
+  Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping);
+  Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, HostAddress);
+}
+
+STATIC
+EFI_STATUS
+PvScsiInitRings (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  EFI_STATUS Status;
+  union {
+    PVSCSI_CMD_DESC_SETUP_RINGS Cmd;
+    UINT32                      Uint32;
+  } AlignedCmd;
+  PVSCSI_CMD_DESC_SETUP_RINGS *Cmd;
+
+  Cmd = &AlignedCmd.Cmd;
+
+  Status = PvScsiAllocateSharedPages (
+             Dev,
+             1,
+             (VOID **)&Dev->RingDesc.RingState,
+             &Dev->RingDesc.RingStateDmaDesc
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  ZeroMem (Dev->RingDesc.RingState, EFI_PAGE_SIZE);
+
+  Status = PvScsiAllocateSharedPages (
+             Dev,
+             1,
+             (VOID **)&Dev->RingDesc.RingReqs,
+             &Dev->RingDesc.RingReqsDmaDesc
+             );
+  if (EFI_ERROR (Status)) {
+    goto FreeRingState;
+  }
+  ZeroMem (Dev->RingDesc.RingReqs, EFI_PAGE_SIZE);
+
+  Status = PvScsiAllocateSharedPages (
+             Dev,
+             1,
+             (VOID **)&Dev->RingDesc.RingCmps,
+             &Dev->RingDesc.RingCmpsDmaDesc
+             );
+  if (EFI_ERROR (Status)) {
+    goto FreeRingReqs;
+  }
+  ZeroMem (Dev->RingDesc.RingCmps, EFI_PAGE_SIZE);
+
+  ZeroMem (Cmd, sizeof (*Cmd));
+  Cmd->ReqRingNumPages = 1;
+  Cmd->CmpRingNumPages = 1;
+  Cmd->RingsStatePPN = RShiftU64 (
+                         Dev->RingDesc.RingStateDmaDesc.DeviceAddress,
+                         EFI_PAGE_SHIFT
+                         );
+  Cmd->ReqRingPPNs[0] = RShiftU64 (
+                          Dev->RingDesc.RingReqsDmaDesc.DeviceAddress,
+                          EFI_PAGE_SHIFT
+                          );
+  Cmd->CmpRingPPNs[0] = RShiftU64 (
+                          Dev->RingDesc.RingCmpsDmaDesc.DeviceAddress,
+                          EFI_PAGE_SHIFT
+                          );
+
+  STATIC_ASSERT (
+    sizeof (*Cmd) % sizeof (UINT32) == 0,
+    "Cmd must be multiple of 32-bit words"
+    );
+  Status = PvScsiWriteCmdDesc (
+             Dev,
+             PvScsiCmdSetupRings,
+             (UINT32 *)Cmd,
+             sizeof (*Cmd) / sizeof (UINT32)
+             );
+  if (EFI_ERROR (Status)) {
+    goto FreeRingCmps;
+  }
+
+  return EFI_SUCCESS;
+
+FreeRingCmps:
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingCmps,
+    &Dev->RingDesc.RingCmpsDmaDesc
+    );
+
+FreeRingReqs:
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingReqs,
+    &Dev->RingDesc.RingReqsDmaDesc
+    );
+
+FreeRingState:
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingState,
+    &Dev->RingDesc.RingStateDmaDesc
+    );
+
+  return Status;
+}
+
+STATIC
+VOID
+PvScsiFreeRings (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingCmps,
+    &Dev->RingDesc.RingCmpsDmaDesc
+    );
+
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingReqs,
+    &Dev->RingDesc.RingReqsDmaDesc
+    );
+
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingState,
+    &Dev->RingDesc.RingStateDmaDesc
+    );
+}
+
 STATIC
 EFI_STATUS
 PvScsiInit (
@@ -466,6 +669,14 @@ PvScsiInit (
     goto RestorePciAttributes;
   }
 
+  //
+  // Init PVSCSI rings
+  //
+  Status = PvScsiInitRings (Dev);
+  if (EFI_ERROR (Status)) {
+    goto RestorePciAttributes;
+  }
+
   //
   // Populate the exported interface's attributes
   //
@@ -509,6 +720,14 @@ PvScsiUninit (
   IN OUT PVSCSI_DEV *Dev
   )
 {
+  //
+  // Reset device to stop device usage of the rings.
+  // This is required to safely free the rings.
+  //
+  PvScsiResetAdapter (Dev);
+
+  PvScsiFreeRings (Dev);
+
   PvScsiRestorePciAttributes (Dev);
 }
 
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index 5f611dbbc98c..6d23b6e1eccf 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -15,12 +15,29 @@
 #include <Library/DebugLib.h>
 #include <Protocol/ScsiPassThruExt.h>
 
+typedef struct {
+  EFI_PHYSICAL_ADDRESS DeviceAddress;
+  VOID                 *Mapping;
+} PVSCSI_DMA_DESC;
+
+typedef struct {
+  PVSCSI_RINGS_STATE   *RingState;
+  PVSCSI_DMA_DESC      RingStateDmaDesc;
+
+  PVSCSI_RING_REQ_DESC *RingReqs;
+  PVSCSI_DMA_DESC      RingReqsDmaDesc;
+
+  PVSCSI_RING_CMP_DESC *RingCmps;
+  PVSCSI_DMA_DESC      RingCmpsDmaDesc;
+} PVSCSI_RING_DESC;
+
 #define PVSCSI_SIG SIGNATURE_32 ('P', 'S', 'C', 'S')
 
 typedef struct {
   UINT32                          Signature;
   EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT64                          OriginalPciAttributes;
+  PVSCSI_RING_DESC                RingDesc;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
index fcffc90d46c8..6200533698fc 100644
--- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -26,6 +26,7 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  BaseLib
   BaseMemoryLib
   DebugLib
   MemoryAllocationLib
-- 
2.27.0


From efad6e392e6efeaf4e2693036592da798b7232ce Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:57 +0300
Subject: [PATCH 14/43] OvmfPkg/PvScsiDxe: Introduce DMA communication buffer

In case device is constrained by IOMMU or guest is running under AMD SEV,
input/output buffers provided to device (DataBuffer and SenseData) needs
to be explicitly mapped to device by PciIo->Map().

To avoid the overhead of mapping/unmapping the DataBuffer and SenseData
to the device for every SCSI requst (and to simplify code), introduce a
single DMA communication buffer that will be mapped to device on
initialization. When a SCSI request needs to be sent to device, the
DataBuffer and SenseData will be copied from/to the DMA communication
buffer as required. This will be done by the following commits.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-15-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 6510e1979491d28c82944e0cfdab5713d482efa1)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 60 ++++++++++++++++++++++++++++++--------
 OvmfPkg/PvScsiDxe/PvScsi.h | 20 +++++++++++++
 2 files changed, 68 insertions(+), 12 deletions(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index c7d367e83a2d..6e350bb2d6e0 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -677,6 +677,19 @@ PvScsiInit (
     goto RestorePciAttributes;
   }
 
+  //
+  // Allocate DMA communication buffer
+  //
+  Status = PvScsiAllocateSharedPages (
+             Dev,
+             EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),
+             (VOID **)&Dev->DmaBuf,
+             &Dev->DmaBufDmaDesc
+             );
+  if (EFI_ERROR (Status)) {
+    goto FreeRings;
+  }
+
   //
   // Populate the exported interface's attributes
   //
@@ -708,18 +721,7 @@ PvScsiInit (
 
   return EFI_SUCCESS;
 
-RestorePciAttributes:
-  PvScsiRestorePciAttributes (Dev);
-
-  return Status;
-}
-
-STATIC
-VOID
-PvScsiUninit (
-  IN OUT PVSCSI_DEV *Dev
-  )
-{
+FreeRings:
   //
   // Reset device to stop device usage of the rings.
   // This is required to safely free the rings.
@@ -728,6 +730,40 @@ PvScsiUninit (
 
   PvScsiFreeRings (Dev);
 
+RestorePciAttributes:
+  PvScsiRestorePciAttributes (Dev);
+
+  return Status;
+}
+
+STATIC
+VOID
+PvScsiUninit (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  //
+  // Reset device to:
+  // - Make device stop processing all requests.
+  // - Stop device usage of the rings.
+  //
+  // This is required to safely free the DMA communication buffer
+  // and the rings.
+  //
+  PvScsiResetAdapter (Dev);
+
+  //
+  // Free DMA communication buffer
+  //
+  PvScsiFreeSharedPages (
+    Dev,
+    EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),
+    Dev->DmaBuf,
+    &Dev->DmaBufDmaDesc
+    );
+
+  PvScsiFreeRings (Dev);
+
   PvScsiRestorePciAttributes (Dev);
 }
 
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index 6d23b6e1eccf..fff12146dc75 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -31,6 +31,21 @@ typedef struct {
   PVSCSI_DMA_DESC      RingCmpsDmaDesc;
 } PVSCSI_RING_DESC;
 
+typedef struct {
+  //
+  // As EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.SenseDataLength is defined
+  // as UINT8, defining here SenseData size to MAX_UINT8 will guarantee it
+  // cannot overflow when passed to device.
+  //
+  UINT8     SenseData[MAX_UINT8];
+  //
+  // This size of the data is arbitrarily chosen.
+  // It seems to be sufficient for all I/O requests sent through
+  // EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() for common boot scenarios.
+  //
+  UINT8     Data[0x2000];
+} PVSCSI_DMA_BUFFER;
+
 #define PVSCSI_SIG SIGNATURE_32 ('P', 'S', 'C', 'S')
 
 typedef struct {
@@ -38,6 +53,8 @@ typedef struct {
   EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT64                          OriginalPciAttributes;
   PVSCSI_RING_DESC                RingDesc;
+  PVSCSI_DMA_BUFFER               *DmaBuf;
+  PVSCSI_DMA_DESC                 DmaBufDmaDesc;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
@@ -47,4 +64,7 @@ typedef struct {
 #define PVSCSI_FROM_PASS_THRU(PassThruPointer) \
   CR (PassThruPointer, PVSCSI_DEV, PassThru, PVSCSI_SIG)
 
+#define PVSCSI_DMA_BUF_DEV_ADDR(Dev, MemberName) \
+  (Dev->DmaBufDmaDesc.DeviceAddress + OFFSET_OF(PVSCSI_DMA_BUFFER, MemberName))
+
 #endif // __PVSCSI_DXE_H_
-- 
2.27.0


From 6791773eff3532209d31d57acb911da06780b362 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:58 +0300
Subject: [PATCH 15/43] OvmfPkg/PvScsiDxe: Support sending SCSI request and
 receive response

Implement EXT_SCSI_PASS_THRU.PassThru().

Machines should be able to boot after this commit.
Tested with Ubuntu 16.04 guest.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-16-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit c4c15b870239c2c23442dfc29c4421ac98d00ee0)
---
 OvmfPkg/OvmfPkg.dec             |   6 +
 OvmfPkg/PvScsiDxe/PvScsi.c      | 456 +++++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsi.h      |   1 +
 OvmfPkg/PvScsiDxe/PvScsiDxe.inf |   5 +-
 4 files changed, 465 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index b0893c3591ee..06aed2b70818 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -130,6 +130,12 @@ [PcdsFixedAtBuild]
   gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit|64|UINT8|0x36
   gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit|0|UINT8|0x37
 
+  ## After PvScsiDxe sends a SCSI request to the device, it waits for
+  #  the request completion in a polling loop.
+  #  This constant defines how many micro-seconds to wait between each
+  #  polling loop iteration.
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiWaitForCmpStallInUsecs|5|UINT32|0x38
+
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 6e350bb2d6e0..da3535c75220 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -32,6 +32,27 @@
 // Ext SCSI Pass Thru utilities
 //
 
+/**
+  Reads a 32-bit value into BAR0 using MMIO
+**/
+STATIC
+EFI_STATUS
+PvScsiMmioRead32 (
+  IN CONST PVSCSI_DEV   *Dev,
+  IN UINT64             Offset,
+  OUT UINT32            *Value
+  )
+{
+  return Dev->PciIo->Mem.Read (
+                           Dev->PciIo,
+                           EfiPciIoWidthUint32,
+                           PCI_BAR_IDX0,
+                           Offset,
+                           1,   // Count
+                           Value
+                           );
+}
+
 /**
   Writes a 32-bit value into BAR0 using MMIO
 **/
@@ -133,6 +154,383 @@ PvScsiResetAdapter (
   return PvScsiWriteCmdDesc (Dev, PvScsiCmdAdapterReset, NULL, 0);
 }
 
+/**
+  Returns if PVSCSI request ring is full
+**/
+STATIC
+BOOLEAN
+PvScsiIsReqRingFull (
+  IN CONST PVSCSI_DEV   *Dev
+  )
+{
+  PVSCSI_RINGS_STATE *RingsState;
+  UINT32             ReqNumEntries;
+
+  RingsState = Dev->RingDesc.RingState;
+  ReqNumEntries = 1U << RingsState->ReqNumEntriesLog2;
+  return (RingsState->ReqProdIdx - RingsState->CmpConsIdx) >= ReqNumEntries;
+}
+
+/**
+  Returns pointer to current request descriptor to produce
+**/
+STATIC
+PVSCSI_RING_REQ_DESC *
+PvScsiGetCurrentRequest (
+  IN CONST PVSCSI_DEV   *Dev
+  )
+{
+  PVSCSI_RINGS_STATE *RingState;
+  UINT32             ReqNumEntries;
+
+  RingState = Dev->RingDesc.RingState;
+  ReqNumEntries = 1U << RingState->ReqNumEntriesLog2;
+  return Dev->RingDesc.RingReqs +
+         (RingState->ReqProdIdx & (ReqNumEntries - 1));
+}
+
+/**
+  Returns pointer to current completion descriptor to consume
+**/
+STATIC
+PVSCSI_RING_CMP_DESC *
+PvScsiGetCurrentResponse (
+  IN CONST PVSCSI_DEV   *Dev
+  )
+{
+  PVSCSI_RINGS_STATE *RingState;
+  UINT32             CmpNumEntries;
+
+  RingState = Dev->RingDesc.RingState;
+  CmpNumEntries = 1U << RingState->CmpNumEntriesLog2;
+  return Dev->RingDesc.RingCmps +
+         (RingState->CmpConsIdx & (CmpNumEntries - 1));
+}
+
+/**
+  Wait for device to signal completion of submitted requests
+**/
+STATIC
+EFI_STATUS
+PvScsiWaitForRequestCompletion (
+  IN CONST PVSCSI_DEV   *Dev
+  )
+{
+  EFI_STATUS Status;
+  UINT32     IntrStatus;
+
+  //
+  // Note: We don't yet support Timeout according to
+  // EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.Timeout.
+  //
+  // This is consistent with some other Scsi PassThru drivers
+  // such as VirtioScsi.
+  //
+  for (;;) {
+    Status = PvScsiMmioRead32 (Dev, PvScsiRegOffsetIntrStatus, &IntrStatus);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // PVSCSI_INTR_CMPL_MASK is set if device completed submitted requests
+    //
+    if ((IntrStatus & PVSCSI_INTR_CMPL_MASK) != 0) {
+      break;
+    }
+
+    gBS->Stall (Dev->WaitForCmpStallInUsecs);
+  }
+
+  //
+  // Acknowledge PVSCSI_INTR_CMPL_MASK in device interrupt-status register
+  //
+  return PvScsiMmioWrite32 (
+           Dev,
+           PvScsiRegOffsetIntrStatus,
+           PVSCSI_INTR_CMPL_MASK
+           );
+}
+
+/**
+  Create a fake host adapter error
+**/
+STATIC
+EFI_STATUS
+ReportHostAdapterError (
+  OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  Packet->InTransferLength = 0;
+  Packet->OutTransferLength = 0;
+  Packet->SenseDataLength = 0;
+  Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+  Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+  return EFI_DEVICE_ERROR;
+}
+
+/**
+  Create a fake host adapter overrun error
+**/
+STATIC
+EFI_STATUS
+ReportHostAdapterOverrunError (
+  OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  Packet->SenseDataLength = 0;
+  Packet->HostAdapterStatus =
+            EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+  Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+  return EFI_BAD_BUFFER_SIZE;
+}
+
+/**
+  Populate a PVSCSI request descriptor from the Extended SCSI Pass Thru
+  Protocol packet.
+**/
+STATIC
+EFI_STATUS
+PopulateRequest (
+  IN CONST PVSCSI_DEV                               *Dev,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+  OUT PVSCSI_RING_REQ_DESC                          *Request
+  )
+{
+  UINT8 TargetValue;
+
+  //
+  // We only use first byte of target identifer
+  //
+  TargetValue = *Target;
+
+  //
+  // Check for unsupported requests
+  //
+  if (
+      //
+      // Bidirectional transfer was requested
+      //
+      (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) ||
+      (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||
+      //
+      // Command Descriptor Block bigger than this constant should be considered
+      // out-of-band. We currently don't support these CDBs.
+      //
+      (Packet->CdbLength > PVSCSI_CDB_MAX_SIZE)
+      ) {
+
+    //
+    // This error code doesn't require updates to the Packet output fields
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check for invalid parameters
+  //
+  if (
+      //
+      // Addressed invalid device
+      //
+      (TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun) ||
+      //
+      // Invalid direction (there doesn't seem to be a macro for the "no data
+      // transferred" "direction", eg. for TEST UNIT READY)
+      //
+      (Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||
+      //
+      // Trying to receive, but destination pointer is NULL, or contradicting
+      // transfer direction
+      //
+      ((Packet->InTransferLength > 0) &&
+       ((Packet->InDataBuffer == NULL) ||
+        (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE)
+        )
+       ) ||
+      //
+      // Trying to send, but source pointer is NULL, or contradicting
+      // transfer direction
+      //
+      ((Packet->OutTransferLength > 0) &&
+       ((Packet->OutDataBuffer == NULL) ||
+        (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ)
+        )
+       )
+      ) {
+
+    //
+    // This error code doesn't require updates to the Packet output fields
+    //
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check for input/output buffer too large for DMA communication buffer
+  //
+  if (Packet->InTransferLength > sizeof (Dev->DmaBuf->Data)) {
+    Packet->InTransferLength = sizeof (Dev->DmaBuf->Data);
+    return ReportHostAdapterOverrunError (Packet);
+  }
+  if (Packet->OutTransferLength > sizeof (Dev->DmaBuf->Data)) {
+    Packet->OutTransferLength = sizeof (Dev->DmaBuf->Data);
+    return ReportHostAdapterOverrunError (Packet);
+  }
+
+  //
+  // Encode PVSCSI request
+  //
+  ZeroMem (Request, sizeof (*Request));
+
+  Request->Bus = 0;
+  Request->Target = TargetValue;
+  //
+  // This cast is safe as PVSCSI_DEV.MaxLun is defined as UINT8
+  //
+  Request->Lun[1] = (UINT8)Lun;
+  Request->SenseLen = Packet->SenseDataLength;
+  //
+  // DMA communication buffer SenseData overflow is not possible
+  // due to Packet->SenseDataLength defined as UINT8
+  //
+  Request->SenseAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, SenseData);
+  Request->CdbLen = Packet->CdbLength;
+  CopyMem (Request->Cdb, Packet->Cdb, Packet->CdbLength);
+  Request->VcpuHint = 0;
+  Request->Tag = PVSCSI_SIMPLE_QUEUE_TAG;
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+    Request->Flags = PVSCSI_FLAG_CMD_DIR_TOHOST;
+    Request->DataLen = Packet->InTransferLength;
+  } else {
+    Request->Flags = PVSCSI_FLAG_CMD_DIR_TODEVICE;
+    Request->DataLen = Packet->OutTransferLength;
+    CopyMem (
+      Dev->DmaBuf->Data,
+      Packet->OutDataBuffer,
+      Packet->OutTransferLength
+      );
+  }
+  Request->DataAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, Data);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Handle the PVSCSI device response:
+  - Copy returned data from DMA communication buffer.
+  - Update fields in Extended SCSI Pass Thru Protocol packet as required.
+  - Translate response code to EFI status code and host adapter status.
+**/
+STATIC
+EFI_STATUS
+HandleResponse (
+  IN PVSCSI_DEV                                     *Dev,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+  IN CONST PVSCSI_RING_CMP_DESC                     *Response
+  )
+{
+  //
+  // Fix SenseDataLength to amount of data returned
+  //
+  if (Packet->SenseDataLength > Response->SenseLen) {
+    Packet->SenseDataLength = (UINT8)Response->SenseLen;
+  }
+  //
+  // Copy sense data from DMA communication buffer
+  //
+  CopyMem (
+    Packet->SenseData,
+    Dev->DmaBuf->SenseData,
+    Packet->SenseDataLength
+    );
+
+  //
+  // Copy device output from DMA communication buffer
+  //
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+    CopyMem (Packet->InDataBuffer, Dev->DmaBuf->Data, Packet->InTransferLength);
+  }
+
+  //
+  // Report target status
+  //
+  Packet->TargetStatus = Response->ScsiStatus;
+
+  //
+  // Host adapter status and function return value depend on
+  // device response's host status
+  //
+  switch (Response->HostStatus) {
+    case PvScsiBtStatSuccess:
+    case PvScsiBtStatLinkedCommandCompleted:
+    case PvScsiBtStatLinkedCommandCompletedWithFlag:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+      return EFI_SUCCESS;
+
+    case PvScsiBtStatDataUnderrun:
+      //
+      // Report transferred amount in underrun
+      //
+      if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+        Packet->InTransferLength = (UINT32)Response->DataLen;
+      } else {
+        Packet->OutTransferLength = (UINT32)Response->DataLen;
+      }
+      Packet->HostAdapterStatus =
+                EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+      return EFI_SUCCESS;
+
+    case PvScsiBtStatDatarun:
+      Packet->HostAdapterStatus =
+                EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+      return EFI_SUCCESS;
+
+    case PvScsiBtStatSelTimeout:
+      Packet->HostAdapterStatus =
+                EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
+      return EFI_TIMEOUT;
+
+    case PvScsiBtStatBusFree:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE;
+      break;
+
+    case PvScsiBtStatInvPhase:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;
+      break;
+
+    case PvScsiBtStatSensFailed:
+      Packet->HostAdapterStatus =
+                EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED;
+      break;
+
+    case PvScsiBtStatTagReject:
+    case PvScsiBtStatBadMsg:
+      Packet->HostAdapterStatus =
+          EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT;
+      break;
+
+    case PvScsiBtStatBusReset:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;
+      break;
+
+    case PvScsiBtStatHaTimeout:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;
+      return EFI_TIMEOUT;
+
+    case PvScsiBtStatScsiParity:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR;
+      break;
+
+    default:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+      break;
+  }
+
+  return EFI_DEVICE_ERROR;
+}
+
 /**
   Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
   EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
@@ -168,7 +566,62 @@ PvScsiPassThru (
   IN EFI_EVENT                                      Event    OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  PVSCSI_DEV            *Dev;
+  EFI_STATUS            Status;
+  PVSCSI_RING_REQ_DESC *Request;
+  PVSCSI_RING_CMP_DESC *Response;
+
+  Dev = PVSCSI_FROM_PASS_THRU (This);
+
+  if (PvScsiIsReqRingFull (Dev)) {
+    return EFI_NOT_READY;
+  }
+
+  Request = PvScsiGetCurrentRequest (Dev);
+
+  Status = PopulateRequest (Dev, Target, Lun, Packet, Request);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Writes to Request must be globally visible before making request
+  // available to device
+  //
+  MemoryFence ();
+  Dev->RingDesc.RingState->ReqProdIdx++;
+
+  Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetKickRwIo, 0);
+  if (EFI_ERROR (Status)) {
+    //
+    // If kicking the host fails, we must fake a host adapter error.
+    // EFI_NOT_READY would save us the effort, but it would also suggest that
+    // the caller retry.
+    //
+    return ReportHostAdapterError (Packet);
+  }
+
+  Status = PvScsiWaitForRequestCompletion (Dev);
+  if (EFI_ERROR (Status)) {
+    //
+    // If waiting for request completion fails, we must fake a host adapter
+    // error. EFI_NOT_READY would save us the effort, but it would also suggest
+    // that the caller retry.
+    //
+    return ReportHostAdapterError (Packet);
+  }
+
+  Response = PvScsiGetCurrentResponse (Dev);
+  Status = HandleResponse (Dev, Packet, Response);
+
+  //
+  // Reads from response must complete before releasing completion entry
+  // to device
+  //
+  MemoryFence ();
+  Dev->RingDesc.RingState->CmpConsIdx++;
+
+  return Status;
 }
 
 STATIC
@@ -652,6 +1105,7 @@ PvScsiInit (
   //
   Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);
   Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);
+  Dev->WaitForCmpStallInUsecs = PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs);
 
   //
   // Set PCI Attributes
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index fff12146dc75..02feac734743 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -57,6 +57,7 @@ typedef struct {
   PVSCSI_DMA_DESC                 DmaBufDmaDesc;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
+  UINTN                           WaitForCmpStallInUsecs;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
 } PVSCSI_DEV;
diff --git a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
index 6200533698fc..284035fb10d4 100644
--- a/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+++ b/OvmfPkg/PvScsiDxe/PvScsiDxe.inf
@@ -39,5 +39,6 @@ [Protocols]
   gEfiPciIoProtocolGuid             ## TO_START
 
 [Pcd]
-  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit       ## CONSUMES
-  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit    ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxLunLimit               ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiMaxTargetLimit            ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiWaitForCmpStallInUsecs    ## CONSUMES
-- 
2.27.0


From 61837682b71ac2da6dc87b542df983a44eb3b89a Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:00:59 +0300
Subject: [PATCH 16/43] OvmfPkg/PvScsiDxe: Reset device on ExitBootServices()

This causes the device to forget about the request/completion rings.
We allocated said rings in EfiBootServicesData type memory, and code
executing after ExitBootServices() is permitted to overwrite it.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-17-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit 7d8a04e9d2fd8a5298636d5a0c702a978b5f22f1)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 43 +++++++++++++++++++++++++++++++++++++-
 OvmfPkg/PvScsiDxe/PvScsi.h |  1 +
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index da3535c75220..d7f0d3c8790c 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -1221,6 +1221,31 @@ PvScsiUninit (
   PvScsiRestorePciAttributes (Dev);
 }
 
+/**
+  Event notification called by ExitBootServices()
+**/
+STATIC
+VOID
+EFIAPI
+PvScsiExitBoot (
+  IN  EFI_EVENT Event,
+  IN  VOID      *Context
+  )
+{
+  PVSCSI_DEV *Dev;
+
+  Dev = Context;
+  DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
+
+  //
+  // Reset the device to stop device usage of the rings.
+  //
+  // We allocated said rings in EfiBootServicesData type memory, and code
+  // executing after ExitBootServices() is permitted to overwrite it.
+  //
+  PvScsiResetAdapter (Dev);
+}
+
 //
 // Driver Binding
 //
@@ -1314,6 +1339,17 @@ PvScsiDriverBindingStart (
     goto ClosePciIo;
   }
 
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                  TPL_CALLBACK,
+                  &PvScsiExitBoot,
+                  Dev,
+                  &Dev->ExitBoot
+                  );
+  if (EFI_ERROR (Status)) {
+    goto UninitDev;
+  }
+
   //
   // Setup complete, attempt to export the driver instance's PassThru interface
   //
@@ -1325,11 +1361,14 @@ PvScsiDriverBindingStart (
                   &Dev->PassThru
                   );
   if (EFI_ERROR (Status)) {
-    goto UninitDev;
+    goto CloseExitBoot;
   }
 
   return EFI_SUCCESS;
 
+CloseExitBoot:
+  gBS->CloseEvent (Dev->ExitBoot);
+
 UninitDev:
   PvScsiUninit (Dev);
 
@@ -1384,6 +1423,8 @@ PvScsiDriverBindingStop (
     return Status;
   }
 
+  gBS->CloseEvent (Dev->ExitBoot);
+
   PvScsiUninit (Dev);
 
   gBS->CloseProtocol (
diff --git a/OvmfPkg/PvScsiDxe/PvScsi.h b/OvmfPkg/PvScsiDxe/PvScsi.h
index 02feac734743..544359ebc05c 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.h
+++ b/OvmfPkg/PvScsiDxe/PvScsi.h
@@ -51,6 +51,7 @@ typedef struct {
 typedef struct {
   UINT32                          Signature;
   EFI_PCI_IO_PROTOCOL             *PciIo;
+  EFI_EVENT                       ExitBoot;
   UINT64                          OriginalPciAttributes;
   PVSCSI_RING_DESC                RingDesc;
   PVSCSI_DMA_BUFFER               *DmaBuf;
-- 
2.27.0


From 2bfb72a44735e621e765b7b44a74a4bbf7baab82 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Sat, 28 Mar 2020 23:01:00 +0300
Subject: [PATCH 17/43] OvmfPkg/PvScsiDxe: Enable device 64-bit DMA addresses

Enable PCI dual-address cycle attribute to signal device
supports 64-bit DMA addresses.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2567
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200328200100.60786-18-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
(cherry picked from commit f34c7645bd87c61f72c2bc4a8f88afabc69de512)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index d7f0d3c8790c..0a66c98421a9 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -874,6 +874,29 @@ PvScsiSetPciAttributes (
     return Status;
   }
 
+  //
+  // Signal device supports 64-bit DMA addresses
+  //
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationEnable,
+                         EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
+                         NULL
+                         );
+  if (EFI_ERROR (Status)) {
+    //
+    // Warn user that device will only be using 32-bit DMA addresses.
+    //
+    // Note that this does not prevent the device/driver from working
+    // and therefore we only warn and continue as usual.
+    //
+    DEBUG ((
+      DEBUG_WARN,
+      "%a: failed to enable 64-bit DMA addresses\n",
+      __FUNCTION__
+      ));
+  }
+
   return EFI_SUCCESS;
 }
 
-- 
2.27.0


From b7591ff579fba871940e823a18ce96db7836563d Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Tue, 31 Mar 2020 14:02:44 +0300
Subject: [PATCH 18/43] Maintainers.txt: Add Liran and Nikita as
 OvmfPkg/PvScsiDxe reviewers

Laszlo suggested that as I have contributed the OvmfPkg PVSCSI driver, I
will also register myself as a reviewer in Maintainers.txt.

In addition, as Nikita have assisted the development of the PVSCSI
driver and have developed another similar OvmfPkg SCSI driver, add him
as a reviewer to PVSCSI driver as-well.

Cc: Nikita Leshenko <nikita.leshchenko@oracle.com>
Suggested-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200331110244.51409-1-liran.alon@oracle.com>
Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
[lersek@redhat.com: fixup "F:" pattern by appending slash character]
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 335644f90f15fac8bfd5575f937fa65af4978a08)
---
 Maintainers.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Maintainers.txt b/Maintainers.txt
index 180ad35d0976..a75d5e1f916f 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -435,6 +435,11 @@ OvmfPkg: CSM modules
 F: OvmfPkg/Csm/
 R: David Woodhouse <dwmw2@infradead.org>
 
+OvmfPkg: PVSCSI driver
+F: OvmfPkg/PvScsiDxe/
+R: Liran Alon <liran.alon@oracle.com>
+R: Nikita Leshenko <nikita.leshchenko@oracle.com>
+
 PcAtChipsetPkg
 F: PcAtChipsetPkg/
 W: https://github.com/tianocore/tianocore.github.io/wiki/PcAtChipsetPkg
-- 
2.27.0


From 61e3d52d532985e1fc13125a79c61fdc5ef16cec Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Tue, 31 Mar 2020 14:04:52 +0300
Subject: [PATCH 19/43] OvmfPkg/PvScsiDxe: Fix VS2019 build error because of
 implicit cast

Sean reported that VS2019 build produce the following build error:
INFO - PvScsi.c
INFO - Generating code
INFO - d:\a\1\s\OvmfPkg\PvScsiDxe\PvScsi.c(459): error C2220: the
       following warning is treated as an error
INFO - d:\a\1\s\OvmfPkg\PvScsiDxe\PvScsi.c(459): warning C4244: '=':
       conversion from 'const UINT16' to 'UINT8', possible loss of data

This result from an implicit cast from PVSCSI Response->ScsiStatus
(Which is UINT16) to Packet->TargetResponse (Which is UINT8).

Fix this issue by adding an appropriate explicit cast and verify with
assert that this truncation do not result in loss of data.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2651
Reported-by: Sean Brogan <sean.brogan@microsoft.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200331110452.51992-1-liran.alon@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
[lersek@redhat.com: rewrap VS2019 diags in commit msg for PatchCheck.py]
(cherry picked from commit 98936dc4f44b4ef47e7221d435de06a0813aa00a)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 0a66c98421a9..1ca50390c0e5 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -455,8 +455,12 @@ HandleResponse (
 
   //
   // Report target status
+  // (Strangely, PVSCSI interface defines Response->ScsiStatus as UINT16.
+  // But it should de-facto always have a value that fits UINT8. To avoid
+  // unexpected behavior, verify value is in UINT8 bounds before casting)
   //
-  Packet->TargetStatus = Response->ScsiStatus;
+  ASSERT (Response->ScsiStatus <= MAX_UINT8);
+  Packet->TargetStatus = (UINT8)Response->ScsiStatus;
 
   //
   // Host adapter status and function return value depend on
-- 
2.27.0


From edc25314cbe771c4f151c2dd76596c50000fa0e7 Mon Sep 17 00:00:00 2001
From: Liran Alon <liran.alon@oracle.com>
Date: Wed, 1 Apr 2020 01:56:37 +0300
Subject: [PATCH 20/43] OvmfPkg/PvScsiDxe: Refactor setup of rings to separate
 function

Previous to this change, PvScsiFreeRings() was not undoing all
operations that was done by PvScsiInitRings().
This is because PvScsiInitRings() was both preparing rings (Allocate
memory and map it for device DMA) and setup the rings against device by
issueing a device command. While PvScsiFreeRings() only unmaps the rings
and free their memory.

Driver do not have a functional error as it makes sure to reset device
before every call site to PvScsiFreeRings(). However, this is not
intuitive.

Therefore, prefer to refactor the setup of the ring against device to a
separate function than PvScsiInitRings().

Signed-off-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200331225637.123318-1-liran.alon@oracle.com>
[lersek@redhat.com: rename FreeDMACommBuffer label to FreeDmaCommBuffer]
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit e210fc130e5c9738909dca432bbf8bf277ba6e37)
---
 OvmfPkg/PvScsiDxe/PvScsi.c | 163 +++++++++++++++++++------------------
 1 file changed, 85 insertions(+), 78 deletions(-)

diff --git a/OvmfPkg/PvScsiDxe/PvScsi.c b/OvmfPkg/PvScsiDxe/PvScsi.c
index 1ca50390c0e5..843534ebf711 100644
--- a/OvmfPkg/PvScsiDxe/PvScsi.c
+++ b/OvmfPkg/PvScsiDxe/PvScsi.c
@@ -991,13 +991,6 @@ PvScsiInitRings (
   )
 {
   EFI_STATUS Status;
-  union {
-    PVSCSI_CMD_DESC_SETUP_RINGS Cmd;
-    UINT32                      Uint32;
-  } AlignedCmd;
-  PVSCSI_CMD_DESC_SETUP_RINGS *Cmd;
-
-  Cmd = &AlignedCmd.Cmd;
 
   Status = PvScsiAllocateSharedPages (
              Dev,
@@ -1032,6 +1025,69 @@ PvScsiInitRings (
   }
   ZeroMem (Dev->RingDesc.RingCmps, EFI_PAGE_SIZE);
 
+  return EFI_SUCCESS;
+
+FreeRingReqs:
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingReqs,
+    &Dev->RingDesc.RingReqsDmaDesc
+    );
+
+FreeRingState:
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingState,
+    &Dev->RingDesc.RingStateDmaDesc
+    );
+
+  return Status;
+}
+
+STATIC
+VOID
+PvScsiFreeRings (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingCmps,
+    &Dev->RingDesc.RingCmpsDmaDesc
+    );
+
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingReqs,
+    &Dev->RingDesc.RingReqsDmaDesc
+    );
+
+  PvScsiFreeSharedPages (
+    Dev,
+    1,
+    Dev->RingDesc.RingState,
+    &Dev->RingDesc.RingStateDmaDesc
+    );
+}
+
+STATIC
+EFI_STATUS
+PvScsiSetupRings (
+  IN OUT PVSCSI_DEV *Dev
+  )
+{
+  union {
+    PVSCSI_CMD_DESC_SETUP_RINGS Cmd;
+    UINT32                      Uint32;
+  } AlignedCmd;
+  PVSCSI_CMD_DESC_SETUP_RINGS *Cmd;
+
+  Cmd = &AlignedCmd.Cmd;
+
   ZeroMem (Cmd, sizeof (*Cmd));
   Cmd->ReqRingNumPages = 1;
   Cmd->CmpRingNumPages = 1;
@@ -1052,71 +1108,12 @@ PvScsiInitRings (
     sizeof (*Cmd) % sizeof (UINT32) == 0,
     "Cmd must be multiple of 32-bit words"
     );
-  Status = PvScsiWriteCmdDesc (
-             Dev,
-             PvScsiCmdSetupRings,
-             (UINT32 *)Cmd,
-             sizeof (*Cmd) / sizeof (UINT32)
-             );
-  if (EFI_ERROR (Status)) {
-    goto FreeRingCmps;
-  }
-
-  return EFI_SUCCESS;
-
-FreeRingCmps:
-  PvScsiFreeSharedPages (
-    Dev,
-    1,
-    Dev->RingDesc.RingCmps,
-    &Dev->RingDesc.RingCmpsDmaDesc
-    );
-
-FreeRingReqs:
-  PvScsiFreeSharedPages (
-    Dev,
-    1,
-    Dev->RingDesc.RingReqs,
-    &Dev->RingDesc.RingReqsDmaDesc
-    );
-
-FreeRingState:
-  PvScsiFreeSharedPages (
-    Dev,
-    1,
-    Dev->RingDesc.RingState,
-    &Dev->RingDesc.RingStateDmaDesc
-    );
-
-  return Status;
-}
-
-STATIC
-VOID
-PvScsiFreeRings (
-  IN OUT PVSCSI_DEV *Dev
-  )
-{
-  PvScsiFreeSharedPages (
-    Dev,
-    1,
-    Dev->RingDesc.RingCmps,
-    &Dev->RingDesc.RingCmpsDmaDesc
-    );
-
-  PvScsiFreeSharedPages (
-    Dev,
-    1,
-    Dev->RingDesc.RingReqs,
-    &Dev->RingDesc.RingReqsDmaDesc
-    );
-
-  PvScsiFreeSharedPages (
-    Dev,
-    1,
-    Dev->RingDesc.RingState,
-    &Dev->RingDesc.RingStateDmaDesc
-    );
+  return PvScsiWriteCmdDesc (
+           Dev,
+           PvScsiCmdSetupRings,
+           (UINT32 *)Cmd,
+           sizeof (*Cmd) / sizeof (UINT32)
+           );
 }
 
 STATIC
@@ -1171,6 +1168,14 @@ PvScsiInit (
     goto FreeRings;
   }
 
+  //
+  // Setup rings against device
+  //
+  Status = PvScsiSetupRings (Dev);
+  if (EFI_ERROR (Status)) {
+    goto FreeDmaCommBuffer;
+  }
+
   //
   // Populate the exported interface's attributes
   //
@@ -1202,13 +1207,15 @@ PvScsiInit (
 
   return EFI_SUCCESS;
 
+FreeDmaCommBuffer:
+  PvScsiFreeSharedPages (
+    Dev,
+    EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),
+    Dev->DmaBuf,
+    &Dev->DmaBufDmaDesc
+    );
+
 FreeRings:
-  //
-  // Reset device to stop device usage of the rings.
-  // This is required to safely free the rings.
-  //
-  PvScsiResetAdapter (Dev);
-
   PvScsiFreeRings (Dev);
 
 RestorePciAttributes:
-- 
2.27.0


From db0155248bcda9b6b77767eedba8912c7242d769 Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:05:56 +0300
Subject: [PATCH 21/43] OvmfPkg/MptScsiDxe: Create empty driver

In preparation for implementing LSI Fusion MPT SCSI devices, create a
basic scaffolding for a driver.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-2-nikita.leshchenko@oracle.com>
(cherry picked from commit feec20b28dd0291b1a5d951e856d642659d23f7e)
---
 Maintainers.txt                   |  3 ++-
 OvmfPkg/MptScsiDxe/MptScsi.c      | 26 ++++++++++++++++++++++++++
 OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 26 ++++++++++++++++++++++++++
 OvmfPkg/OvmfPkgIa32.dsc           |  4 ++++
 OvmfPkg/OvmfPkgIa32.fdf           |  3 +++
 OvmfPkg/OvmfPkgIa32X64.dsc        |  4 ++++
 OvmfPkg/OvmfPkgIa32X64.fdf        |  3 +++
 OvmfPkg/OvmfPkgX64.dsc            |  4 ++++
 OvmfPkg/OvmfPkgX64.fdf            |  3 +++
 9 files changed, 75 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/MptScsiDxe/MptScsi.c
 create mode 100644 OvmfPkg/MptScsiDxe/MptScsiDxe.inf

diff --git a/Maintainers.txt b/Maintainers.txt
index a75d5e1f916f..3b0ec2b8bea6 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -435,7 +435,8 @@ OvmfPkg: CSM modules
 F: OvmfPkg/Csm/
 R: David Woodhouse <dwmw2@infradead.org>
 
-OvmfPkg: PVSCSI driver
+OvmfPkg: MptScsi and PVSCSI driver
+F: OvmfPkg/MptScsiDxe/
 F: OvmfPkg/PvScsiDxe/
 R: Liran Alon <liran.alon@oracle.com>
 R: Nikita Leshenko <nikita.leshchenko@oracle.com>
diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
new file mode 100644
index 000000000000..c6c8142dfde6
--- /dev/null
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -0,0 +1,26 @@
+/** @file
+
+  This driver produces Extended SCSI Pass Thru Protocol instances for
+  LSI Fusion MPT SCSI devices.
+
+  Copyright (C) 2020, Oracle and/or its affiliates.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiSpec.h>
+
+//
+// Entry Point
+//
+
+EFI_STATUS
+EFIAPI
+MptScsiEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
new file mode 100644
index 000000000000..b4006a7c2d97
--- /dev/null
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -0,0 +1,26 @@
+## @file
+# This driver produces Extended SCSI Pass Thru Protocol instances for
+# LSI Fusion MPT SCSI devices.
+#
+# Copyright (C) 2020, Oracle and/or its affiliates.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = MptScsiDxe
+  FILE_GUID                      = 2B3DB5DD-B315-4961-8454-0AFF3C811B19
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = MptScsiEntryPoint
+
+[Sources]
+  MptScsi.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 82a9444bb92b..d391ee0a28df 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -48,6 +48,7 @@ [Defines]
   # Device drivers
   #
   DEFINE PVSCSI_ENABLE           = TRUE
+  DEFINE MPT_SCSI_ENABLE         = TRUE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -718,6 +719,9 @@ [Components]
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
 !if $(PVSCSI_ENABLE) == TRUE
   OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
+!if $(MPT_SCSI_ENABLE) == TRUE
+  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
 !endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index d6b9c74e6cfe..b47d50276d13 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -230,6 +230,9 @@ [FV.DXEFV]
 !if $(PVSCSI_ENABLE) == TRUE
 INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
 !endif
+!if $(MPT_SCSI_ENABLE) == TRUE
+INF  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index d34a51e86ba1..2341d7f53a1c 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -48,6 +48,7 @@ [Defines]
   # Device drivers
   #
   DEFINE PVSCSI_ENABLE           = TRUE
+  DEFINE MPT_SCSI_ENABLE         = TRUE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -731,6 +732,9 @@ [Components.X64]
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
 !if $(PVSCSI_ENABLE) == TRUE
   OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
+!if $(MPT_SCSI_ENABLE) == TRUE
+  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
 !endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index bce1bdb42d71..af0f4c958841 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -231,6 +231,9 @@ [FV.DXEFV]
 !if $(PVSCSI_ENABLE) == TRUE
 INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
 !endif
+!if $(MPT_SCSI_ENABLE) == TRUE
+INF  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 55d975c4d3f9..0036455d9c62 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -48,6 +48,7 @@ [Defines]
   # Device drivers
   #
   DEFINE PVSCSI_ENABLE           = TRUE
+  DEFINE MPT_SCSI_ENABLE         = TRUE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -729,6 +730,9 @@ [Components]
   OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf
 !if $(PVSCSI_ENABLE) == TRUE
   OvmfPkg/PvScsiDxe/PvScsiDxe.inf
+!endif
+!if $(MPT_SCSI_ENABLE) == TRUE
+  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
 !endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index bce1bdb42d71..af0f4c958841 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -231,6 +231,9 @@ [FV.DXEFV]
 !if $(PVSCSI_ENABLE) == TRUE
 INF  OvmfPkg/PvScsiDxe/PvScsiDxe.inf
 !endif
+!if $(MPT_SCSI_ENABLE) == TRUE
+INF  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
-- 
2.27.0


From 206fa42f38976013017b428632d761da5ee39297 Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:05:57 +0300
Subject: [PATCH 22/43] OvmfPkg/MptScsiDxe: Install DriverBinding Protocol

In order to probe and connect to the MptScsi device we need this
protocol. Currently it does nothing.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200504210607.144434-3-nikita.leshchenko@oracle.com>
(cherry picked from commit ad8f2d6b073e5c3a9612c6ca78174f8e4890c98d)
---
 OvmfPkg/MptScsiDxe/MptScsi.c      | 67 ++++++++++++++++++++++++++++++-
 OvmfPkg/MptScsiDxe/MptScsiDxe.inf |  1 +
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index c6c8142dfde6..581d3909b84d 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -9,8 +9,66 @@
 
 **/
 
+#include <Library/UefiLib.h>
 #include <Uefi/UefiSpec.h>
 
+//
+// Higher versions will be used before lower, 0x10-0xffffffef is the version
+// range for IVH (Indie Hardware Vendors)
+//
+#define MPT_SCSI_BINDING_VERSION 0x10
+
+//
+// Driver Binding
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiControllerSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL            *This,
+  IN EFI_HANDLE                             ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL               *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiControllerStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL            *This,
+  IN EFI_HANDLE                             ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL               *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiControllerStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL            *This,
+  IN  EFI_HANDLE                            ControllerHandle,
+  IN  UINTN                                 NumberOfChildren,
+  IN  EFI_HANDLE                            *ChildHandleBuffer
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = {
+  &MptScsiControllerSupported,
+  &MptScsiControllerStart,
+  &MptScsiControllerStop,
+  MPT_SCSI_BINDING_VERSION,
+  NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2
+  NULL, // DriverBindingHandle, filled as well
+};
+
 //
 // Entry Point
 //
@@ -22,5 +80,12 @@ MptScsiEntryPoint (
   IN EFI_SYSTEM_TABLE *SystemTable
   )
 {
-  return EFI_UNSUPPORTED;
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &mMptScsiDriverBinding,
+           ImageHandle, // The handle to install onto
+           NULL, // TODO Component name
+           NULL // TODO Component name
+           );
 }
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
index b4006a7c2d97..53585068684f 100644
--- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -24,3 +24,4 @@ [Packages]
 
 [LibraryClasses]
   UefiDriverEntryPoint
+  UefiLib
-- 
2.27.0


From 629cce5c452a436642699c67b83e8e64a993ffec Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:05:58 +0300
Subject: [PATCH 23/43] OvmfPkg/MptScsiDxe: Report name of driver

Install Component Name protocols to have a nice display name for the
driver in places such as UEFI shell.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jaben Carsey <jaben.carsey@intel.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200504210607.144434-4-nikita.leshchenko@oracle.com>
(cherry picked from commit be7fcaa1c9e952aaacdcbe806933ce70a391aa78)
---
 OvmfPkg/MptScsiDxe/MptScsi.c | 61 ++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 581d3909b84d..64949a809022 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -69,6 +69,63 @@ EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = {
   NULL, // DriverBindingHandle, filled as well
 };
 
+//
+// Component Name
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+  { "eng;en", L"LSI Fusion MPT SCSI Driver" },
+  { NULL,     NULL                   }
+};
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL mComponentName;
+
+EFI_STATUS
+EFIAPI
+MptScsiGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &mComponentName) // Iso639Language
+           );
+}
+
+EFI_STATUS
+EFIAPI
+MptScsiGetDeviceName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  DeviceHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
+  &MptScsiGetDriverName,
+  &MptScsiGetDeviceName,
+  "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+STATIC
+EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &MptScsiGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &MptScsiGetDeviceName,
+  "en" // SupportedLanguages, RFC 4646 language codes
+};
+
 //
 // Entry Point
 //
@@ -85,7 +142,7 @@ MptScsiEntryPoint (
            SystemTable,
            &mMptScsiDriverBinding,
            ImageHandle, // The handle to install onto
-           NULL, // TODO Component name
-           NULL // TODO Component name
+           &mComponentName,
+           &mComponentName2
            );
 }
-- 
2.27.0


From ead01d91cb8aeaab6a8eb6a535d2c291b4343435 Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:05:59 +0300
Subject: [PATCH 24/43] OvmfPkg/MptScsiDxe: Probe PCI devices and look for
 MptScsi

The MptScsiControllerSupported function is called on handles passed in
by the ConnectController() boot service and if the handle is the
lsi53c1030 controller the function would return success. A successful
return value will attach our driver to the device.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Message-Id: <20200504210607.144434-5-nikita.leshchenko@oracle.com>
(cherry picked from commit f47074425d2ecdd03cb651cc218b88226ad4a0fb)
---
 .../Include/IndustryStandard/FusionMptScsi.h  | 23 +++++++++
 OvmfPkg/MptScsiDxe/MptScsi.c                  | 49 ++++++++++++++++++-
 OvmfPkg/MptScsiDxe/MptScsiDxe.inf             |  5 ++
 3 files changed, 76 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/Include/IndustryStandard/FusionMptScsi.h

diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
new file mode 100644
index 000000000000..df9bdc2f0348
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
@@ -0,0 +1,23 @@
+/** @file
+
+  Macros and type definitions for LSI Fusion MPT SCSI devices.
+
+  Copyright (C) 2020, Oracle and/or its affiliates.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __FUSION_MPT_SCSI_H__
+#define __FUSION_MPT_SCSI_H__
+
+//
+// Device offsets and constants
+//
+
+#define LSI_LOGIC_PCI_VENDOR_ID 0x1000
+#define LSI_53C1030_PCI_DEVICE_ID 0x0030
+#define LSI_SAS1068_PCI_DEVICE_ID 0x0054
+#define LSI_SAS1068E_PCI_DEVICE_ID 0x0058
+
+#endif // __FUSION_MPT_SCSI_H__
diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 64949a809022..4e2f8f2296fb 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -9,7 +9,11 @@
 
 **/
 
+#include <IndustryStandard/FusionMptScsi.h>
+#include <IndustryStandard/Pci.h>
+#include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
+#include <Protocol/PciIo.h>
 #include <Uefi/UefiSpec.h>
 
 //
@@ -31,7 +35,50 @@ MptScsiControllerSupported (
   IN EFI_DEVICE_PATH_PROTOCOL               *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&PciIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        0,
+                        sizeof (Pci) / sizeof (UINT32),
+                        &Pci
+                        );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID &&
+      (Pci.Hdr.DeviceId == LSI_53C1030_PCI_DEVICE_ID ||
+       Pci.Hdr.DeviceId == LSI_SAS1068_PCI_DEVICE_ID ||
+       Pci.Hdr.DeviceId == LSI_SAS1068E_PCI_DEVICE_ID)) {
+    Status = EFI_SUCCESS;
+  } else {
+    Status = EFI_UNSUPPORTED;
+  }
+
+Done:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+  return Status;
 }
 
 STATIC
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
index 53585068684f..414b96e5a248 100644
--- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -21,7 +21,12 @@ [Sources]
 
 [Packages]
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
+
+[Protocols]
+  gEfiPciIoProtocolGuid        ## TO_START
-- 
2.27.0


From 81a88638f64aa1f328da5642edeff785ea59849e Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:00 +0300
Subject: [PATCH 25/43] OvmfPkg/MptScsiDxe: Install stubbed EXT_SCSI_PASS_THRU

Support dynamic insertion and removal of the protocol

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-6-nikita.leshchenko@oracle.com>
(cherry picked from commit a53e5b417413bae3f3ea89976380d169e2f6861d)
---
 OvmfPkg/MptScsiDxe/MptScsi.c      | 181 +++++++++++++++++++++++++++++-
 OvmfPkg/MptScsiDxe/MptScsiDxe.inf |   5 +-
 2 files changed, 183 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 4e2f8f2296fb..40d392c2346f 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -11,9 +11,12 @@
 
 #include <IndustryStandard/FusionMptScsi.h>
 #include <IndustryStandard/Pci.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
+#include <Protocol/ScsiPassThruExt.h>
 #include <Uefi/UefiSpec.h>
 
 //
@@ -22,6 +25,109 @@
 //
 #define MPT_SCSI_BINDING_VERSION 0x10
 
+//
+// Runtime Structures
+//
+
+#define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')
+typedef struct {
+  UINT32                          Signature;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
+  EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
+} MPT_SCSI_DEV;
+
+#define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
+  CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
+
+//
+// Ext SCSI Pass Thru
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiPassThru (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+  IN EFI_EVENT                                      Event     OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiGetNextTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN OUT UINT8                                      **Target,
+  IN OUT UINT64                                     *Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiGetNextTarget (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
+  IN OUT UINT8                                     **Target
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiBuildDevicePath (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
+  IN UINT8                                         *Target,
+  IN UINT64                                        Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL                  **DevicePath
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiGetTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
+  IN EFI_DEVICE_PATH_PROTOCOL                      *DevicePath,
+  OUT UINT8                                        **Target,
+  OUT UINT64                                       *Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiResetChannel (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MptScsiResetTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
+  IN UINT8                                         *Target,
+  IN UINT64                                        Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
 //
 // Driver Binding
 //
@@ -90,7 +196,49 @@ MptScsiControllerStart (
   IN EFI_DEVICE_PATH_PROTOCOL               *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS           Status;
+  MPT_SCSI_DEV         *Dev;
+
+  Dev = AllocateZeroPool (sizeof (*Dev));
+  if (Dev == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Dev->Signature = MPT_SCSI_DEV_SIGNATURE;
+
+  //
+  // Host adapter channel, doesn't exist
+  //
+  Dev->PassThruMode.AdapterId = MAX_UINT32;
+  Dev->PassThruMode.Attributes =
+    EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
+    EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
+
+  Dev->PassThru.Mode = &Dev->PassThruMode;
+  Dev->PassThru.PassThru = &MptScsiPassThru;
+  Dev->PassThru.GetNextTargetLun = &MptScsiGetNextTargetLun;
+  Dev->PassThru.BuildDevicePath = &MptScsiBuildDevicePath;
+  Dev->PassThru.GetTargetLun = &MptScsiGetTargetLun;
+  Dev->PassThru.ResetChannel = &MptScsiResetChannel;
+  Dev->PassThru.ResetTargetLun = &MptScsiResetTargetLun;
+  Dev->PassThru.GetNextTarget = &MptScsiGetNextTarget;
+
+  Status = gBS->InstallProtocolInterface (
+                  &ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Dev->PassThru
+                  );
+  if (EFI_ERROR (Status)) {
+    goto FreePool;
+  }
+
+  return EFI_SUCCESS;
+
+FreePool:
+  FreePool (Dev);
+
+  return Status;
 }
 
 STATIC
@@ -103,7 +251,36 @@ MptScsiControllerStop (
   IN  EFI_HANDLE                            *ChildHandleBuffer
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS                      Status;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+  MPT_SCSI_DEV                    *Dev;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  (VOID **)&PassThru,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Dev = MPT_SCSI_FROM_PASS_THRU (PassThru);
+
+  Status = gBS->UninstallProtocolInterface (
+                  ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  &Dev->PassThru
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  FreePool (Dev);
+
+  return Status;
 }
 
 STATIC
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
index 414b96e5a248..9f7c98829ee1 100644
--- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -24,9 +24,12 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
 
 [Protocols]
-  gEfiPciIoProtocolGuid        ## TO_START
+  gEfiExtScsiPassThruProtocolGuid        ## BY_START
+  gEfiPciIoProtocolGuid                  ## TO_START
-- 
2.27.0


From d0c17fe46f6da9da22c4bb5ef4cbd7d7d38fb99f Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:01 +0300
Subject: [PATCH 26/43] OvmfPkg/MptScsiDxe: Report targets and one LUN

The controller supports up to 8 targets in practice (Not reported by the
controller, but based on the implementation of the virtual device),
report them in GetNextTarget and GetNextTargetLun. The firmware will
then try to communicate with them and create a block device for each one
that responds.

Support for multiple LUNs will be implemented in another series.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-7-nikita.leshchenko@oracle.com>
(cherry picked from commit 093cceaf79b5d16880050a08e4bd465954ea6a97)
---
 OvmfPkg/MptScsiDxe/MptScsi.c      | 63 ++++++++++++++++++++++++++++++-
 OvmfPkg/MptScsiDxe/MptScsiDxe.inf |  5 +++
 OvmfPkg/OvmfPkg.dec               |  4 ++
 3 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 40d392c2346f..d396bff85cb6 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -11,8 +11,10 @@
 
 #include <IndustryStandard/FusionMptScsi.h>
 #include <IndustryStandard/Pci.h>
+#include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
 #include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
@@ -34,6 +36,7 @@ typedef struct {
   UINT32                          Signature;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
+  UINT8                           MaxTarget;
 } MPT_SCSI_DEV;
 
 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
@@ -57,6 +60,22 @@ MptScsiPassThru (
   return EFI_UNSUPPORTED;
 }
 
+STATIC
+BOOLEAN
+IsTargetInitialized (
+  IN UINT8                                          *Target
+  )
+{
+  UINTN Idx;
+
+  for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
+    if (Target[Idx] != 0xFF) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
 STATIC
 EFI_STATUS
 EFIAPI
@@ -66,7 +85,28 @@ MptScsiGetNextTargetLun (
   IN OUT UINT64                                     *Lun
   )
 {
-  return EFI_UNSUPPORTED;
+  MPT_SCSI_DEV *Dev;
+
+  Dev = MPT_SCSI_FROM_PASS_THRU (This);
+  //
+  // Currently support only LUN 0, so hardcode it
+  //
+  if (!IsTargetInitialized (*Target)) {
+    ZeroMem (*Target, TARGET_MAX_BYTES);
+    *Lun = 0;
+  } else if (**Target > Dev->MaxTarget || *Lun > 0) {
+    return EFI_INVALID_PARAMETER;
+  } else if (**Target < Dev->MaxTarget) {
+    //
+    // This device interface support 256 targets only, so it's enough to
+    // increment the LSB of Target, as it will never overflow.
+    //
+    **Target += 1;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
 }
 
 STATIC
@@ -77,7 +117,24 @@ MptScsiGetNextTarget (
   IN OUT UINT8                                     **Target
   )
 {
-  return EFI_UNSUPPORTED;
+  MPT_SCSI_DEV *Dev;
+
+  Dev = MPT_SCSI_FROM_PASS_THRU (This);
+  if (!IsTargetInitialized (*Target)) {
+    ZeroMem (*Target, TARGET_MAX_BYTES);
+  } else if (**Target > Dev->MaxTarget) {
+    return EFI_INVALID_PARAMETER;
+  } else if (**Target < Dev->MaxTarget) {
+    //
+    // This device interface support 256 targets only, so it's enough to
+    // increment the LSB of Target, as it will never overflow.
+    //
+    **Target += 1;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
 }
 
 STATIC
@@ -206,6 +263,8 @@ MptScsiControllerStart (
 
   Dev->Signature = MPT_SCSI_DEV_SIGNATURE;
 
+  Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);
+
   //
   // Host adapter channel, doesn't exist
   //
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
index 9f7c98829ee1..d5fd2516e475 100644
--- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -24,8 +24,10 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  BaseMemoryLib
   DebugLib
   MemoryAllocationLib
+  PcdLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
@@ -33,3 +35,6 @@ [LibraryClasses]
 [Protocols]
   gEfiExtScsiPassThruProtocolGuid        ## BY_START
   gEfiPciIoProtocolGuid                  ## TO_START
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit   ## CONSUMES
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 06aed2b70818..f4017664aaac 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -136,6 +136,10 @@ [PcdsFixedAtBuild]
   #  polling loop iteration.
   gUefiOvmfPkgTokenSpaceGuid.PcdPvScsiWaitForCmpStallInUsecs|5|UINT32|0x38
 
+  ## Set the *inclusive* number of targets that MptScsi exposes for scan
+  #  by ScsiBusDxe.
+  gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit|7|UINT8|0x39
+
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa
-- 
2.27.0


From 9fe792f1519aae4c470a2cf172bdc51de78d9c5e Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:02 +0300
Subject: [PATCH 27/43] OvmfPkg/MptScsiDxe: Build and decode DevicePath

Used to identify the individual disks in the hardware tree.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-8-nikita.leshchenko@oracle.com>
(cherry picked from commit f9941d31dd6f95ca7557d5a5f2c1874bd3bd0e14)
---
 OvmfPkg/MptScsiDxe/MptScsi.c | 61 ++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index d396bff85cb6..66d57f1c85d8 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -147,7 +147,36 @@ MptScsiBuildDevicePath (
   IN OUT EFI_DEVICE_PATH_PROTOCOL                  **DevicePath
   )
 {
-  return EFI_UNSUPPORTED;
+  MPT_SCSI_DEV     *Dev;
+  SCSI_DEVICE_PATH *ScsiDevicePath;
+
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // This device support 256 targets only, so it's enough to dereference
+  // the LSB of Target.
+  //
+  Dev = MPT_SCSI_FROM_PASS_THRU (This);
+  if (*Target > Dev->MaxTarget || Lun > 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  ScsiDevicePath = AllocateZeroPool (sizeof (*ScsiDevicePath));
+  if (ScsiDevicePath == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ScsiDevicePath->Header.Type      = MESSAGING_DEVICE_PATH;
+  ScsiDevicePath->Header.SubType   = MSG_SCSI_DP;
+  ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
+  ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
+  ScsiDevicePath->Pun              = *Target;
+  ScsiDevicePath->Lun              = (UINT16)Lun;
+
+  *DevicePath = &ScsiDevicePath->Header;
+  return EFI_SUCCESS;
 }
 
 STATIC
@@ -160,7 +189,35 @@ MptScsiGetTargetLun (
   OUT UINT64                                       *Lun
   )
 {
-  return EFI_UNSUPPORTED;
+  MPT_SCSI_DEV     *Dev;
+  SCSI_DEVICE_PATH *ScsiDevicePath;
+
+  if (DevicePath == NULL ||
+      Target == NULL || *Target == NULL || Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DevicePath->Type    != MESSAGING_DEVICE_PATH ||
+      DevicePath->SubType != MSG_SCSI_DP) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Dev = MPT_SCSI_FROM_PASS_THRU (This);
+  ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
+  if (ScsiDevicePath->Pun > Dev->MaxTarget ||
+      ScsiDevicePath->Lun > 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  ZeroMem (*Target, TARGET_MAX_BYTES);
+  //
+  // This device support 256 targets only, so it's enough to set the LSB
+  // of Target.
+  //
+  **Target = (UINT8)ScsiDevicePath->Pun;
+  *Lun = ScsiDevicePath->Lun;
+
+  return EFI_SUCCESS;
 }
 
 STATIC
-- 
2.27.0


From 9460f6d25d1a41a6e3b07aede84179281c23265b Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:03 +0300
Subject: [PATCH 28/43] OvmfPkg/MptScsiDxe: Open PciIo protocol for later use

This will give us an exclusive access to the PciIo of this device
after it was started and until is will be stopped.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-9-nikita.leshchenko@oracle.com>
(cherry picked from commit da8c0b8f4d2df23de249580a595afea76b5185fd)
---
 OvmfPkg/MptScsiDxe/MptScsi.c | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 66d57f1c85d8..3dfc78cf2e1f 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -37,6 +37,7 @@ typedef struct {
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
   UINT8                           MaxTarget;
+  EFI_PCI_IO_PROTOCOL             *PciIo;
 } MPT_SCSI_DEV;
 
 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
@@ -322,6 +323,18 @@ MptScsiControllerStart (
 
   Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);
 
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&Dev->PciIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto FreePool;
+  }
+
   //
   // Host adapter channel, doesn't exist
   //
@@ -346,11 +359,19 @@ MptScsiControllerStart (
                   &Dev->PassThru
                   );
   if (EFI_ERROR (Status)) {
-    goto FreePool;
+    goto CloseProtocol;
   }
 
   return EFI_SUCCESS;
 
+CloseProtocol:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
 FreePool:
   FreePool (Dev);
 
@@ -394,6 +415,13 @@ MptScsiControllerStop (
     return Status;
   }
 
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
   FreePool (Dev);
 
   return Status;
-- 
2.27.0


From 8b5974a1b340cd14484b67c6d8f67da52375cd82 Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:04 +0300
Subject: [PATCH 29/43] OvmfPkg/MptScsiDxe: Set and restore PCI attributes

Enable the IO Space and Bus Mastering and restore the original values
when the device is stopped. This is a standard procedure in PCI
drivers.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-10-nikita.leshchenko@oracle.com>
(cherry picked from commit ecdbdba636b6bac706641ca47aa973193079a3cc)
---
 OvmfPkg/MptScsiDxe/MptScsi.c | 65 +++++++++++++++++++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 3dfc78cf2e1f..289bd9fc372b 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -38,6 +38,7 @@ typedef struct {
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
   UINT8                           MaxTarget;
   EFI_PCI_IO_PROTOCOL             *PciIo;
+  UINT64                          OriginalPciAttributes;
 } MPT_SCSI_DEV;
 
 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
@@ -335,6 +336,53 @@ MptScsiControllerStart (
     goto FreePool;
   }
 
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationGet,
+                         0,
+                         &Dev->OriginalPciAttributes
+                         );
+  if (EFI_ERROR (Status)) {
+    goto CloseProtocol;
+  }
+
+  //
+  // Enable I/O Space & Bus-Mastering
+  //
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationEnable,
+                         (EFI_PCI_IO_ATTRIBUTE_IO |
+                          EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
+                         NULL
+                         );
+  if (EFI_ERROR (Status)) {
+    goto CloseProtocol;
+  }
+
+  //
+  // Signal device supports 64-bit DMA addresses
+  //
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationEnable,
+                         EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
+                         NULL
+                         );
+  if (EFI_ERROR (Status)) {
+    //
+    // Warn user that device will only be using 32-bit DMA addresses.
+    //
+    // Note that this does not prevent the device/driver from working
+    // and therefore we only warn and continue as usual.
+    //
+    DEBUG ((
+      DEBUG_WARN,
+      "%a: failed to enable 64-bit DMA addresses\n",
+      __FUNCTION__
+      ));
+  }
+
   //
   // Host adapter channel, doesn't exist
   //
@@ -359,11 +407,19 @@ MptScsiControllerStart (
                   &Dev->PassThru
                   );
   if (EFI_ERROR (Status)) {
-    goto CloseProtocol;
+    goto RestoreAttributes;
   }
 
   return EFI_SUCCESS;
 
+RestoreAttributes:
+  Dev->PciIo->Attributes (
+                Dev->PciIo,
+                EfiPciIoAttributeOperationSet,
+                Dev->OriginalPciAttributes,
+                NULL
+                );
+
 CloseProtocol:
   gBS->CloseProtocol (
          ControllerHandle,
@@ -415,6 +471,13 @@ MptScsiControllerStop (
     return Status;
   }
 
+  Dev->PciIo->Attributes (
+                Dev->PciIo,
+                EfiPciIoAttributeOperationSet,
+                Dev->OriginalPciAttributes,
+                NULL
+                );
+
   gBS->CloseProtocol (
          ControllerHandle,
          &gEfiPciIoProtocolGuid,
-- 
2.27.0


From 3616bb13045b070f45999800788adb8e07679648 Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:05 +0300
Subject: [PATCH 30/43] OvmfPkg/MptScsiDxe: Initialize hardware

Reset and send the IO controller initialization request. The reply is
read back to complete the doorbell function but it isn't useful to us
because it doesn't contain relevant data or status codes.

See "LSI53C1030 PCI-X to Dual Channel Ultra320 SCSI Multifunction
Controller" technical manual for more information.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Message-Id: <20200504210607.144434-11-nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 81cada9892cc4bf3725009c5fb836115da10b1c8)
---
 .../Include/IndustryStandard/FusionMptScsi.h  | 128 +++++++++++
 OvmfPkg/MptScsiDxe/MptScsi.c                  | 198 +++++++++++++++++-
 2 files changed, 325 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
index df9bdc2f0348..655d629d902e 100644
--- a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
+++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
@@ -20,4 +20,132 @@
 #define LSI_SAS1068_PCI_DEVICE_ID 0x0054
 #define LSI_SAS1068E_PCI_DEVICE_ID 0x0058
 
+#define MPT_REG_DOORBELL  0x00
+#define MPT_REG_WRITE_SEQ 0x04
+#define MPT_REG_HOST_DIAG 0x08
+#define MPT_REG_TEST      0x0c
+#define MPT_REG_DIAG_DATA 0x10
+#define MPT_REG_DIAG_ADDR 0x14
+#define MPT_REG_ISTATUS   0x30
+#define MPT_REG_IMASK     0x34
+#define MPT_REG_REQ_Q     0x40
+#define MPT_REG_REP_Q     0x44
+
+#define MPT_DOORBELL_RESET     0x40
+#define MPT_DOORBELL_HANDSHAKE 0x42
+
+#define MPT_IMASK_DOORBELL 0x01
+#define MPT_IMASK_REPLY    0x08
+
+#define MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST 0x00
+#define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT        0x02
+
+#define MPT_SG_ENTRY_TYPE_SIMPLE 0x01
+
+#define MPT_IOC_WHOINIT_ROM_BIOS 0x02
+
+//
+// Device structures
+//
+
+#pragma pack (1)
+typedef struct {
+  UINT8     WhoInit;
+  UINT8     Reserved1;
+  UINT8     ChainOffset;
+  UINT8     Function;
+  UINT8     Flags;
+  UINT8     MaxDevices;
+  UINT8     MaxBuses;
+  UINT8     MessageFlags;
+  UINT32    MessageContext;
+  UINT16    ReplyFrameSize;
+  UINT16    Reserved2;
+  UINT32    HostMfaHighAddr;
+  UINT32    SenseBufferHighAddr;
+} MPT_IO_CONTROLLER_INIT_REQUEST;
+
+typedef struct {
+  UINT8     WhoInit;
+  UINT8     Reserved1;
+  UINT8     MessageLength;
+  UINT8     Function;
+  UINT8     Flags;
+  UINT8     MaxDevices;
+  UINT8     MaxBuses;
+  UINT8     MessageFlags;
+  UINT32    MessageContext;
+  UINT16    Reserved2;
+  UINT16    IocStatus;
+  UINT32    IocLogInfo;
+} MPT_IO_CONTROLLER_INIT_REPLY;
+
+typedef struct {
+  UINT8     TargetId;
+  UINT8     Bus;
+  UINT8     ChainOffset;
+  UINT8     Function;
+  UINT8     CdbLength;
+  UINT8     SenseBufferLength;
+  UINT8     Reserved;
+  UINT8     MessageFlags;
+  UINT32    MessageContext;
+  UINT8     Lun[8];
+  UINT32    Control;
+  UINT8     Cdb[16];
+  UINT32    DataLength;
+  UINT32    SenseBufferLowAddress;
+} MPT_SCSI_IO_REQUEST;
+
+typedef struct {
+  UINT32    Length:             24;
+  UINT32    EndOfList:          1;
+  UINT32    Is64BitAddress:     1;
+  //
+  // True when the buffer contains data to be transfered. Otherwise it's the
+  // destination buffer
+  //
+  UINT32    BufferContainsData: 1;
+  UINT32    LocalAddress:       1;
+  UINT32    ElementType:        2;
+  UINT32    EndOfBuffer:        1;
+  UINT32    LastElement:        1;
+  UINT64    DataBufferAddress;
+} MPT_SG_ENTRY_SIMPLE;
+
+typedef struct {
+  UINT8     TargetId;
+  UINT8     Bus;
+  UINT8     MessageLength;
+  UINT8     Function;
+  UINT8     CdbLength;
+  UINT8     SenseBufferLength;
+  UINT8     Reserved;
+  UINT8     MessageFlags;
+  UINT32    MessageContext;
+  UINT8     ScsiStatus;
+  UINT8     ScsiState;
+  UINT16    IocStatus;
+  UINT32    IocLogInfo;
+  UINT32    TransferCount;
+  UINT32    SenseCount;
+  UINT32    ResponseInfo;
+} MPT_SCSI_IO_REPLY;
+
+typedef struct {
+  MPT_SCSI_IO_REQUEST Header;
+  MPT_SG_ENTRY_SIMPLE Sg;
+} MPT_SCSI_REQUEST_WITH_SG;
+#pragma pack ()
+
+typedef union {
+  MPT_SCSI_IO_REPLY        Data;
+  UINT64                   Uint64; // 8 byte alignment required by HW
+} MPT_SCSI_IO_REPLY_ALIGNED;
+
+typedef union {
+  MPT_SCSI_REQUEST_WITH_SG Data;
+  UINT64                   Uint64; // 8 byte alignment required by HW
+} MPT_SCSI_REQUEST_ALIGNED;
+
 #endif // __FUSION_MPT_SCSI_H__
diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 289bd9fc372b..2cc69b88dab3 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -44,6 +44,192 @@ typedef struct {
 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
   CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
 
+//
+// Hardware functions
+//
+
+STATIC
+EFI_STATUS
+Out32 (
+  IN MPT_SCSI_DEV       *Dev,
+  IN UINT32             Addr,
+  IN UINT32             Data
+  )
+{
+  return Dev->PciIo->Io.Write (
+                          Dev->PciIo,
+                          EfiPciIoWidthUint32,
+                          PCI_BAR_IDX0,
+                          Addr,
+                          1,
+                          &Data
+                          );
+}
+
+STATIC
+EFI_STATUS
+In32 (
+  IN  MPT_SCSI_DEV       *Dev,
+  IN  UINT32             Addr,
+  OUT UINT32             *Data
+  )
+{
+  return Dev->PciIo->Io.Read (
+                          Dev->PciIo,
+                          EfiPciIoWidthUint32,
+                          PCI_BAR_IDX0,
+                          Addr,
+                          1,
+                          Data
+                          );
+}
+
+STATIC
+EFI_STATUS
+MptDoorbell (
+  IN MPT_SCSI_DEV       *Dev,
+  IN UINT8              DoorbellFunc,
+  IN UINT8              DoorbellArg
+  )
+{
+  return Out32 (
+           Dev,
+           MPT_REG_DOORBELL,
+           (((UINT32)DoorbellFunc) << 24) | (DoorbellArg << 16)
+           );
+}
+
+STATIC
+EFI_STATUS
+MptScsiReset (
+  IN MPT_SCSI_DEV       *Dev
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // Reset hardware
+  //
+  Status = MptDoorbell (Dev, MPT_DOORBELL_RESET, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Mask interrupts
+  //
+  Status = Out32 (Dev, MPT_REG_IMASK, MPT_IMASK_DOORBELL | MPT_IMASK_REPLY);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Clear interrupt status
+  //
+  Status = Out32 (Dev, MPT_REG_ISTATUS, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiInit (
+  IN MPT_SCSI_DEV       *Dev
+  )
+{
+  EFI_STATUS                       Status;
+  union {
+    MPT_IO_CONTROLLER_INIT_REQUEST Data;
+    UINT32                         Uint32;
+  } AlignedReq;
+  MPT_IO_CONTROLLER_INIT_REQUEST   *Req;
+  MPT_IO_CONTROLLER_INIT_REPLY     Reply;
+  UINT8                            *ReplyBytes;
+  UINT32                           ReplyWord;
+
+  Req = &AlignedReq.Data;
+
+  Status = MptScsiReset (Dev);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  ZeroMem (Req, sizeof (*Req));
+  ZeroMem (&Reply, sizeof (Reply));
+  Req->WhoInit = MPT_IOC_WHOINIT_ROM_BIOS;
+  Req->Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT;
+  STATIC_ASSERT (
+    FixedPcdGet8 (PcdMptScsiMaxTargetLimit) < 255,
+    "Req supports 255 targets only (max target is 254)"
+    );
+  Req->MaxDevices = Dev->MaxTarget + 1;
+  Req->MaxBuses = 1;
+
+  //
+  // Send controller init through doorbell
+  //
+  STATIC_ASSERT (
+    sizeof (*Req) % sizeof (UINT32) == 0,
+    "Req must be multiple of UINT32"
+    );
+  STATIC_ASSERT (
+    sizeof (*Req) / sizeof (UINT32) <= MAX_UINT8,
+    "Req must fit in MAX_UINT8 Dwords"
+    );
+  Status = MptDoorbell (
+             Dev,
+             MPT_DOORBELL_HANDSHAKE,
+             (UINT8)(sizeof (*Req) / sizeof (UINT32))
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = Dev->PciIo->Io.Write (
+                            Dev->PciIo,
+                            EfiPciIoWidthFifoUint32,
+                            PCI_BAR_IDX0,
+                            MPT_REG_DOORBELL,
+                            sizeof (*Req) / sizeof (UINT32),
+                            Req
+                            );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Read reply through doorbell
+  // Each 32bit (Dword) read produces 16bit (Word) of data
+  //
+  // The reply is read back to complete the doorbell function but it
+  // isn't useful because it doesn't contain relevant data or status
+  // codes.
+  //
+  STATIC_ASSERT (
+    sizeof (Reply) % sizeof (UINT16) == 0,
+    "Reply must be multiple of UINT16"
+    );
+  ReplyBytes = (UINT8 *)&Reply;
+  while (ReplyBytes != (UINT8 *)(&Reply + 1)) {
+    Status = In32 (Dev, MPT_REG_DOORBELL, &ReplyWord);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    CopyMem (ReplyBytes, &ReplyWord, sizeof (UINT16));
+    ReplyBytes += sizeof (UINT16);
+  }
+
+  //
+  // Clear interrupts generated by doorbell reply
+  //
+  Status = Out32 (Dev, MPT_REG_ISTATUS, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
 //
 // Ext SCSI Pass Thru
 //
@@ -383,6 +569,11 @@ MptScsiControllerStart (
       ));
   }
 
+  Status = MptScsiInit (Dev);
+  if (EFI_ERROR (Status)) {
+    goto RestoreAttributes;
+  }
+
   //
   // Host adapter channel, doesn't exist
   //
@@ -407,11 +598,14 @@ MptScsiControllerStart (
                   &Dev->PassThru
                   );
   if (EFI_ERROR (Status)) {
-    goto RestoreAttributes;
+    goto UninitDev;
   }
 
   return EFI_SUCCESS;
 
+UninitDev:
+  MptScsiReset (Dev);
+
 RestoreAttributes:
   Dev->PciIo->Attributes (
                 Dev->PciIo,
@@ -471,6 +665,8 @@ MptScsiControllerStop (
     return Status;
   }
 
+  MptScsiReset (Dev);
+
   Dev->PciIo->Attributes (
                 Dev->PciIo,
                 EfiPciIoAttributeOperationSet,
-- 
2.27.0


From 25a9eaf6e5be947bf365fceab30a6b356251f80a Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:06 +0300
Subject: [PATCH 31/43] OvmfPkg/MptScsiDxe: Implement the PassThru method

Machines should be able to boot after this commit. Tested with different
Linux distributions (Ubuntu, CentOS) and different Windows
versions (Windows 7, Windows 10, Server 2016).

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-12-nikita.leshchenko@oracle.com>
[lersek@redhat.com: MPT_SCSI_DMA_ADDR_HIGH: drop redundant space char]
(cherry picked from commit 505812ae1d2d1d3efa5d9c622d87a990918442ee)
---
 .../Include/IndustryStandard/FusionMptScsi.h  |   9 +
 OvmfPkg/MptScsiDxe/MptScsi.c                  | 409 +++++++++++++++++-
 OvmfPkg/MptScsiDxe/MptScsiDxe.inf             |   4 +
 OvmfPkg/OvmfPkg.dec                           |   3 +
 4 files changed, 423 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
index 655d629d902e..99778d1537da 100644
--- a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
+++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h
@@ -44,6 +44,15 @@
 
 #define MPT_IOC_WHOINIT_ROM_BIOS 0x02
 
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE  (0x00 << 24)
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE (0x01 << 24)
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ  (0x02 << 24)
+
+#define MPT_SCSI_IOCSTATUS_SUCCESS          0x0000
+#define MPT_SCSI_IOCSTATUS_DEVICE_NOT_THERE 0x0043
+#define MPT_SCSI_IOCSTATUS_DATA_OVERRUN     0x0044
+#define MPT_SCSI_IOCSTATUS_DATA_UNDERRUN    0x0045
+
 //
 // Device structures
 //
diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 2cc69b88dab3..8be38f6cdb88 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -11,6 +11,7 @@
 
 #include <IndustryStandard/FusionMptScsi.h>
 #include <IndustryStandard/Pci.h>
+#include <Library/BaseLib.h>
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
 #include <Library/MemoryAllocationLib.h>
@@ -18,6 +19,7 @@
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
+#include <Protocol/PciRootBridgeIo.h>
 #include <Protocol/ScsiPassThruExt.h>
 #include <Uefi/UefiSpec.h>
 
@@ -31,19 +33,50 @@
 // Runtime Structures
 //
 
+typedef struct {
+  MPT_SCSI_REQUEST_ALIGNED        IoRequest;
+  MPT_SCSI_IO_REPLY_ALIGNED       IoReply;
+  //
+  // As EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.SenseDataLength is defined
+  // as UINT8, defining here SenseData size to MAX_UINT8 will guarantee it
+  // cannot overflow when passed to device.
+  //
+  UINT8                           Sense[MAX_UINT8];
+  //
+  // This size of the data is arbitrarily chosen.
+  // It seems to be sufficient for all I/O requests sent through
+  // EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() for common boot scenarios.
+  //
+  UINT8                           Data[0x2000];
+} MPT_SCSI_DMA_BUFFER;
+
 #define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')
 typedef struct {
   UINT32                          Signature;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
   UINT8                           MaxTarget;
+  UINT32                          StallPerPollUsec;
   EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT64                          OriginalPciAttributes;
+  MPT_SCSI_DMA_BUFFER             *Dma;
+  EFI_PHYSICAL_ADDRESS            DmaPhysical;
+  VOID                            *DmaMapping;
+  BOOLEAN                         IoReplyEnqueued;
 } MPT_SCSI_DEV;
 
 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
   CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
 
+#define MPT_SCSI_DMA_ADDR(Dev, MemberName) \
+  (Dev->DmaPhysical + OFFSET_OF (MPT_SCSI_DMA_BUFFER, MemberName))
+
+#define MPT_SCSI_DMA_ADDR_HIGH(Dev, MemberName) \
+  ((UINT32)RShiftU64 (MPT_SCSI_DMA_ADDR (Dev, MemberName), 32))
+
+#define MPT_SCSI_DMA_ADDR_LOW(Dev, MemberName) \
+  ((UINT32)MPT_SCSI_DMA_ADDR (Dev, MemberName))
+
 //
 // Hardware functions
 //
@@ -165,6 +198,9 @@ MptScsiInit (
     );
   Req->MaxDevices = Dev->MaxTarget + 1;
   Req->MaxBuses = 1;
+  Req->ReplyFrameSize = sizeof Dev->Dma->IoReply.Data;
+  Req->HostMfaHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, IoRequest);
+  Req->SenseBufferHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, Sense);
 
   //
   // Send controller init through doorbell
@@ -230,6 +266,288 @@ MptScsiInit (
   return EFI_SUCCESS;
 }
 
+STATIC
+EFI_STATUS
+ReportHostAdapterError (
+  OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  DEBUG ((DEBUG_ERROR, "%a: fatal error in scsi request\n", __FUNCTION__));
+  Packet->InTransferLength  = 0;
+  Packet->OutTransferLength = 0;
+  Packet->SenseDataLength   = 0;
+  Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+  Packet->TargetStatus      = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED;
+  return EFI_DEVICE_ERROR;
+}
+
+STATIC
+EFI_STATUS
+ReportHostAdapterOverrunError (
+  OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  Packet->SenseDataLength = 0;
+  Packet->HostAdapterStatus =
+            EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+  Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+  return EFI_BAD_BUFFER_SIZE;
+}
+
+STATIC
+EFI_STATUS
+MptScsiPopulateRequest (
+  IN MPT_SCSI_DEV                                   *Dev,
+  IN UINT8                                          Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  MPT_SCSI_REQUEST_WITH_SG *Request;
+
+  Request = &Dev->Dma->IoRequest.Data;
+
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
+      (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) ||
+      Packet->CdbLength > sizeof (Request->Header.Cdb)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (Target > Dev->MaxTarget || Lun > 0 ||
+      Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
+      //
+      // Trying to receive, but destination pointer is NULL, or contradicting
+      // transfer direction
+      //
+      (Packet->InTransferLength > 0 &&
+       (Packet->InDataBuffer == NULL ||
+        Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE
+         )
+        ) ||
+
+      //
+      // Trying to send, but source pointer is NULL, or contradicting transfer
+      // direction
+      //
+      (Packet->OutTransferLength > 0 &&
+       (Packet->OutDataBuffer == NULL ||
+        Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ
+         )
+        )
+    ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Packet->InTransferLength > sizeof (Dev->Dma->Data)) {
+    Packet->InTransferLength = sizeof (Dev->Dma->Data);
+    return ReportHostAdapterOverrunError (Packet);
+  }
+  if (Packet->OutTransferLength > sizeof (Dev->Dma->Data)) {
+    Packet->OutTransferLength = sizeof (Dev->Dma->Data);
+    return ReportHostAdapterOverrunError (Packet);
+  }
+
+  ZeroMem (Request, sizeof (*Request));
+  Request->Header.TargetId = Target;
+  //
+  // Only LUN 0 is currently supported, hence the cast is safe
+  //
+  Request->Header.Lun[1] = (UINT8)Lun;
+  Request->Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
+  Request->Header.MessageContext = 1; // We handle one request at a time
+
+  Request->Header.CdbLength = Packet->CdbLength;
+  CopyMem (Request->Header.Cdb, Packet->Cdb, Packet->CdbLength);
+
+  //
+  // SenseDataLength is UINT8, Sense[] is MAX_UINT8, so we can't overflow
+  //
+  ZeroMem (Dev->Dma->Sense, Packet->SenseDataLength);
+  Request->Header.SenseBufferLength = Packet->SenseDataLength;
+  Request->Header.SenseBufferLowAddress = MPT_SCSI_DMA_ADDR_LOW (Dev, Sense);
+
+  Request->Sg.EndOfList = 1;
+  Request->Sg.EndOfBuffer = 1;
+  Request->Sg.LastElement = 1;
+  Request->Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE;
+  Request->Sg.Is64BitAddress = 1;
+  Request->Sg.DataBufferAddress = MPT_SCSI_DMA_ADDR (Dev, Data);
+
+  //
+  // "MPT_SG_ENTRY_SIMPLE.Length" is a 24-bit quantity.
+  //
+  STATIC_ASSERT (
+    sizeof (Dev->Dma->Data) < SIZE_16MB,
+    "MPT_SCSI_DMA_BUFFER.Data must be smaller than 16MB"
+    );
+
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+    Request->Header.DataLength = Packet->InTransferLength;
+    Request->Sg.Length = Packet->InTransferLength;
+    Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ;
+  } else {
+    Request->Header.DataLength = Packet->OutTransferLength;
+    Request->Sg.Length = Packet->OutTransferLength;
+    Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE;
+
+    CopyMem (Dev->Dma->Data, Packet->OutDataBuffer, Packet->OutTransferLength);
+    Request->Sg.BufferContainsData = 1;
+  }
+
+  if (Request->Header.DataLength == 0) {
+    Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiSendRequest (
+  IN MPT_SCSI_DEV                                   *Dev,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  EFI_STATUS Status;
+
+  if (!Dev->IoReplyEnqueued) {
+    //
+    // Put one free reply frame on the reply queue, the hardware may use it to
+    // report an error to us.
+    //
+    Status = Out32 (Dev, MPT_REG_REP_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoReply));
+    if (EFI_ERROR (Status)) {
+      return EFI_DEVICE_ERROR;
+    }
+    Dev->IoReplyEnqueued = TRUE;
+  }
+
+  Status = Out32 (Dev, MPT_REG_REQ_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoRequest));
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiGetReply (
+  IN MPT_SCSI_DEV                                   *Dev,
+  OUT UINT32                                        *Reply
+  )
+{
+  EFI_STATUS Status;
+  UINT32     Istatus;
+  UINT32     EmptyReply;
+
+  //
+  // Timeouts are not supported for
+  // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() in this implementation.
+  //
+  for (;;) {
+    Status = In32 (Dev, MPT_REG_ISTATUS, &Istatus);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // Interrupt raised
+    //
+    if (Istatus & MPT_IMASK_REPLY) {
+      break;
+    }
+
+    gBS->Stall (Dev->StallPerPollUsec);
+  }
+
+  Status = In32 (Dev, MPT_REG_REP_Q, Reply);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // The driver is supposed to fetch replies until 0xffffffff is returned, which
+  // will reset the interrupt status. We put only one request, so we expect the
+  // next read reply to be the last.
+  //
+  Status = In32 (Dev, MPT_REG_REP_Q, &EmptyReply);
+  if (EFI_ERROR (Status) || EmptyReply != MAX_UINT32) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiHandleReply (
+  IN MPT_SCSI_DEV                                   *Dev,
+  IN UINT32                                         Reply,
+  OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
+  )
+{
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+    CopyMem (Packet->InDataBuffer, Dev->Dma->Data, Packet->InTransferLength);
+  }
+
+  if (Reply == Dev->Dma->IoRequest.Data.Header.MessageContext) {
+    //
+    // This is a turbo reply, everything is good
+    //
+    Packet->SenseDataLength = 0;
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+    Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+
+  } else if ((Reply & BIT31) != 0) {
+    DEBUG ((DEBUG_INFO, "%a: Full reply returned\n", __FUNCTION__));
+    //
+    // When reply MSB is set, we got a full reply. Since we submitted only one
+    // reply frame, we know it's IoReply.
+    //
+    Dev->IoReplyEnqueued = FALSE;
+
+    Packet->TargetStatus = Dev->Dma->IoReply.Data.ScsiStatus;
+    //
+    // Make sure device only lowers SenseDataLength before copying sense
+    //
+    ASSERT (Dev->Dma->IoReply.Data.SenseCount <= Packet->SenseDataLength);
+    Packet->SenseDataLength =
+      (UINT8)MIN (Dev->Dma->IoReply.Data.SenseCount, Packet->SenseDataLength);
+    CopyMem (Packet->SenseData, Dev->Dma->Sense, Packet->SenseDataLength);
+
+    if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+      Packet->InTransferLength = Dev->Dma->IoReply.Data.TransferCount;
+    } else {
+      Packet->OutTransferLength = Dev->Dma->IoReply.Data.TransferCount;
+    }
+
+    switch (Dev->Dma->IoReply.Data.IocStatus) {
+    case MPT_SCSI_IOCSTATUS_SUCCESS:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+      break;
+    case MPT_SCSI_IOCSTATUS_DEVICE_NOT_THERE:
+      Packet->HostAdapterStatus =
+        EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
+      return EFI_TIMEOUT;
+    case MPT_SCSI_IOCSTATUS_DATA_UNDERRUN:
+    case MPT_SCSI_IOCSTATUS_DATA_OVERRUN:
+      Packet->HostAdapterStatus =
+        EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+      break;
+    default:
+      Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+      return EFI_DEVICE_ERROR;
+    }
+
+  } else {
+    DEBUG ((DEBUG_ERROR, "%a: unexpected reply (%x)\n", __FUNCTION__, Reply));
+    return ReportHostAdapterError (Packet);
+  }
+
+  return EFI_SUCCESS;
+}
+
 //
 // Ext SCSI Pass Thru
 //
@@ -245,7 +563,33 @@ MptScsiPassThru (
   IN EFI_EVENT                                      Event     OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS   Status;
+  MPT_SCSI_DEV *Dev;
+  UINT32       Reply;
+
+  Dev = MPT_SCSI_FROM_PASS_THRU (This);
+  //
+  // We only use first byte of target identifer
+  //
+  Status = MptScsiPopulateRequest (Dev, *Target, Lun, Packet);
+  if (EFI_ERROR (Status)) {
+    //
+    // MptScsiPopulateRequest modified packet according to the error
+    //
+    return Status;
+  }
+
+  Status = MptScsiSendRequest (Dev, Packet);
+  if (EFI_ERROR (Status)) {
+    return ReportHostAdapterError (Packet);
+  }
+
+  Status = MptScsiGetReply (Dev, &Reply);
+  if (EFI_ERROR (Status)) {
+    return ReportHostAdapterError (Packet);
+  }
+
+  return MptScsiHandleReply (Dev, Reply, Packet);
 }
 
 STATIC
@@ -500,6 +844,8 @@ MptScsiControllerStart (
 {
   EFI_STATUS           Status;
   MPT_SCSI_DEV         *Dev;
+  UINTN                Pages;
+  UINTN                BytesMapped;
 
   Dev = AllocateZeroPool (sizeof (*Dev));
   if (Dev == NULL) {
@@ -509,6 +855,7 @@ MptScsiControllerStart (
   Dev->Signature = MPT_SCSI_DEV_SIGNATURE;
 
   Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);
+  Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec);
 
   Status = gBS->OpenProtocol (
                   ControllerHandle,
@@ -569,11 +916,45 @@ MptScsiControllerStart (
       ));
   }
 
-  Status = MptScsiInit (Dev);
+  //
+  // Create buffers for data transfer
+  //
+  Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma));
+  Status = Dev->PciIo->AllocateBuffer (
+                         Dev->PciIo,
+                         AllocateAnyPages,
+                         EfiBootServicesData,
+                         Pages,
+                         (VOID **)&Dev->Dma,
+                         EFI_PCI_ATTRIBUTE_MEMORY_CACHED
+                         );
   if (EFI_ERROR (Status)) {
     goto RestoreAttributes;
   }
 
+  BytesMapped = EFI_PAGES_TO_SIZE (Pages);
+  Status = Dev->PciIo->Map (
+                         Dev->PciIo,
+                         EfiPciIoOperationBusMasterCommonBuffer,
+                         Dev->Dma,
+                         &BytesMapped,
+                         &Dev->DmaPhysical,
+                         &Dev->DmaMapping
+                         );
+  if (EFI_ERROR (Status)) {
+    goto FreeBuffer;
+  }
+
+  if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Unmap;
+  }
+
+  Status = MptScsiInit (Dev);
+  if (EFI_ERROR (Status)) {
+    goto Unmap;
+  }
+
   //
   // Host adapter channel, doesn't exist
   //
@@ -606,6 +987,19 @@ MptScsiControllerStart (
 UninitDev:
   MptScsiReset (Dev);
 
+Unmap:
+  Dev->PciIo->Unmap (
+                Dev->PciIo,
+                Dev->DmaMapping
+                );
+
+FreeBuffer:
+  Dev->PciIo->FreeBuffer (
+                Dev->PciIo,
+                Pages,
+                Dev->Dma
+                );
+
 RestoreAttributes:
   Dev->PciIo->Attributes (
                 Dev->PciIo,
@@ -667,6 +1061,17 @@ MptScsiControllerStop (
 
   MptScsiReset (Dev);
 
+  Dev->PciIo->Unmap (
+                Dev->PciIo,
+                Dev->DmaMapping
+                );
+
+  Dev->PciIo->FreeBuffer (
+                Dev->PciIo,
+                EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)),
+                Dev->Dma
+                );
+
   Dev->PciIo->Attributes (
                 Dev->PciIo,
                 EfiPciIoAttributeOperationSet,
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
index d5fd2516e475..09108939a5a2 100644
--- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -24,6 +24,7 @@ [Packages]
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  BaseLib
   BaseMemoryLib
   DebugLib
   MemoryAllocationLib
@@ -38,3 +39,6 @@ [Protocols]
 
 [FixedPcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit   ## CONSUMES
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec ## CONSUMES
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index f4017664aaac..464a7aaec2ec 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -140,6 +140,9 @@ [PcdsFixedAtBuild]
   #  by ScsiBusDxe.
   gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit|7|UINT8|0x39
 
+  ## Microseconds to stall between polling for MptScsi request result
+  gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x40
+
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa
-- 
2.27.0


From 4b54230503fbc263f0f311692a42137f7d8587be Mon Sep 17 00:00:00 2001
From: Nikita Leshenko <nikita.leshchenko@oracle.com>
Date: Tue, 5 May 2020 00:06:07 +0300
Subject: [PATCH 32/43] OvmfPkg/MptScsiDxe: Reset device on ExitBootServices()

This causes the device to forget about the reply frame. We allocated the
reply frame in EfiBootServicesData type memory, and code executing after
ExitBootServices() is permitted to overwrite it.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-13-nikita.leshchenko@oracle.com>
(cherry picked from commit c635a56384bfc4fdf3e00d102ea0eb7aa8d840fd)
---
 OvmfPkg/MptScsiDxe/MptScsi.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index 8be38f6cdb88..9ed1831bac6d 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -59,6 +59,7 @@ typedef struct {
   UINT32                          StallPerPollUsec;
   EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT64                          OriginalPciAttributes;
+  EFI_EVENT                       ExitBoot;
   MPT_SCSI_DMA_BUFFER             *Dma;
   EFI_PHYSICAL_ADDRESS            DmaPhysical;
   VOID                            *DmaMapping;
@@ -762,6 +763,20 @@ MptScsiResetChannel (
   return EFI_UNSUPPORTED;
 }
 
+STATIC
+VOID
+EFIAPI
+MptScsiExitBoot (
+  IN  EFI_EVENT Event,
+  IN  VOID      *Context
+  )
+{
+  MPT_SCSI_DEV *Dev;
+
+  Dev = Context;
+  DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
+  MptScsiReset (Dev);
+}
 STATIC
 EFI_STATUS
 EFIAPI
@@ -955,6 +970,17 @@ MptScsiControllerStart (
     goto Unmap;
   }
 
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                  TPL_CALLBACK,
+                  &MptScsiExitBoot,
+                  Dev,
+                  &Dev->ExitBoot
+                  );
+  if (EFI_ERROR (Status)) {
+    goto UninitDev;
+  }
+
   //
   // Host adapter channel, doesn't exist
   //
@@ -979,11 +1005,14 @@ MptScsiControllerStart (
                   &Dev->PassThru
                   );
   if (EFI_ERROR (Status)) {
-    goto UninitDev;
+    goto CloseExitBoot;
   }
 
   return EFI_SUCCESS;
 
+CloseExitBoot:
+  gBS->CloseEvent (Dev->ExitBoot);
+
 UninitDev:
   MptScsiReset (Dev);
 
@@ -1059,6 +1088,8 @@ MptScsiControllerStop (
     return Status;
   }
 
+  gBS->CloseEvent (Dev->ExitBoot);
+
   MptScsiReset (Dev);
 
   Dev->PciIo->Unmap (
-- 
2.27.0


From 9e9876bc2e2bb794d785858eda7c6d0572a7914c Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Wed, 15 Jul 2020 16:20:31 +0800
Subject: [PATCH 33/43] OvmfPkg/OvmfPkg.dec: Adjust PcdMptScsiStallPerPollUsec
 token value

The token value of PcdMptScsiStallPerPollUsec should be 0x3a since the
previous token value is 0x39.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Liran Alon <liran.alon@oracle.com>
Cc: Nikita Leshenko <nikita.leshchenko@oracle.com>
Signed-off-by: Gary Lin <glin@suse.com>
Message-Id: <20200715082031.30978-1-glin@suse.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
[lersek@redhat.com: clarify subject, fix typos in commit message]
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit d9269d69138860edb1ec9796ed48549dc6ba5735)
---
 OvmfPkg/OvmfPkg.dec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 464a7aaec2ec..e86a3f86c106 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -141,7 +141,7 @@ [PcdsFixedAtBuild]
   gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiMaxTargetLimit|7|UINT8|0x39
 
   ## Microseconds to stall between polling for MptScsi request result
-  gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x40
+  gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x3a
 
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
-- 
2.27.0


From 01a45461ec6c28cb62824bf995cf210b89768e6f Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:20 +0800
Subject: [PATCH 34/43] OvmfPkg/LsiScsiDxe: Create the empty driver

Create the driver with only a dummy LsiScsiEntryPoint() for the further
implementation of the driver for LSI 53C895A SCSI controller.

v2: Fix the mixed-case GUID string

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-2-glin@suse.com>
(cherry picked from commit e94d04a01b5a631390f6abd0d1e8f62a347ca72c)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c      | 25 +++++++++++++++++++++++++
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf | 26 ++++++++++++++++++++++++++
 OvmfPkg/OvmfPkgIa32.dsc           |  4 ++++
 OvmfPkg/OvmfPkgIa32.fdf           |  3 +++
 OvmfPkg/OvmfPkgIa32X64.dsc        |  4 ++++
 OvmfPkg/OvmfPkgIa32X64.fdf        |  3 +++
 OvmfPkg/OvmfPkgX64.dsc            |  4 ++++
 OvmfPkg/OvmfPkgX64.fdf            |  3 +++
 8 files changed, 72 insertions(+)
 create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsi.c
 create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
new file mode 100644
index 000000000000..9c90941688ed
--- /dev/null
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -0,0 +1,25 @@
+/** @file
+
+  This driver produces Extended SCSI Pass Thru Protocol instances for
+  LSI 53C895A SCSI devices.
+
+  Copyright (C) 2020, SUSE LLC.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiSpec.h>
+
+//
+// Entry point of this driver
+//
+EFI_STATUS
+EFIAPI
+LsiScsiEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
new file mode 100644
index 000000000000..8b6dccaff3eb
--- /dev/null
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -0,0 +1,26 @@
+## @file
+# This driver produces Extended SCSI Pass Thru Protocol instances for
+# LSI 53C895A SCSI devices.
+#
+# Copyright (C) 2020, SUSE LLC.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = LsiScsiDxe
+  FILE_GUID                      = EB4EB21F-5A3D-40BE-8BD2-F1B0E38E5744
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = LsiScsiEntryPoint
+
+[Sources]
+  LsiScsi.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index d391ee0a28df..c5a7ed59b072 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -49,6 +49,7 @@ [Defines]
   #
   DEFINE PVSCSI_ENABLE           = TRUE
   DEFINE MPT_SCSI_ENABLE         = TRUE
+  DEFINE LSI_SCSI_ENABLE         = FALSE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -722,6 +723,9 @@ [Components]
 !endif
 !if $(MPT_SCSI_ENABLE) == TRUE
   OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+!endif
+!if $(LSI_SCSI_ENABLE) == TRUE
+  OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
 !endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index b47d50276d13..0db81d3774d3 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -233,6 +233,9 @@ [FV.DXEFV]
 !if $(MPT_SCSI_ENABLE) == TRUE
 INF  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
 !endif
+!if $(LSI_SCSI_ENABLE) == TRUE
+INF  OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 2341d7f53a1c..1ae4a238e810 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -49,6 +49,7 @@ [Defines]
   #
   DEFINE PVSCSI_ENABLE           = TRUE
   DEFINE MPT_SCSI_ENABLE         = TRUE
+  DEFINE LSI_SCSI_ENABLE         = FALSE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -735,6 +736,9 @@ [Components.X64]
 !endif
 !if $(MPT_SCSI_ENABLE) == TRUE
   OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+!endif
+!if $(LSI_SCSI_ENABLE) == TRUE
+  OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
 !endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index af0f4c958841..47824f0d8127 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -234,6 +234,9 @@ [FV.DXEFV]
 !if $(MPT_SCSI_ENABLE) == TRUE
 INF  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
 !endif
+!if $(LSI_SCSI_ENABLE) == TRUE
+INF  OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 0036455d9c62..7e22d2599878 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -49,6 +49,7 @@ [Defines]
   #
   DEFINE PVSCSI_ENABLE           = TRUE
   DEFINE MPT_SCSI_ENABLE         = TRUE
+  DEFINE LSI_SCSI_ENABLE         = FALSE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -733,6 +734,9 @@ [Components]
 !endif
 !if $(MPT_SCSI_ENABLE) == TRUE
   OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+!endif
+!if $(LSI_SCSI_ENABLE) == TRUE
+  OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
 !endif
   MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
   MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index af0f4c958841..47824f0d8127 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -234,6 +234,9 @@ [FV.DXEFV]
 !if $(MPT_SCSI_ENABLE) == TRUE
 INF  OvmfPkg/MptScsiDxe/MptScsiDxe.inf
 !endif
+!if $(LSI_SCSI_ENABLE) == TRUE
+INF  OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+!endif
 
 !if $(SECURE_BOOT_ENABLE) == TRUE
   INF  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
-- 
2.27.0


From 49bdbdf1fb5a5f59d4c85e7f151cc524b19f11c7 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:21 +0800
Subject: [PATCH 35/43] OvmfPkg/LsiScsiDxe: Install the skeleton of driver
 binding

Implement the dummy functions for EFI Driver Binding protocol.

v2: Remove "STATIC" from LsiScsiControllerSupported()

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-3-glin@suse.com>
(cherry picked from commit 5e6b870a53f7c6eb38bdab27bcdae3e42b2a4ff2)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c      | 72 ++++++++++++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsi.h      | 49 +++++++++++++++++++++
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf |  2 +
 3 files changed, 122 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsi.h

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 9c90941688ed..79a2af4fee73 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -9,8 +9,71 @@
 
 **/
 
+#include <Library/UefiLib.h>
 #include <Uefi/UefiSpec.h>
 
+#include "LsiScsi.h"
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  )
+{
+  return EFI_SUCCESS;
+}
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC
+EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+  &LsiScsiControllerSupported,
+  &LsiScsiControllerStart,
+  &LsiScsiControllerStop,
+  0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
+  NULL, // ImageHandle, to be overwritten by
+        // EfiLibInstallDriverBindingComponentName2() in LsiScsiEntryPoint()
+  NULL  // DriverBindingHandle, ditto
+};
+
+
 //
 // Entry point of this driver
 //
@@ -21,5 +84,12 @@ LsiScsiEntryPoint (
   IN EFI_SYSTEM_TABLE *SystemTable
   )
 {
-  return EFI_UNSUPPORTED;
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gDriverBinding,
+           ImageHandle, // The handle to install onto
+           NULL, // TODO Component name
+           NULL  // TODO Component name
+           );
 }
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
new file mode 100644
index 000000000000..328bd289b8e8
--- /dev/null
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -0,0 +1,49 @@
+/** @file
+
+  Internal definitions for the LSI 53C895A SCSI driver, which produces
+  Extended SCSI Pass Thru Protocol instances for LSI 53C895A SCSI devices.
+
+  Copyright (C) 2020, SUSE LLC.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _LSI_SCSI_DXE_H_
+#define _LSI_SCSI_DXE_H_
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  );
+
+#endif // _LSI_SCSI_DXE_H_
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
index 8b6dccaff3eb..5cb15c456549 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -18,9 +18,11 @@ [Defines]
 
 [Sources]
   LsiScsi.c
+  LsiScsi.h
 
 [Packages]
   MdePkg/MdePkg.dec
 
 [LibraryClasses]
   UefiDriverEntryPoint
+  UefiLib
-- 
2.27.0


From 6e528eaa889cc6f90e7c62c32c6960f0d0a9106e Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:22 +0800
Subject: [PATCH 36/43] OvmfPkg/LsiScsiDxe: Report the name of the driver

Implement LsiScsiGetDriverName() and LsiScsiGetDeviceName()
to report the name of the driver.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-4-glin@suse.com>
(cherry picked from commit 386ca8abf74cd98ca9c0ef64b4427acd4e2e2431)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c | 69 ++++++++++++++++++++++++++++++++++--
 OvmfPkg/LsiScsiDxe/LsiScsi.h | 31 ++++++++++++++++
 2 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 79a2af4fee73..62daa3ab99bf 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -74,6 +74,71 @@ EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
 };
 
 
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+// Device type names ("LSI 53C895A SCSI Controller") are not formatted because
+// the driver supports only that device type. Therefore the driver name
+// suffices for unambiguous identification.
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+  { "eng;en", L"LSI 53C895A SCSI Controller Driver" },
+  { NULL,     NULL                                  }
+};
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gComponentName) // Iso639Language
+           );
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetDeviceName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  DeviceHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
+  &LsiScsiGetDriverName,
+  &LsiScsiGetDeviceName,
+  "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+STATIC
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &LsiScsiGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &LsiScsiGetDeviceName,
+  "en" // SupportedLanguages, RFC 4646 language codes
+};
+
 //
 // Entry point of this driver
 //
@@ -89,7 +154,7 @@ LsiScsiEntryPoint (
            SystemTable,
            &gDriverBinding,
            ImageHandle, // The handle to install onto
-           NULL, // TODO Component name
-           NULL  // TODO Component name
+           &gComponentName,
+           &gComponentName2
            );
 }
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 328bd289b8e8..6c8dcbd70a0a 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -46,4 +46,35 @@ LsiScsiControllerStop (
   IN EFI_HANDLE                  *ChildHandleBuffer
   );
 
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+// Device type names ("LSI 53C895A SCSI Controller") are not formatted because
+// the driver supports only that device type. Therefore the driver name
+// suffices for unambiguous identification.
+//
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetDeviceName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  DeviceHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  );
+
 #endif // _LSI_SCSI_DXE_H_
-- 
2.27.0


From efcf241a0def31de5ca9a6384401ac39da27113a Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:23 +0800
Subject: [PATCH 37/43] OvmfPkg/LsiScsiDxe: Probe PCI devices and look for
 LsiScsi

Implement LsiScsiControllerSupported() to probe the PCI ID and look for
LSI 53C895A.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-5-glin@suse.com>
(cherry picked from commit 79f802a50ee942e297c86138e6c19cab77a3a2eb)
---
 OvmfPkg/Include/IndustryStandard/LsiScsi.h | 20 +++++++++
 OvmfPkg/LsiScsiDxe/LsiScsi.c               | 48 +++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf          |  6 +++
 3 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/Include/IndustryStandard/LsiScsi.h

diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
new file mode 100644
index 000000000000..c09e864a1f39
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
@@ -0,0 +1,20 @@
+/** @file
+
+  Macros and type definitions for LSI 53C895A SCSI devices.
+
+  Copyright (C) 2020, SUSE LLC.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _LSI_SCSI_H_
+#define _LSI_SCSI_H_
+
+//
+// Device ID
+//
+#define LSI_LOGIC_PCI_VENDOR_ID   0x1000
+#define LSI_53C895A_PCI_DEVICE_ID 0x0012
+
+#endif // _LSI_SCSI_H_
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 62daa3ab99bf..5bca85bd75eb 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -9,7 +9,12 @@
 
 **/
 
+#include <IndustryStandard/LsiScsi.h>
+#include <IndustryStandard/Pci.h>
+#include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/PciRootBridgeIo.h>
 #include <Uefi/UefiSpec.h>
 
 #include "LsiScsi.h"
@@ -31,7 +36,48 @@ LsiScsiControllerSupported (
   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
   )
 {
-  return EFI_UNSUPPORTED;
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&PciIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        0,
+                        sizeof (Pci) / sizeof (UINT32),
+                        &Pci
+                        );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID &&
+      Pci.Hdr.DeviceId == LSI_53C895A_PCI_DEVICE_ID) {
+    Status = EFI_SUCCESS;
+  } else {
+    Status = EFI_UNSUPPORTED;
+  }
+
+Done:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+  return Status;
 }
 
 EFI_STATUS
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
index 5cb15c456549..7ce11fcc6a03 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -22,7 +22,13 @@ [Sources]
 
 [Packages]
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  BaseLib
+  UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
+
+[Protocols]
+  gEfiPciIoProtocolGuid                  ## TO_START
-- 
2.27.0


From 8e0389bf8a719a0ca410a8fef5e2f84666454351 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:24 +0800
Subject: [PATCH 38/43] OvmfPkg/LsiScsiDxe: Install stubbed EXT_SCSI_PASS_THRU

Partially implement LsiScsiControllerStart() and LsiScsiControllerStop()
to insert the scaffolding of EXT_SCSI_PASS_THRU functions.

v3: Squash the newline below the declaration of LSI_SCSI_FROM_PASS_THRU
v2: Remove the closing of PciIo protocol from LsiScsiControllerStop().

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-6-glin@suse.com>
(cherry picked from commit 23d982e20528261b0bb3a704131d78d7e05bc59d)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c      | 164 +++++++++++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsi.h      |  78 ++++++++++++++
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf |   3 +
 3 files changed, 244 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 5bca85bd75eb..67fadb411e85 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -11,14 +11,105 @@
 
 #include <IndustryStandard/LsiScsi.h>
 #include <IndustryStandard/Pci.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
 #include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/ScsiPassThruExt.h>
 #include <Uefi/UefiSpec.h>
 
 #include "LsiScsi.h"
 
+//
+// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
+// for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,
+// sections
+// - 14.1 SCSI Driver Model Overview,
+// - 14.7 Extended SCSI Pass Thru Protocol.
+//
+
+EFI_STATUS
+EFIAPI
+LsiScsiPassThru (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+  IN EFI_EVENT                                      Event     OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetNextTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT8                       **TargetPointer,
+  IN OUT UINT64                      *Lun
+  )
+{
+  return EFI_NOT_FOUND;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiBuildDevicePath (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN UINT8                           *Target,
+  IN UINT64                          Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL    **DevicePath
+  )
+{
+  return EFI_NOT_FOUND;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath,
+  OUT UINT8                           **TargetPointer,
+  OUT UINT64                          *Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiResetChannel (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiResetTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN UINT8                           *Target,
+  IN UINT64                          Lun
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetNextTarget (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT8                       **TargetPointer
+  )
+{
+  return EFI_NOT_FOUND;
+}
+
 //
 // Probe, start and stop functions of this driver, called by the DXE core for
 // specific devices.
@@ -88,7 +179,49 @@ LsiScsiControllerStart (
   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
   )
 {
+  EFI_STATUS           Status;
+  LSI_SCSI_DEV         *Dev;
+
+  Dev = AllocateZeroPool (sizeof (*Dev));
+  if (Dev == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Dev->Signature = LSI_SCSI_DEV_SIGNATURE;
+
+  //
+  // Host adapter channel, doesn't exist
+  //
+  Dev->PassThruMode.AdapterId = MAX_UINT32;
+  Dev->PassThruMode.Attributes =
+    EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
+    EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
+
+  Dev->PassThru.Mode = &Dev->PassThruMode;
+  Dev->PassThru.PassThru = &LsiScsiPassThru;
+  Dev->PassThru.GetNextTargetLun = &LsiScsiGetNextTargetLun;
+  Dev->PassThru.BuildDevicePath = &LsiScsiBuildDevicePath;
+  Dev->PassThru.GetTargetLun = &LsiScsiGetTargetLun;
+  Dev->PassThru.ResetChannel = &LsiScsiResetChannel;
+  Dev->PassThru.ResetTargetLun = &LsiScsiResetTargetLun;
+  Dev->PassThru.GetNextTarget = &LsiScsiGetNextTarget;
+
+  Status = gBS->InstallProtocolInterface (
+                  &ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Dev->PassThru
+                  );
+  if (EFI_ERROR (Status)) {
+    goto FreePool;
+  }
+
   return EFI_SUCCESS;
+
+FreePool:
+  FreePool (Dev);
+
+  return Status;
 }
 
 EFI_STATUS
@@ -100,7 +233,36 @@ LsiScsiControllerStop (
   IN EFI_HANDLE                  *ChildHandleBuffer
   )
 {
-  return EFI_SUCCESS;
+  EFI_STATUS                      Status;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+  LSI_SCSI_DEV                    *Dev;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  (VOID **)&PassThru,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Dev = LSI_SCSI_FROM_PASS_THRU (PassThru);
+
+  Status = gBS->UninstallProtocolInterface (
+                  ControllerHandle,
+                  &gEfiExtScsiPassThruProtocolGuid,
+                  &Dev->PassThru
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  FreePool (Dev);
+
+  return Status;
 }
 
 //
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 6c8dcbd70a0a..751d5b193b51 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -12,6 +12,18 @@
 #ifndef _LSI_SCSI_DXE_H_
 #define _LSI_SCSI_DXE_H_
 
+typedef struct {
+  UINT32                          Signature;
+  EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
+} LSI_SCSI_DEV;
+
+#define LSI_SCSI_DEV_SIGNATURE SIGNATURE_32 ('L','S','I','S')
+
+#define LSI_SCSI_FROM_PASS_THRU(PassThruPtr) \
+  CR (PassThruPtr, LSI_SCSI_DEV, PassThru, LSI_SCSI_DEV_SIGNATURE)
+
+
 //
 // Probe, start and stop functions of this driver, called by the DXE core for
 // specific devices.
@@ -47,6 +59,72 @@ LsiScsiControllerStop (
   );
 
 
+//
+// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
+// for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,
+// sections
+// - 14.1 SCSI Driver Model Overview,
+// - 14.7 Extended SCSI Pass Thru Protocol.
+//
+
+EFI_STATUS
+EFIAPI
+LsiScsiPassThru (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
+  IN UINT8                                          *Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+  IN EFI_EVENT                                      Event     OPTIONAL
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetNextTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT8                       **TargetPointer,
+  IN OUT UINT64                      *Lun
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiBuildDevicePath (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN UINT8                           *Target,
+  IN UINT64                          Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL    **DevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath,
+  OUT UINT8                           **TargetPointer,
+  OUT UINT64                          *Lun
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiResetChannel (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiResetTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN UINT8                           *Target,
+  IN UINT64                          Lun
+  );
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetNextTarget (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT8                       **TargetPointer
+  );
+
+
 //
 // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
 // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
index 7ce11fcc6a03..14f9c68308cc 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -26,9 +26,12 @@ [Packages]
 
 [LibraryClasses]
   BaseLib
+  BaseMemoryLib
+  MemoryAllocationLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
 
 [Protocols]
+  gEfiExtScsiPassThruProtocolGuid        ## BY_START
   gEfiPciIoProtocolGuid                  ## TO_START
-- 
2.27.0


From 98072462fce37b9a1079f1427fa512ad93e91f88 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:25 +0800
Subject: [PATCH 39/43] OvmfPkg/LsiScsiDxe: Report Targets and LUNs

Implement LsiScsiGetNextTargetLun(), LsiScsiBuildDevicePath(),
LsiScsiGetTargetLun(), and LsiScsiGetNextTarget() to report Targets and
LUNs and build the device path.

This commit also introduces two PCD value: PcdLsiScsiMaxTargetLimit and
PcdLsiScsiMaxLunLimit as the limits for Targets and LUNs.

v3:
  - Update the range of LUN in the assertioin
  - Squash the spurious newline into the previous commit
v2:
  - Zero out (*Target) in LsiScsiGetTargetLun()
  - Use CopyMem() instead of the one-byte shortcut to copy target from
    ScsiDevicePath->Pun
  - Add asserts for PcdLsiScsiMaxTargetLimit and PcdLsiScsiMaxLunLimit

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Message-Id: <20200717061130.8881-7-glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 12d99b8f2367c2fd1ca9b3165acd52a0c9dcc99e)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c      | 148 +++++++++++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsi.h      |   2 +
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf |   6 ++
 OvmfPkg/OvmfPkg.dec               |   5 +
 4 files changed, 159 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 67fadb411e85..172779240636 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -15,6 +15,7 @@
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
 #include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Protocol/PciIo.h>
@@ -53,6 +54,49 @@ LsiScsiGetNextTargetLun (
   IN OUT UINT64                      *Lun
   )
 {
+  LSI_SCSI_DEV *Dev;
+  UINTN        Idx;
+  UINT8        *Target;
+  UINT16       LastTarget;
+
+  //
+  // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
+  //
+  Target = *TargetPointer;
+
+  //
+  // Search for first non-0xFF byte. If not found, return first target & LUN.
+  //
+  for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
+    ;
+  if (Idx == TARGET_MAX_BYTES) {
+    SetMem (Target, TARGET_MAX_BYTES, 0x00);
+    *Lun = 0;
+    return EFI_SUCCESS;
+  }
+
+  CopyMem (&LastTarget, Target, sizeof LastTarget);
+
+  //
+  // increment (target, LUN) pair if valid on input
+  //
+  Dev = LSI_SCSI_FROM_PASS_THRU (This);
+  if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (*Lun < Dev->MaxLun) {
+    ++*Lun;
+    return EFI_SUCCESS;
+  }
+
+  if (LastTarget < Dev->MaxTarget) {
+    *Lun = 0;
+    ++LastTarget;
+    CopyMem (Target, &LastTarget, sizeof LastTarget);
+    return EFI_SUCCESS;
+  }
+
   return EFI_NOT_FOUND;
 }
 
@@ -65,7 +109,34 @@ LsiScsiBuildDevicePath (
   IN OUT EFI_DEVICE_PATH_PROTOCOL    **DevicePath
   )
 {
-  return EFI_NOT_FOUND;
+  UINT16           TargetValue;
+  LSI_SCSI_DEV     *Dev;
+  SCSI_DEVICE_PATH *ScsiDevicePath;
+
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyMem (&TargetValue, Target, sizeof TargetValue);
+  Dev = LSI_SCSI_FROM_PASS_THRU (This);
+  if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun || Lun > 0xFFFF) {
+    return EFI_NOT_FOUND;
+  }
+
+  ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);
+  if (ScsiDevicePath == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ScsiDevicePath->Header.Type      = MESSAGING_DEVICE_PATH;
+  ScsiDevicePath->Header.SubType   = MSG_SCSI_DP;
+  ScsiDevicePath->Header.Length[0] = (UINT8)  sizeof *ScsiDevicePath;
+  ScsiDevicePath->Header.Length[1] = (UINT8) (sizeof *ScsiDevicePath >> 8);
+  ScsiDevicePath->Pun              = TargetValue;
+  ScsiDevicePath->Lun              = (UINT16) Lun;
+
+  *DevicePath = &ScsiDevicePath->Header;
+  return EFI_SUCCESS;
 }
 
 EFI_STATUS
@@ -77,7 +148,33 @@ LsiScsiGetTargetLun (
   OUT UINT64                          *Lun
   )
 {
-  return EFI_UNSUPPORTED;
+  SCSI_DEVICE_PATH *ScsiDevicePath;
+  LSI_SCSI_DEV     *Dev;
+  UINT8            *Target;
+
+  if (DevicePath == NULL || TargetPointer == NULL || *TargetPointer == NULL ||
+      Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DevicePath->Type    != MESSAGING_DEVICE_PATH ||
+      DevicePath->SubType != MSG_SCSI_DP) {
+    return EFI_UNSUPPORTED;
+  }
+
+  ScsiDevicePath = (SCSI_DEVICE_PATH *) DevicePath;
+  Dev = LSI_SCSI_FROM_PASS_THRU (This);
+  if (ScsiDevicePath->Pun > Dev->MaxTarget ||
+      ScsiDevicePath->Lun > Dev->MaxLun) {
+    return EFI_NOT_FOUND;
+  }
+
+  Target = *TargetPointer;
+  ZeroMem (Target, TARGET_MAX_BYTES);
+  CopyMem (Target, &ScsiDevicePath->Pun, sizeof ScsiDevicePath->Pun);
+  *Lun = ScsiDevicePath->Lun;
+
+  return EFI_SUCCESS;
 }
 
 EFI_STATUS
@@ -107,6 +204,42 @@ LsiScsiGetNextTarget (
   IN OUT UINT8                       **TargetPointer
   )
 {
+  LSI_SCSI_DEV *Dev;
+  UINTN        Idx;
+  UINT8        *Target;
+  UINT16       LastTarget;
+
+  //
+  // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
+  //
+  Target = *TargetPointer;
+
+  //
+  // Search for first non-0xFF byte. If not found, return first target.
+  //
+  for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
+    ;
+  if (Idx == TARGET_MAX_BYTES) {
+    SetMem (Target, TARGET_MAX_BYTES, 0x00);
+    return EFI_SUCCESS;
+  }
+
+  CopyMem (&LastTarget, Target, sizeof LastTarget);
+
+  //
+  // increment target if valid on input
+  //
+  Dev = LSI_SCSI_FROM_PASS_THRU (This);
+  if (LastTarget > Dev->MaxTarget) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (LastTarget < Dev->MaxTarget) {
+    ++LastTarget;
+    CopyMem (Target, &LastTarget, sizeof LastTarget);
+    return EFI_SUCCESS;
+  }
+
   return EFI_NOT_FOUND;
 }
 
@@ -189,6 +322,17 @@ LsiScsiControllerStart (
 
   Dev->Signature = LSI_SCSI_DEV_SIGNATURE;
 
+  STATIC_ASSERT (
+    FixedPcdGet8 (PcdLsiScsiMaxTargetLimit) < 8,
+    "LSI 53C895A supports targets [0..7]"
+    );
+  STATIC_ASSERT (
+    FixedPcdGet8 (PcdLsiScsiMaxLunLimit) < 128,
+    "LSI 53C895A supports LUNs [0..127]"
+    );
+  Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit);
+  Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit);
+
   //
   // Host adapter channel, doesn't exist
   //
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 751d5b193b51..6c6ed25f1c33 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -14,6 +14,8 @@
 
 typedef struct {
   UINT32                          Signature;
+  UINT8                           MaxTarget;
+  UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
 } LSI_SCSI_DEV;
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
index 14f9c68308cc..6111449577a8 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -27,7 +27,9 @@ [Packages]
 [LibraryClasses]
   BaseLib
   BaseMemoryLib
+  DebugLib
   MemoryAllocationLib
+  PcdLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
@@ -35,3 +37,7 @@ [LibraryClasses]
 [Protocols]
   gEfiExtScsiPassThruProtocolGuid        ## BY_START
   gEfiPciIoProtocolGuid                  ## TO_START
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit   ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit      ## CONSUMES
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index e86a3f86c106..30f9f046a296 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -143,6 +143,11 @@ [PcdsFixedAtBuild]
   ## Microseconds to stall between polling for MptScsi request result
   gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x3a
 
+  ## Set the *inclusive* number of targets and LUNs that LsiScsi exposes for
+  #  scan by ScsiBusDxe.
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit|7|UINT8|0x3b
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit|0|UINT8|0x3c
+
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa
-- 
2.27.0


From 2d207ac82ba5022971715b786ad9ccb32fc2d205 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:26 +0800
Subject: [PATCH 40/43] OvmfPkg/LsiScsiDxe: Open PciIo protocol and initialize
 the device

Open PciIo protocol and use it to initialize the device. The
initialization of LSI 53C895A is simple: just set the SRST bit in
Interrupt Status Zero register to reset the device.

v2:
  - Use the BITx macros for the bit constants
  - Add the closing of PciIo protocol in LsiScsiControllerStop()

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Message-Id: <20200717061130.8881-8-glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 8d6193902f2da15c8bad9941bfe493cca903a3ca)
---
 OvmfPkg/Include/IndustryStandard/LsiScsi.h |  21 ++++
 OvmfPkg/LsiScsiDxe/LsiScsi.c               | 136 ++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsi.h               |   3 +
 3 files changed, 159 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
index c09e864a1f39..185e553c8eb4 100644
--- a/OvmfPkg/Include/IndustryStandard/LsiScsi.h
+++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
@@ -17,4 +17,25 @@
 #define LSI_LOGIC_PCI_VENDOR_ID   0x1000
 #define LSI_53C895A_PCI_DEVICE_ID 0x0012
 
+//
+// LSI 53C895A Registers
+//
+#define LSI_REG_DSTAT             0x0C
+#define LSI_REG_ISTAT0            0x14
+#define LSI_REG_DSP               0x2C
+#define LSI_REG_SIST0             0x42
+#define LSI_REG_SIST1             0x43
+
+//
+// The status bits for Interrupt Status Zero (ISTAT0)
+//
+#define LSI_ISTAT0_DIP            BIT0
+#define LSI_ISTAT0_SIP            BIT1
+#define LSI_ISTAT0_INTF           BIT2
+#define LSI_ISTAT0_CON            BIT3
+#define LSI_ISTAT0_SEM            BIT4
+#define LSI_ISTAT0_SIGP           BIT5
+#define LSI_ISTAT0_SRST           BIT6
+#define LSI_ISTAT0_ABRT           BIT7
+
 #endif // _LSI_SCSI_H_
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 172779240636..a9c107c04ef1 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -25,6 +25,33 @@
 
 #include "LsiScsi.h"
 
+STATIC
+EFI_STATUS
+Out8 (
+  IN LSI_SCSI_DEV *Dev,
+  IN UINT32       Addr,
+  IN UINT8        Data
+  )
+{
+  return Dev->PciIo->Io.Write (
+                          Dev->PciIo,
+                          EfiPciIoWidthUint8,
+                          PCI_BAR_IDX0,
+                          Addr,
+                          1,
+                          &Data
+                          );
+}
+
+STATIC
+EFI_STATUS
+LsiScsiReset (
+  IN LSI_SCSI_DEV *Dev
+  )
+{
+  return Out8 (Dev, LSI_REG_ISTAT0, LSI_ISTAT0_SRST);
+}
+
 //
 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
 // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,
@@ -243,6 +270,21 @@ LsiScsiGetNextTarget (
   return EFI_NOT_FOUND;
 }
 
+STATIC
+VOID
+EFIAPI
+LsiScsiExitBoot (
+  IN  EFI_EVENT Event,
+  IN  VOID      *Context
+  )
+{
+  LSI_SCSI_DEV *Dev;
+
+  Dev = Context;
+  DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
+  LsiScsiReset (Dev);
+}
+
 //
 // Probe, start and stop functions of this driver, called by the DXE core for
 // specific devices.
@@ -333,6 +375,58 @@ LsiScsiControllerStart (
   Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit);
   Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit);
 
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&Dev->PciIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto FreePool;
+  }
+
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationGet,
+                         0,
+                         &Dev->OrigPciAttrs
+                         );
+  if (EFI_ERROR (Status)) {
+    goto CloseProtocol;
+  }
+
+  //
+  // Enable I/O Space & Bus-Mastering
+  //
+  Status = Dev->PciIo->Attributes (
+                         Dev->PciIo,
+                         EfiPciIoAttributeOperationEnable,
+                         (EFI_PCI_IO_ATTRIBUTE_IO |
+                          EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
+                         NULL
+                         );
+  if (EFI_ERROR (Status)) {
+    goto CloseProtocol;
+  }
+
+  Status = LsiScsiReset (Dev);
+  if (EFI_ERROR (Status)) {
+    goto RestoreAttributes;
+  }
+
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                  TPL_CALLBACK,
+                  &LsiScsiExitBoot,
+                  Dev,
+                  &Dev->ExitBoot
+                  );
+  if (EFI_ERROR (Status)) {
+    goto UninitDev;
+  }
+
   //
   // Host adapter channel, doesn't exist
   //
@@ -357,11 +451,33 @@ LsiScsiControllerStart (
                   &Dev->PassThru
                   );
   if (EFI_ERROR (Status)) {
-    goto FreePool;
+    goto CloseExitBoot;
   }
 
   return EFI_SUCCESS;
 
+CloseExitBoot:
+  gBS->CloseEvent (Dev->ExitBoot);
+
+UninitDev:
+  LsiScsiReset (Dev);
+
+RestoreAttributes:
+  Dev->PciIo->Attributes (
+                Dev->PciIo,
+                EfiPciIoAttributeOperationSet,
+                Dev->OrigPciAttrs,
+                NULL
+                );
+
+CloseProtocol:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
 FreePool:
   FreePool (Dev);
 
@@ -404,6 +520,24 @@ LsiScsiControllerStop (
     return Status;
   }
 
+  gBS->CloseEvent (Dev->ExitBoot);
+
+  LsiScsiReset (Dev);
+
+  Dev->PciIo->Attributes (
+                Dev->PciIo,
+                EfiPciIoAttributeOperationSet,
+                Dev->OrigPciAttrs,
+                NULL
+                );
+
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
   FreePool (Dev);
 
   return Status;
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 6c6ed25f1c33..8c2acff6e86f 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -14,6 +14,9 @@
 
 typedef struct {
   UINT32                          Signature;
+  UINT64                          OrigPciAttrs;
+  EFI_EVENT                       ExitBoot;
+  EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
-- 
2.27.0


From 88aabab1fbb8375fc288d600462292de5f4b5f60 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:27 +0800
Subject: [PATCH 41/43] OvmfPkg/LsiScsiDxe: Map DMA buffer

Map DMA buffer and perpare for the implementation of LsiScsiPassThru().

v2:
  - Replace 0x10000 with SIZE_64KB macro for the DMA buffer data array
  - Remove DUAL_ADDRESS_CYCLE from PciIo since we don't really need
    64-bit DMA address

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-9-glin@suse.com>
(cherry picked from commit f1d6c1eba1b76e57568c8d7c947f60041939f1f9)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c | 62 +++++++++++++++++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsi.h | 14 ++++++++
 2 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index a9c107c04ef1..52c224aad9a5 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -356,6 +356,8 @@ LsiScsiControllerStart (
 {
   EFI_STATUS           Status;
   LSI_SCSI_DEV         *Dev;
+  UINTN                Pages;
+  UINTN                BytesMapped;
 
   Dev = AllocateZeroPool (sizeof (*Dev));
   if (Dev == NULL) {
@@ -411,11 +413,45 @@ LsiScsiControllerStart (
     goto CloseProtocol;
   }
 
-  Status = LsiScsiReset (Dev);
+  //
+  // Create buffers for data transfer
+  //
+  Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma));
+  Status = Dev->PciIo->AllocateBuffer (
+                         Dev->PciIo,
+                         AllocateAnyPages,
+                         EfiBootServicesData,
+                         Pages,
+                         (VOID **)&Dev->Dma,
+                         EFI_PCI_ATTRIBUTE_MEMORY_CACHED
+                         );
   if (EFI_ERROR (Status)) {
     goto RestoreAttributes;
   }
 
+  BytesMapped = EFI_PAGES_TO_SIZE (Pages);
+  Status = Dev->PciIo->Map (
+                         Dev->PciIo,
+                         EfiPciIoOperationBusMasterCommonBuffer,
+                         Dev->Dma,
+                         &BytesMapped,
+                         &Dev->DmaPhysical,
+                         &Dev->DmaMapping
+                         );
+  if (EFI_ERROR (Status)) {
+    goto FreeBuffer;
+  }
+
+  if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Unmap;
+  }
+
+  Status = LsiScsiReset (Dev);
+  if (EFI_ERROR (Status)) {
+    goto Unmap;
+  }
+
   Status = gBS->CreateEvent (
                   EVT_SIGNAL_EXIT_BOOT_SERVICES,
                   TPL_CALLBACK,
@@ -462,6 +498,19 @@ CloseExitBoot:
 UninitDev:
   LsiScsiReset (Dev);
 
+Unmap:
+  Dev->PciIo->Unmap (
+                Dev->PciIo,
+                Dev->DmaMapping
+                );
+
+FreeBuffer:
+  Dev->PciIo->FreeBuffer (
+                Dev->PciIo,
+                Pages,
+                Dev->Dma
+                );
+
 RestoreAttributes:
   Dev->PciIo->Attributes (
                 Dev->PciIo,
@@ -524,6 +573,17 @@ LsiScsiControllerStop (
 
   LsiScsiReset (Dev);
 
+  Dev->PciIo->Unmap (
+                Dev->PciIo,
+                Dev->DmaMapping
+                );
+
+  Dev->PciIo->FreeBuffer (
+                Dev->PciIo,
+                EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)),
+                Dev->Dma
+                );
+
   Dev->PciIo->Attributes (
                 Dev->PciIo,
                 EfiPciIoAttributeOperationSet,
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 8c2acff6e86f..9f9e5c7fed00 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -12,6 +12,17 @@
 #ifndef _LSI_SCSI_DXE_H_
 #define _LSI_SCSI_DXE_H_
 
+typedef struct {
+  //
+  // Allocate 64KB for read/write buffer. It seems sufficient for the common
+  // boot scenarios.
+  //
+  // NOTE: The number of bytes for data transmission is bounded by DMA Byte
+  //       Count (DBC), a 24-bit register, so the maximum is 0xFFFFFF (16MB-1).
+  //
+  UINT8                           Data[SIZE_64KB];
+} LSI_SCSI_DMA_BUFFER;
+
 typedef struct {
   UINT32                          Signature;
   UINT64                          OrigPciAttrs;
@@ -19,6 +30,9 @@ typedef struct {
   EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
+  LSI_SCSI_DMA_BUFFER             *Dma;
+  EFI_PHYSICAL_ADDRESS            DmaPhysical;
+  VOID                            *DmaMapping;
   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
 } LSI_SCSI_DEV;
-- 
2.27.0


From 91b167b9b7ad67cc6129ba610e2c2d39518f1d14 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:28 +0800
Subject: [PATCH 42/43] OvmfPkg/LsiScsiDxe: Examine the incoming SCSI Request
 Packet

This is the first part of LsiScsiPassThru(). Before processing the SCSI
Request packet, we have to make sure whether the packet is valid or not.

v2: Make LsiScsiPassThru() return EFI_UNSUPPORTED since this function is
    half-implemented

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200717061130.8881-10-glin@suse.com>
(cherry picked from commit 97e60818b64495db562dc456a1fb31980898f4c3)
---
 OvmfPkg/LsiScsiDxe/LsiScsi.c | 98 ++++++++++++++++++++++++++++++++++++
 OvmfPkg/LsiScsiDxe/LsiScsi.h |  4 ++
 2 files changed, 102 insertions(+)

diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 52c224aad9a5..9859632e02d6 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -52,6 +52,95 @@ LsiScsiReset (
   return Out8 (Dev, LSI_REG_ISTAT0, LSI_ISTAT0_SRST);
 }
 
+STATIC
+EFI_STATUS
+ReportHostAdapterOverrunError (
+  OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  Packet->SenseDataLength = 0;
+  Packet->HostAdapterStatus =
+            EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+  Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+  return EFI_BAD_BUFFER_SIZE;
+}
+
+/**
+
+  Check the request packet from the Extended SCSI Pass Thru Protocol. The
+  request packet is modified, to be forwarded outwards by LsiScsiPassThru(),
+  if invalid or unsupported parameters are detected.
+
+  @param[in] Dev          The LSI 53C895A SCSI device the packet targets.
+
+  @param[in] Target       The SCSI target controlled by the LSI 53C895A SCSI
+                          device.
+
+  @param[in] Lun          The Logical Unit Number under the SCSI target.
+
+  @param[in out] Packet   The Extended SCSI Pass Thru Protocol packet.
+
+
+  @retval EFI_SUCCESS  The Extended SCSI Pass Thru Protocol packet was valid.
+
+  @return              Otherwise, invalid or unsupported parameters were
+                       detected. Status codes are meant for direct forwarding
+                       by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
+                       implementation.
+
+ **/
+STATIC
+EFI_STATUS
+LsiScsiCheckRequest (
+  IN LSI_SCSI_DEV                                   *Dev,
+  IN UINT8                                          Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  if (Target > Dev->MaxTarget || Lun > Dev->MaxLun ||
+      Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
+      //
+      // Trying to receive, but destination pointer is NULL, or contradicting
+      // transfer direction
+      //
+      (Packet->InTransferLength > 0 &&
+       (Packet->InDataBuffer == NULL ||
+        Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE
+         )
+        ) ||
+
+      //
+      // Trying to send, but source pointer is NULL, or contradicting transfer
+      // direction
+      //
+      (Packet->OutTransferLength > 0 &&
+       (Packet->OutDataBuffer == NULL ||
+        Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ
+         )
+        )
+    ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
+      (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) ||
+      Packet->CdbLength > sizeof Dev->Dma->Cdb) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (Packet->InTransferLength > sizeof Dev->Dma->Data) {
+    Packet->InTransferLength = sizeof Dev->Dma->Data;
+    return ReportHostAdapterOverrunError (Packet);
+  }
+  if (Packet->OutTransferLength > sizeof Dev->Dma->Data) {
+    Packet->OutTransferLength = sizeof Dev->Dma->Data;
+    return ReportHostAdapterOverrunError (Packet);
+  }
+
+  return EFI_SUCCESS;
+}
+
 //
 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
 // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,
@@ -70,6 +159,15 @@ LsiScsiPassThru (
   IN EFI_EVENT                                      Event     OPTIONAL
   )
 {
+  EFI_STATUS   Status;
+  LSI_SCSI_DEV *Dev;
+
+  Dev = LSI_SCSI_FROM_PASS_THRU (This);
+  Status = LsiScsiCheckRequest (Dev, *Target, Lun, Packet);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
   return EFI_UNSUPPORTED;
 }
 
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 9f9e5c7fed00..05deeed379fe 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -13,6 +13,10 @@
 #define _LSI_SCSI_DXE_H_
 
 typedef struct {
+  //
+  // The max size of CDB is 32.
+  //
+  UINT8                           Cdb[32];
   //
   // Allocate 64KB for read/write buffer. It seems sufficient for the common
   // boot scenarios.
-- 
2.27.0


From 8db13bac6cde898fa87b84ee1ca96d2295b3501c Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Fri, 17 Jul 2020 14:11:29 +0800
Subject: [PATCH 43/43] OvmfPkg/LsiScsiDxe: Process the SCSI Request Packet

This is the second part of LsiScsiPassThru(). LsiScsiProcessRequest() is
added to translate the SCSI Request Packet into the LSI 53C895A
commands. This function utilizes the so-called Script buffer to transmit
a series of commands to the chip and then polls the DMA Status (DSTAT)
register until the Scripts Interrupt Instruction Received (SIR) bit
sets. Once the script is done, the SCSI Request Packet will be modified
to reflect the result of the script. The Cumulative SCSI Byte Count
(CSBC) register is fetched before and after the script to calculate the
transferred bytes and update InTransferLength/OutTransferLength if
necessary.

v3:
  - Set DStat, SIst0, and SIst1 to 0 before using them
  - Amend the if statements for the DMA data instruction and add the
    assertions for the data direction
  - Also set SenseDataLength to 0 on the error path
  - Fix typos and amend comments
  - Amend the error handling of the calculation of transferred bytes
v2:
  - Use the BITx macros for the most of LSI_* constants
  - Fix a typo: contorller => controller
  - Add SeaBIOS lsi-scsi driver as one of the references of the script
  - Cast the result of sizeof to UINT32 for the instructions of the
    script
  - Drop the backslashes
  - Replace LSI_SCSI_DMA_ADDR_LOW with LSI_SCSI_DMA_ADDR since we
    already removed DUAL_ADDRESS_CYCLE
  - Add more comments for the script
  - Fix the check of the script size at the end of the script
  - Always set SenseDataLength to 0 to avoid the caller to access
    SenseData
  - Improve the error handling in LsiScsiProcessRequest()

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Gary Lin <glin@suse.com>
Message-Id: <20200717061130.8881-11-glin@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit 31830b07020c9796579d5b08dd642d19bce7d4f5)
---
 OvmfPkg/Include/IndustryStandard/LsiScsi.h |  64 ++++
 OvmfPkg/LsiScsiDxe/LsiScsi.c               | 408 ++++++++++++++++++++-
 OvmfPkg/LsiScsiDxe/LsiScsi.h               |  21 ++
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf          |   3 +
 OvmfPkg/OvmfPkg.dec                        |   3 +
 5 files changed, 498 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
index 185e553c8eb4..01d75323cdbc 100644
--- a/OvmfPkg/Include/IndustryStandard/LsiScsi.h
+++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
@@ -25,6 +25,19 @@
 #define LSI_REG_DSP               0x2C
 #define LSI_REG_SIST0             0x42
 #define LSI_REG_SIST1             0x43
+#define LSI_REG_CSBC              0xDC
+
+//
+// The status bits for DMA Status (DSTAT)
+//
+#define LSI_DSTAT_IID             BIT0
+#define LSI_DSTAT_R               BIT1
+#define LSI_DSTAT_SIR             BIT2
+#define LSI_DSTAT_SSI             BIT3
+#define LSI_DSTAT_ABRT            BIT4
+#define LSI_DSTAT_BF              BIT5
+#define LSI_DSTAT_MDPE            BIT6
+#define LSI_DSTAT_DFE             BIT7
 
 //
 // The status bits for Interrupt Status Zero (ISTAT0)
@@ -38,4 +51,55 @@
 #define LSI_ISTAT0_SRST           BIT6
 #define LSI_ISTAT0_ABRT           BIT7
 
+//
+// The status bits for SCSI Interrupt Status Zero (SIST0)
+//
+#define LSI_SIST0_PAR             BIT0
+#define LSI_SIST0_RST             BIT1
+#define LSI_SIST0_UDC             BIT2
+#define LSI_SIST0_SGE             BIT3
+#define LSI_SIST0_RSL             BIT4
+#define LSI_SIST0_SEL             BIT5
+#define LSI_SIST0_CMP             BIT6
+#define LSI_SIST0_MA              BIT7
+
+//
+// The status bits for SCSI Interrupt Status One (SIST1)
+//
+#define LSI_SIST1_HTH             BIT0
+#define LSI_SIST1_GEN             BIT1
+#define LSI_SIST1_STO             BIT2
+#define LSI_SIST1_R3              BIT3
+#define LSI_SIST1_SBMC            BIT4
+#define LSI_SIST1_R5              BIT5
+#define LSI_SIST1_R6              BIT6
+#define LSI_SIST1_R7              BIT7
+
+//
+// LSI 53C895A Script Instructions
+//
+#define LSI_INS_TYPE_BLK          0x00000000
+#define LSI_INS_TYPE_IO           BIT30
+#define LSI_INS_TYPE_TC           BIT31
+
+#define LSI_INS_BLK_SCSIP_DAT_OUT 0x00000000
+#define LSI_INS_BLK_SCSIP_DAT_IN  BIT24
+#define LSI_INS_BLK_SCSIP_CMD     BIT25
+#define LSI_INS_BLK_SCSIP_STAT    (BIT24 | BIT25)
+#define LSI_INS_BLK_SCSIP_MSG_OUT (BIT25 | BIT26)
+#define LSI_INS_BLK_SCSIP_MSG_IN  (BIT24 | BIT25 | BIT26)
+
+#define LSI_INS_IO_OPC_SEL        0x00000000
+#define LSI_INS_IO_OPC_WAIT_RESEL BIT28
+
+#define LSI_INS_TC_CP             BIT17
+#define LSI_INS_TC_JMP            BIT19
+#define LSI_INS_TC_RA             BIT23
+
+#define LSI_INS_TC_OPC_JMP        0x00000000
+#define LSI_INS_TC_OPC_INT        (BIT27 | BIT28)
+
+#define LSI_INS_TC_SCSIP_DAT_OUT  0x00000000
+#define LSI_INS_TC_SCSIP_MSG_IN   (BIT24 | BIT25 | BIT26)
+
 #endif // _LSI_SCSI_H_
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
index 9859632e02d6..ed5fc3bfdd9d 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.c
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -43,6 +43,60 @@ Out8 (
                           );
 }
 
+STATIC
+EFI_STATUS
+Out32 (
+  IN LSI_SCSI_DEV       *Dev,
+  IN UINT32             Addr,
+  IN UINT32             Data
+  )
+{
+  return Dev->PciIo->Io.Write (
+                          Dev->PciIo,
+                          EfiPciIoWidthUint32,
+                          PCI_BAR_IDX0,
+                          Addr,
+                          1,
+                          &Data
+                          );
+}
+
+STATIC
+EFI_STATUS
+In8 (
+  IN  LSI_SCSI_DEV *Dev,
+  IN  UINT32       Addr,
+  OUT UINT8        *Data
+  )
+{
+  return Dev->PciIo->Io.Read (
+                          Dev->PciIo,
+                          EfiPciIoWidthUint8,
+                          PCI_BAR_IDX0,
+                          Addr,
+                          1,
+                          Data
+                          );
+}
+
+STATIC
+EFI_STATUS
+In32 (
+  IN  LSI_SCSI_DEV *Dev,
+  IN  UINT32       Addr,
+  OUT UINT32       *Data
+  )
+{
+  return Dev->PciIo->Io.Read (
+                          Dev->PciIo,
+                          EfiPciIoWidthUint32,
+                          PCI_BAR_IDX0,
+                          Addr,
+                          1,
+                          Data
+                          );
+}
+
 STATIC
 EFI_STATUS
 LsiScsiReset (
@@ -141,6 +195,357 @@ LsiScsiCheckRequest (
   return EFI_SUCCESS;
 }
 
+/**
+
+  Interpret the request packet from the Extended SCSI Pass Thru Protocol and
+  compose the script to submit the command and data to the controller.
+
+  @param[in] Dev          The LSI 53C895A SCSI device the packet targets.
+
+  @param[in] Target       The SCSI target controlled by the LSI 53C895A SCSI
+                          device.
+
+  @param[in] Lun          The Logical Unit Number under the SCSI target.
+
+  @param[in out] Packet   The Extended SCSI Pass Thru Protocol packet.
+
+
+  @retval EFI_SUCCESS  The Extended SCSI Pass Thru Protocol packet was valid.
+
+  @return              Otherwise, invalid or unsupported parameters were
+                       detected. Status codes are meant for direct forwarding
+                       by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
+                       implementation.
+
+ **/
+STATIC
+EFI_STATUS
+LsiScsiProcessRequest (
+  IN LSI_SCSI_DEV                                   *Dev,
+  IN UINT8                                          Target,
+  IN UINT64                                         Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+  )
+{
+  EFI_STATUS Status;
+  UINT32     *Script;
+  UINT8      *Cdb;
+  UINT8      *MsgOut;
+  UINT8      *MsgIn;
+  UINT8      *ScsiStatus;
+  UINT8      *Data;
+  UINT8      DStat;
+  UINT8      SIst0;
+  UINT8      SIst1;
+  UINT32     Csbc;
+  UINT32     CsbcBase;
+  UINT32     Transferred;
+
+  Script      = Dev->Dma->Script;
+  Cdb         = Dev->Dma->Cdb;
+  Data        = Dev->Dma->Data;
+  MsgIn       = Dev->Dma->MsgIn;
+  MsgOut      = &Dev->Dma->MsgOut;
+  ScsiStatus  = &Dev->Dma->Status;
+
+  *ScsiStatus = 0xFF;
+
+  DStat = 0;
+  SIst0 = 0;
+  SIst1 = 0;
+
+  SetMem (Cdb, sizeof Dev->Dma->Cdb, 0x00);
+  CopyMem (Cdb, Packet->Cdb, Packet->CdbLength);
+
+  //
+  // Fetch the first Cumulative SCSI Byte Count (CSBC).
+  //
+  // CSBC is a cumulative counter of the actual number of bytes that have been
+  // transferred across the SCSI bus during data phases, i.e. it will not
+  // count bytes sent in command, status, message in and out phases.
+  //
+  Status = In32 (Dev, LSI_REG_CSBC, &CsbcBase);
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  //
+  // Clean up the DMA buffer for the script.
+  //
+  SetMem (Script, sizeof Dev->Dma->Script, 0x00);
+
+  //
+  // Compose the script to transfer data between the host and the device.
+  //
+  // References:
+  //   * LSI53C895A PCI to Ultra2 SCSI Controller Version 2.2
+  //     - Chapter 5 SCSI SCRIPT Instruction Set
+  //   * SEABIOS lsi-scsi driver
+  //
+  // All instructions used here consist of 2 32bit words. The first word
+  // contains the command to execute. The second word is loaded into the
+  // DMA SCRIPTS Pointer Save (DSPS) register as either the DMA address
+  // for data transmission or the address/offset for the jump command.
+  // Some commands, such as the selection of the target, don't need to
+  // transfer data through DMA or jump to another instruction, then DSPS
+  // has to be zero.
+  //
+  // There are 3 major parts in this script. The first part (1~3) contains
+  // the instructions to select target and LUN and send the SCSI command
+  // from the request packet. The second part (4~7) is to handle the
+  // potential disconnection and prepare for the data transmission. The
+  // instructions in the third part (8~10) transmit the given data and
+  // collect the result. Instruction 11 raises the interrupt and marks the
+  // end of the script.
+  //
+
+  //
+  // 1. Select target.
+  //
+  *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_SEL | (UINT32)Target << 16;
+  *Script++ = 0x00000000;
+
+  //
+  // 2. Select LUN.
+  //
+  *MsgOut   = 0x80 | (UINT8) Lun; // 0x80: Identify bit
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_OUT |
+              (UINT32)sizeof Dev->Dma->MsgOut;
+  *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgOut);
+
+  //
+  // 3. Send the SCSI Command.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_CMD |
+              (UINT32)sizeof Dev->Dma->Cdb;
+  *Script++ = LSI_SCSI_DMA_ADDR (Dev, Cdb);
+
+  //
+  // 4. Check whether the current SCSI phase is "Message In" or not
+  //    and jump to 7 if it is.
+  //    Note: LSI_INS_TC_RA stands for "Relative Address Mode", so the
+  //          offset 0x18 in the second word means jumping forward
+  //          3 (0x18/8) instructions.
+  //
+  *Script++ = LSI_INS_TYPE_TC | LSI_INS_TC_OPC_JMP |
+              LSI_INS_TC_SCSIP_MSG_IN | LSI_INS_TC_RA |
+              LSI_INS_TC_CP;
+  *Script++ = 0x00000018;
+
+  //
+  // 5. Read "Message" from the initiator to trigger reselect.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN |
+              (UINT32)sizeof Dev->Dma->MsgIn;
+  *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn);
+
+  //
+  // 6. Wait reselect.
+  //
+  *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_WAIT_RESEL;
+  *Script++ = 0x00000000;
+
+  //
+  // 7. Read "Message" from the initiator again
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN |
+              (UINT32)sizeof Dev->Dma->MsgIn;
+  *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn);
+
+  //
+  // 8. Set the DMA command for the read/write operations.
+  //    Note: Some requests, e.g. "TEST UNIT READY", do not come with
+  //          allocated InDataBuffer or OutDataBuffer. We skip the DMA
+  //          data command for those requests or this script would fail
+  //          with LSI_SIST0_SGE due to the zero data length.
+  //
+  // LsiScsiCheckRequest() prevents both integer overflows in the command
+  // opcodes, and buffer overflows.
+  //
+  if (Packet->InTransferLength > 0) {
+    ASSERT (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ);
+    ASSERT (Packet->InTransferLength <= sizeof Dev->Dma->Data);
+    *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_IN |
+                Packet->InTransferLength;
+    *Script++ = LSI_SCSI_DMA_ADDR (Dev, Data);
+  } else if (Packet->OutTransferLength > 0) {
+    ASSERT (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE);
+    ASSERT (Packet->OutTransferLength <= sizeof Dev->Dma->Data);
+    CopyMem (Data, Packet->OutDataBuffer, Packet->OutTransferLength);
+    *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_OUT |
+                Packet->OutTransferLength;
+    *Script++ = LSI_SCSI_DMA_ADDR (Dev, Data);
+  }
+
+  //
+  // 9. Get the SCSI status.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_STAT |
+              (UINT32)sizeof Dev->Dma->Status;
+  *Script++ = LSI_SCSI_DMA_ADDR (Dev, Status);
+
+  //
+  // 10. Get the SCSI message.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN |
+              (UINT32)sizeof Dev->Dma->MsgIn;
+  *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn);
+
+  //
+  // 11. Raise the interrupt to end the script.
+  //
+  *Script++ = LSI_INS_TYPE_TC | LSI_INS_TC_OPC_INT |
+              LSI_INS_TC_SCSIP_DAT_OUT | LSI_INS_TC_JMP;
+  *Script++ = 0x00000000;
+
+  //
+  // Make sure the size of the script doesn't exceed the buffer.
+  //
+  ASSERT (Script <= Dev->Dma->Script + ARRAY_SIZE (Dev->Dma->Script));
+
+  //
+  // The controller starts to execute the script once the DMA Script
+  // Pointer (DSP) register is set.
+  //
+  Status = Out32 (Dev, LSI_REG_DSP, LSI_SCSI_DMA_ADDR (Dev, Script));
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  //
+  // Poll the device registers (DSTAT, SIST0, and SIST1) until the SIR
+  // bit sets.
+  //
+  for(;;) {
+    Status = In8 (Dev, LSI_REG_DSTAT, &DStat);
+    if (EFI_ERROR (Status)) {
+      goto Error;
+    }
+    Status = In8 (Dev, LSI_REG_SIST0, &SIst0);
+    if (EFI_ERROR (Status)) {
+      goto Error;
+    }
+    Status = In8 (Dev, LSI_REG_SIST1, &SIst1);
+    if (EFI_ERROR (Status)) {
+      goto Error;
+    }
+
+    if (SIst0 != 0 || SIst1 != 0) {
+      goto Error;
+    }
+
+    //
+    // Check the SIR (SCRIPTS Interrupt Instruction Received) bit.
+    //
+    if (DStat & LSI_DSTAT_SIR) {
+      break;
+    }
+
+    gBS->Stall (Dev->StallPerPollUsec);
+  }
+
+  //
+  // Check if everything is good.
+  //   SCSI Message Code 0x00: COMMAND COMPLETE
+  //   SCSI Status  Code 0x00: Good
+  //
+  if (MsgIn[0] != 0 || *ScsiStatus != 0) {
+    goto Error;
+  }
+
+  //
+  // Fetch CSBC again to calculate the transferred bytes and update
+  // InTransferLength/OutTransferLength.
+  //
+  // Note: The number of transferred bytes is bounded by
+  //       "sizeof Dev->Dma->Data", so it's safe to subtract CsbcBase
+  //       from Csbc. If the CSBC register wraps around, the correct
+  //       difference is ensured by the standard C modular arithmetic.
+  //
+  Status = In32 (Dev, LSI_REG_CSBC, &Csbc);
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  Transferred = Csbc - CsbcBase;
+  if (Packet->InTransferLength > 0) {
+    if (Transferred <= Packet->InTransferLength) {
+      Packet->InTransferLength = Transferred;
+    } else {
+      goto Error;
+    }
+  } else if (Packet->OutTransferLength > 0) {
+    if (Transferred <= Packet->OutTransferLength) {
+      Packet->OutTransferLength = Transferred;
+    } else {
+      goto Error;
+    }
+  }
+
+  //
+  // Copy Data to InDataBuffer if necessary.
+  //
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+    CopyMem (Packet->InDataBuffer, Data, Packet->InTransferLength);
+  }
+
+  //
+  // Always set SenseDataLength to 0.
+  // The instructions of LSI53C895A don't reply sense data. Instead, it
+  // relies on the SCSI command, "REQUEST SENSE", to get sense data. We set
+  // SenseDataLength to 0 to notify ScsiDiskDxe that there is no sense data
+  // written even if this request is processed successfully, so that It will
+  // issue "REQUEST SENSE" later to retrieve sense data.
+  //
+  Packet->SenseDataLength   = 0;
+  Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+  Packet->TargetStatus      = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+
+  return EFI_SUCCESS;
+
+Error:
+  DEBUG ((DEBUG_VERBOSE, "%a: dstat: %02X, sist0: %02X, sist1: %02X\n",
+    __FUNCTION__, DStat, SIst0, SIst1));
+  //
+  // Update the request packet to reflect the status.
+  //
+  if (*ScsiStatus != 0xFF) {
+    Packet->TargetStatus    = *ScsiStatus;
+  } else {
+    Packet->TargetStatus    = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED;
+  }
+
+  if (SIst0 & LSI_SIST0_PAR) {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR;
+  } else if (SIst0 & LSI_SIST0_RST) {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;
+  } else if (SIst0 & LSI_SIST0_UDC) {
+    //
+    // The target device is disconnected unexpectedly. According to UEFI spec,
+    // this is TIMEOUT_COMMAND.
+    //
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;
+  } else if (SIst0 & LSI_SIST0_SGE) {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+  } else if (SIst1 & LSI_SIST1_HTH) {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;
+  } else if (SIst1 & LSI_SIST1_GEN) {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;
+  } else if (SIst1 & LSI_SIST1_STO) {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
+  } else {
+    Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+  }
+
+  //
+  // SenseData may be used to inspect the error. Since we don't set sense data,
+  // SenseDataLength has to be 0.
+  //
+  Packet->SenseDataLength = 0;
+
+  return EFI_DEVICE_ERROR;
+}
+
 //
 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
 // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,
@@ -168,7 +573,7 @@ LsiScsiPassThru (
     return Status;
   }
 
-  return EFI_UNSUPPORTED;
+  return LsiScsiProcessRequest (Dev, *Target, Lun, Packet);
 }
 
 EFI_STATUS
@@ -474,6 +879,7 @@ LsiScsiControllerStart (
     );
   Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit);
   Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit);
+  Dev->StallPerPollUsec = PcdGet32 (PcdLsiScsiStallPerPollUsec);
 
   Status = gBS->OpenProtocol (
                   ControllerHandle,
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
index 05deeed379fe..6ecf523f5a9e 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsi.h
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -13,6 +13,11 @@
 #define _LSI_SCSI_DXE_H_
 
 typedef struct {
+  //
+  // Allocate 32 UINT32 entries for the script and it's sufficient for
+  // 16 instructions.
+  //
+  UINT32                          Script[32];
   //
   // The max size of CDB is 32.
   //
@@ -25,6 +30,18 @@ typedef struct {
   //       Count (DBC), a 24-bit register, so the maximum is 0xFFFFFF (16MB-1).
   //
   UINT8                           Data[SIZE_64KB];
+  //
+  // For SCSI Message In phase
+  //
+  UINT8                           MsgIn[2];
+  //
+  // For SCSI Message Out phase
+  //
+  UINT8                           MsgOut;
+  //
+  // For SCSI Status phase
+  //
+  UINT8                           Status;
 } LSI_SCSI_DMA_BUFFER;
 
 typedef struct {
@@ -34,6 +51,7 @@ typedef struct {
   EFI_PCI_IO_PROTOCOL             *PciIo;
   UINT8                           MaxTarget;
   UINT8                           MaxLun;
+  UINT32                          StallPerPollUsec;
   LSI_SCSI_DMA_BUFFER             *Dma;
   EFI_PHYSICAL_ADDRESS            DmaPhysical;
   VOID                            *DmaMapping;
@@ -46,6 +64,9 @@ typedef struct {
 #define LSI_SCSI_FROM_PASS_THRU(PassThruPtr) \
   CR (PassThruPtr, LSI_SCSI_DEV, PassThru, LSI_SCSI_DEV_SIGNATURE)
 
+#define LSI_SCSI_DMA_ADDR(Dev, MemberName) \
+  ((UINT32)(Dev->DmaPhysical + OFFSET_OF (LSI_SCSI_DMA_BUFFER, MemberName)))
+
 
 //
 // Probe, start and stop functions of this driver, called by the DXE core for
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
index 6111449577a8..4c7abcec618f 100644
--- a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -41,3 +41,6 @@ [Protocols]
 [FixedPcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit   ## CONSUMES
   gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit      ## CONSUMES
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiStallPerPollUsec ## CONSUMES
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 30f9f046a296..0b38dc27d7e5 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -148,6 +148,9 @@ [PcdsFixedAtBuild]
   gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit|7|UINT8|0x3b
   gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit|0|UINT8|0x3c
 
+  ## Microseconds to stall between polling for LsiScsi request result
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiStallPerPollUsec|5|UINT32|0x3d
+
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogBase|0x0|UINT32|0x8
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageEventLogSize|0x0|UINT32|0x9
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize|0x0|UINT32|0xa
-- 
2.27.0

