/** @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 #include #include #include #include #include #include #include #include #include #include #include #include #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 ); }