aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/OvmfPkg/LsiScsiDxe/LsiScsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/edk2/OvmfPkg/LsiScsiDxe/LsiScsi.c')
-rw-r--r--roms/edk2/OvmfPkg/LsiScsiDxe/LsiScsi.c1210
1 files changed, 1210 insertions, 0 deletions
diff --git a/roms/edk2/OvmfPkg/LsiScsiDxe/LsiScsi.c b/roms/edk2/OvmfPkg/LsiScsiDxe/LsiScsi.c
new file mode 100644
index 000000000..ed5fc3bfd
--- /dev/null
+++ b/roms/edk2/OvmfPkg/LsiScsiDxe/LsiScsi.c
@@ -0,0 +1,1210 @@
+/** @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
+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 (
+ 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 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,
+// 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;
+ }
+
+ return LsiScsiProcessRequest (Dev, *Target, Lun, Packet);
+}
+
+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;
+ }
+
+ Target = *TargetPointer;
+ ZeroMem (Target, TARGET_MAX_BYTES);
+ CopyMem (Target, &ScsiDevicePath->Pun, sizeof 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;
+
+ 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);
+ 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;
+ }
+
+ //
+ // 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
+ );
+}