public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Gary Lin" <glin@suse.com>
To: <devel@edk2.groups.io>
Cc: Jordan Justen <jordan.l.justen@intel.com>,
	Laszlo Ersek <lersek@redhat.com>,
	Ard Biesheuvel <ard.biesheuvel@arm.com>
Subject: [RFC PATCH 1/1] OvmfPkg: Introduce LSI 53C895A SCSI Controller Driver
Date: Fri, 12 Jun 2020 18:04:51 +0800	[thread overview]
Message-ID: <20200612100451.12832-1-glin@suse.com> (raw)

This commit introduces the driver for LSI 53C895A SCSI Controller which,
so that OVMF can access the devices attached to the emulated "lsi" SCSI
controller.

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>
---
Although lsi is now considered obsolete in QEMU, I wrote this driver
mainly for learning the UEFI SCSI driver and ... FUN! The majority of
the code is actually borrowed from VirtioScsci and MptScsi, and I mainly
focus on LsiScsiPassThru().

If it's fine to add this driver into OvmfPkg, I'll start to split this
patch into smaller pieces to make it easier to review.
 
---
 OvmfPkg/Include/IndustryStandard/LsiScsi.h |   80 ++
 OvmfPkg/LsiScsiDxe/LsiScsi.c               | 1120 ++++++++++++++++++++
 OvmfPkg/LsiScsiDxe/LsiScsi.h               |  199 ++++
 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf          |   45 +
 OvmfPkg/OvmfPkg.dec                        |    9 +
 OvmfPkg/OvmfPkgIa32.dsc                    |    4 +
 OvmfPkg/OvmfPkgIa32.fdf                    |    3 +
 OvmfPkg/OvmfPkgIa32X64.dsc                 |    4 +
 OvmfPkg/OvmfPkgIa32X64.fdf                 |    3 +
 OvmfPkg/OvmfPkgX64.dsc                     |    4 +
 OvmfPkg/OvmfPkgX64.fdf                     |    3 +
 11 files changed, 1474 insertions(+)
 create mode 100644 OvmfPkg/Include/IndustryStandard/LsiScsi.h
 create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsi.c
 create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsi.h
 create mode 100644 OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf

diff --git a/OvmfPkg/Include/IndustryStandard/LsiScsi.h b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
new file mode 100644
index 000000000000..cbf049c18310
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/LsiScsi.h
@@ -0,0 +1,80 @@
+/** @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
+
+//
+// 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 DMA Status (DSTAT)
+//
+#define LSI_DSTAT_IID             0x01
+#define LSI_DSTAT_R               0x02
+#define LSI_DSTAT_SIR             0x04
+#define LSI_DSTAT_SSI             0x08
+#define LSI_DSTAT_ABRT            0x10
+#define LSI_DSTAT_BF              0x20
+#define LSI_DSTAT_MDPE            0x40
+#define LSI_DSTAT_DFE             0x80
+
+//
+// The status bits for Interrupt Status Zero (ISTAT0)
+//
+#define LSI_ISTAT0_DIP            0x01
+#define LSI_ISTAT0_SIP            0x02
+#define LSI_ISTAT0_INTF           0x04
+#define LSI_ISTAT0_CON            0x08
+#define LSI_ISTAT0_SEM            0x10
+#define LSI_ISTAT0_SIGP           0x20
+#define LSI_ISTAT0_SRST           0x40
+#define LSI_ISTAT0_ABRT           0x80
+
+//
+// LSI 53C895A Script Instructions
+//
+#define LSI_INS_TYPE_BLK          0x00000000
+#define LSI_INS_TYPE_IO           0x40000000
+#define LSI_INS_TYPE_TC           0x80000000
+
+#define LSI_INS_BLK_SCSIP_DAT_OUT 0x00000000
+#define LSI_INS_BLK_SCSIP_DAT_IN  0x01000000
+#define LSI_INS_BLK_SCSIP_CMD     0x02000000
+#define LSI_INS_BLK_SCSIP_STAT    0x03000000
+#define LSI_INS_BLK_SCSIP_MSG_OUT 0x06000000
+#define LSI_INS_BLK_SCSIP_MSG_IN  0x07000000
+
+#define LSI_INS_IO_OPC_SEL        0x00000000
+#define LSI_INS_IO_OPC_WAIT_RESEL 0x10000000
+
+#define LSI_INS_TC_CP             0x00020000
+#define LSI_INS_TC_JMP            0x00080000
+#define LSI_INS_TC_RA             0x00800000
+
+#define LSI_INS_TC_OPC_JMP        0x00000000
+#define LSI_INS_TC_OPC_INT        0x18000000
+
+#define LSI_INS_TC_SCSIP_DAT_OUT  0x00000000
+#define LSI_INS_TC_SCSIP_MSG_IN   0x07000000
+
+#endif // _LSI_SCSI_H_
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.c b/OvmfPkg/LsiScsiDxe/LsiScsi.c
new file mode 100644
index 000000000000..a226a0e789da
--- /dev/null
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -0,0 +1,1120 @@
+/** @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 <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/PcdLib.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"
+
+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
+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
+LsiScsiReset (
+  IN LSI_SCSI_DEV *Dev
+  )
+{
+  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;
+}
+
+/**
+
+  Interpret the request packet from the Extended SCSI Pass Thru Protocol and
+  compose the script to submit the command and data to the contorller.
+
+  @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;
+
+  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;
+
+  SetMem (Cdb, sizeof Dev->Dma->Cdb, 0x00);
+  CopyMem (Cdb, Packet->Cdb, Packet->CdbLength);
+
+  //
+  // Compose the script to transfer data between the host and the device.
+  //
+  // Reference:
+  //   LSI53C895A PCI to Ultra2 SCSI Controller Version 2.2
+  //   - Chapter 5 SCSI SCRIPT Instruction Set
+  //
+  // 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.
+  //
+  // The controller starts to execute the script once the DMA Script
+  // Pointer (DSP) register is set.
+  //
+  SetMem (Script, sizeof Dev->Dma->Script, 0x00);
+
+  //
+  // Select target.
+  //
+  *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_SEL | (UINT32)Target << 16;
+  *Script++ = 0x00000000;
+
+  //
+  // Select lun.
+  //
+  *MsgOut   = 0x80 | (UINT8) Lun; // 0x80: Identify bit
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_OUT | \
+              sizeof Dev->Dma->MsgOut;
+  *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, MsgOut);
+
+  //
+  // Send the SCSI Command.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_CMD | \
+              sizeof Dev->Dma->Cdb;
+  *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, Cdb);
+
+  //
+  // Handle disconnect
+  //
+  // Check whether the current SCSI phase is "Message In" or not.
+  // If not, try to enter the "Message In" phase to reconnect to the
+  // device.
+  //
+  *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;
+  //
+  // Read "Message" from the initiator to trigger reselect.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN | \
+              sizeof Dev->Dma->MsgIn;
+  *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, MsgIn);
+  //
+  // Wait reselect.
+  //
+  *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_WAIT_RESEL;
+  *Script++ = 0x00000000;
+  //
+  // Read "Message" from the initiator again.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN | \
+              sizeof Dev->Dma->MsgIn;
+  *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, MsgIn);
+
+  //
+  // Set the DMA command for the read/write operations.
+  //
+  if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ &&
+      Packet->InTransferLength > 0) {
+    *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_IN | \
+                Packet->InTransferLength;
+    *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, Data);
+  } else if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE &&
+             Packet->OutTransferLength > 0) {
+    // LsiScsiCheckRequest() guarantees that OutTransferLength is no
+    // larger than sizeof Dev->Dma->Data, so we can safely copy the
+    // the data to 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_LOW (Dev, Data);
+  }
+
+  //
+  // Get the SCSI status.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_STAT | \
+              sizeof Dev->Dma->Status;
+  *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, Status);
+
+  //
+  // Get the SCSI message.
+  //
+  *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN | \
+              sizeof Dev->Dma->MsgIn;
+  *Script++ = LSI_SCSI_DMA_ADDR_LOW (Dev, MsgIn);
+
+  //
+  // 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 script doesn't exceed the size.
+  //
+  ASSERT (Script < Dev->Dma->Script + sizeof Dev->Dma->Script);
+
+  //
+  // Execute the script.
+  //
+  Status = Out32 (Dev, LSI_REG_DSP, LSI_SCSI_DMA_ADDR_LOW(Dev, Script));
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // 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) {
+    //
+    // Copy Data to InDataBuffer if necessary.
+    //
+    if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
+      CopyMem (Packet->InDataBuffer, Data, Packet->InTransferLength);
+    }
+
+    //
+    // The controller doesn't return sense data when replying "TEST UNIT READY",
+    // so we have to set SenseDataLength to 0 to notify ScsiIo to issue
+    // "REQUEST SENSE" for the sense data.
+    //
+    if (Cdb[0] == 0x00) {
+      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;
+  }
+  Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+  Packet->InTransferLength  = 0;
+  Packet->OutTransferLength = 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,
+// 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   Status;
+  LSI_SCSI_DEV *Dev;
+
+  Dev = LSI_SCSI_FROM_PASS_THRU (This);
+  Status = LsiScsiCheckRequest (Dev, *Target, Lun, Packet);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = LsiScsiProcessRequest (Dev, *Target, Lun, Packet);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiGetNextTargetLun (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN OUT UINT8                       **TargetPointer,
+  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;
+}
+
+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
+  )
+{
+  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
+EFIAPI
+LsiScsiGetTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath,
+  OUT UINT8                           **TargetPointer,
+  OUT UINT64                          *Lun
+  )
+{
+  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;
+  }
+
+  //
+  // This device support 8 targets only, so it's enough to set the LSB
+  // of Target.
+  //
+  Target = *TargetPointer;
+  *Target = (UINT8)ScsiDevicePath->Pun;
+  *Lun = ScsiDevicePath->Lun;
+
+  return EFI_SUCCESS;
+}
+
+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
+  )
+{
+  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;
+}
+
+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.
+//
+// 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          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
+EFIAPI
+LsiScsiControllerStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath OPTIONAL
+  )
+{
+  EFI_STATUS           Status;
+  LSI_SCSI_DEV         *Dev;
+  UINTN                Pages;
+  UINTN                BytesMapped;
+
+  Dev = AllocateZeroPool (sizeof (*Dev));
+  if (Dev == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Dev->Signature = LSI_SCSI_DEV_SIGNATURE;
+
+  Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit);
+  Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit);
+  Dev->StallPerPollUsec = PcdGet32 (PcdLsiScsiStallPerPollUsec);
+
+  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;
+  }
+
+  //
+  // 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__
+      ));
+  }
+
+  //
+  // 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,
+                  &LsiScsiExitBoot,
+                  Dev,
+                  &Dev->ExitBoot
+                  );
+  if (EFI_ERROR (Status)) {
+    goto UninitDev;
+  }
+
+  //
+  // 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 CloseExitBoot;
+  }
+
+  return EFI_SUCCESS;
+
+CloseExitBoot:
+  gBS->CloseEvent (Dev->ExitBoot);
+
+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,
+                EfiPciIoAttributeOperationSet,
+                Dev->OrigPciAttrs,
+                NULL
+                );
+
+CloseProtocol:
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
+FreePool:
+  FreePool (Dev);
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+LsiScsiControllerStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  ControllerHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  )
+{
+  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;
+  }
+
+  gBS->CloseEvent (Dev->ExitBoot);
+
+  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,
+                Dev->OrigPciAttrs,
+                NULL
+                );
+
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
+  FreePool (Dev);
+
+  return Status;
+}
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC
+EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+  &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
+};
+
+
+//
+// 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
+//
+EFI_STATUS
+EFIAPI
+LsiScsiEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gDriverBinding,
+           ImageHandle, // The handle to install onto
+           &gComponentName,
+           &gComponentName2
+           );
+}
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsi.h b/OvmfPkg/LsiScsiDxe/LsiScsi.h
new file mode 100644
index 000000000000..1a16ef9f7795
--- /dev/null
+++ b/OvmfPkg/LsiScsiDxe/LsiScsi.h
@@ -0,0 +1,199 @@
+/** @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_
+
+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.
+  //
+  UINT8                           Cdb[32];
+  //
+  // Allocate 64KB for read/write buffer.
+  //
+  UINT8                           Data[0x10000];
+  //
+  // 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 {
+  UINT32                          Signature;
+  UINT64                          OrigPciAttrs;
+  EFI_EVENT                       ExitBoot;
+  EFI_PCI_IO_PROTOCOL             *PciIo;
+  UINT8                           MaxTarget;
+  UINT8                           MaxLun;
+  UINT32                          StallPerPollUsec;
+  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;
+
+#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)
+
+#define LSI_SCSI_DMA_ADDR_LOW(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
+// 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
+//
+
+STATIC
+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
+  );
+
+
+//
+// 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
+// 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_
diff --git a/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
new file mode 100644
index 000000000000..10f7f0eebd0e
--- /dev/null
+++ b/OvmfPkg/LsiScsiDxe/LsiScsiDxe.inf
@@ -0,0 +1,45 @@
+## @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
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiExtScsiPassThruProtocolGuid        ## BY_START
+  gEfiPciIoProtocolGuid                  ## TO_START
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxTargetLimit   ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiMaxLunLimit      ## CONSUMES
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdLsiScsiStallPerPollUsec ## CONSUMES
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 65bb2bb0eb4c..ddf81f2bbcb8 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -174,6 +174,15 @@ [PcdsFixedAtBuild]
   ## Microseconds to stall between polling for MptScsi request result
   gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x40
 
+  ## 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
+
+  ## 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
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index d0df9cbbfb2b..ea1940fc354b 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -50,6 +50,7 @@ [Defines]
   #
   DEFINE PVSCSI_ENABLE           = TRUE
   DEFINE MPT_SCSI_ENABLE         = TRUE
+  DEFINE LSI_SCSI_ENABLE         = TRUE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -775,6 +776,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 e2b759aa8d05..2b9a6b58015f 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -236,6 +236,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 b3ae62fee92b..2cc8cb8053c6 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         = TRUE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -789,6 +790,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 bfca1eff9e83..83ff6aef2e8c 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -237,6 +237,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 f7fe75ebf531..2708ce0d2df5 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         = TRUE
 
   #
   # Flash size selection. Setting FD_SIZE_IN_KB on the command line directly to
@@ -785,6 +786,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 bfca1eff9e83..83ff6aef2e8c 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -237,6 +237,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.25.1


             reply	other threads:[~2020-06-12 10:05 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-12 10:04 Gary Lin [this message]
2020-06-15 11:27 ` [RFC PATCH 1/1] OvmfPkg: Introduce LSI 53C895A SCSI Controller Driver Laszlo Ersek
2020-06-17  2:09   ` Gary Lin
2020-06-17 10:38     ` Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200612100451.12832-1-glin@suse.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox