From: Nikita Leshenko <nikita.leshchenko@oracle.com>
To: edk2-devel@lists.01.org
Cc: liran.alon@oracle.com, Nikita Leshenko <nikita.leshchenko@oracle.com>
Subject: [PATCH 12/14] OvmfPkg/MptScsiDxe: Implement the PassThru method
Date: Thu, 31 Jan 2019 12:07:22 +0200 [thread overview]
Message-ID: <20190131100724.20907-13-nikita.leshchenko@oracle.com> (raw)
In-Reply-To: <20190131100724.20907-1-nikita.leshchenko@oracle.com>
Machines should be able to boot after this commit. Tested with different Linux
distributions (Ubuntu, CentOS) and different Windows versions (Windows 7,
Windows 10, Server 2016).
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Aaron Young <aaron.young@oracle.com>
Reviewed-by: Liran Alon <liran.alon@oracle.com>
---
OvmfPkg/MptScsiDxe/MptScsi.c | 255 +++++++++++++++++++++++++++++-
OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 3 +
OvmfPkg/OvmfPkg.dec | 2 +
3 files changed, 256 insertions(+), 4 deletions(-)
diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c
index fbabeb22ca..a7dab0f71a 100644
--- a/OvmfPkg/MptScsiDxe/MptScsi.c
+++ b/OvmfPkg/MptScsiDxe/MptScsi.c
@@ -54,9 +54,15 @@
#define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT 0x02
#define MPT_SG_ENTRY_TYPE_SIMPLE 0x01
+#define MPT_SG_ENTRY_SIMPLE_MAX_LENGTH ((1 << 24) - 1)
#define MPT_IOC_WHOINIT_ROM_BIOS 0x02
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE (0x01 << 24)
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ (0x02 << 24)
+
+#define MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE 0x0043
+
//
// Runtime Structures
//
@@ -110,6 +116,8 @@ typedef struct {
UINT32 Length: 24;
UINT32 EndOfList: 1;
UINT32 Is64BitAddress: 1;
+ // True when the buffer contains data to be transfered. Otherwise it's the
+ // destination buffer
UINT32 BufferContainsData: 1;
UINT32 LocalAddress: 1;
UINT32 ElementType: 2;
@@ -136,6 +144,11 @@ typedef struct {
UINT32 ResponseInfo;
} __attribute__((aligned(8), packed)) MPT_SCSI_IO_ERROR_REPLY; // Align required by HW
+typedef struct {
+ MPT_SCSI_IO_REQUEST Header;
+ MPT_SG_ENTRY_SIMPLE Sg;
+} __attribute__((aligned(8), packed)) MPT_SCSI_REQUEST_WITH_SG; // Align required by HW
+
#define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')
typedef struct {
UINT32 Signature;
@@ -143,6 +156,9 @@ typedef struct {
EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT64 OriginalPciAttributes;
+ UINT32 StallPerPollUsec;
+ MPT_SCSI_IO_ERROR_REPLY IoErrorReply;
+ MPT_SCSI_REQUEST_WITH_SG IoRequest;
} MPT_SCSI_DEV;
#define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
@@ -228,6 +244,8 @@ MptScsiInit (
{
EFI_STATUS Status;
+ Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec);
+
Status = MptScsiReset (Dev);
if (EFI_ERROR (Status)) {
return Status;
@@ -273,6 +291,193 @@ MptScsiInit (
return Status;
}
+ // Put one free reply frame on the reply queue, the hardware may use it to
+ // report an error to us.
+ ASSERT ((UINTN) &Dev->IoErrorReply <= MAX_UINT32);
+ Status = Out32 (Dev, MPT_REG_REP_Q, (UINT32)(UINTN) &Dev->IoErrorReply);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiPopulateRequest (
+ IN UINT8 Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ OUT MPT_SCSI_REQUEST_WITH_SG *Request
+ )
+{
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
+ Packet->CdbLength > sizeof (Request->Header.CDB)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Target > 0 || Lun > 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->InTransferLength > MPT_SG_ENTRY_SIMPLE_MAX_LENGTH) {
+ Packet->InTransferLength = MPT_SG_ENTRY_SIMPLE_MAX_LENGTH;
+ return EFI_BAD_BUFFER_SIZE;
+ }
+ if (Packet->OutTransferLength > MPT_SG_ENTRY_SIMPLE_MAX_LENGTH) {
+ Packet->OutTransferLength = MPT_SG_ENTRY_SIMPLE_MAX_LENGTH;
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ ZeroMem (Request, sizeof (*Request));
+ Request->Header.TargetID = Target;
+ // It's 1 and not 0, for some reason...
+ Request->Header.LUN[1] = Lun;
+ Request->Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
+ Request->Header.MessageContext = 1; // Hardcoded, we handle one request at a time
+
+ Request->Header.CDBLength = Packet->CdbLength;
+ CopyMem (Request->Header.CDB, Packet->Cdb, Packet->CdbLength);
+
+ Request->Header.SenseBufferLength = Packet->SenseDataLength;
+ ASSERT ((UINTN) Packet->SenseData <= MAX_UINT32);
+ Request->Header.SenseBufferLowAddress = (UINT32)(UINTN) Packet->SenseData;
+
+ Request->Sg.EndOfList = 1;
+ Request->Sg.EndOfBuffer = 1;
+ Request->Sg.LastElement = 1;
+ Request->Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE;
+
+ switch (Packet->DataDirection)
+ {
+ case EFI_EXT_SCSI_DATA_DIRECTION_READ:
+ Request->Header.DataLength = Packet->InTransferLength;
+ Request->Sg.Length = Packet->InTransferLength;
+ ASSERT ((UINTN) Packet->InDataBuffer <= MAX_UINT32);
+ Request->Sg.DataBufferAddress = (UINT32)(UINTN) Packet->InDataBuffer;
+ Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ;
+ break;
+ case EFI_EXT_SCSI_DATA_DIRECTION_WRITE:
+ Request->Header.DataLength = Packet->OutTransferLength;
+ Request->Sg.Length = Packet->OutTransferLength;
+ ASSERT ((UINTN) Packet->OutDataBuffer <= MAX_UINT32);
+ Request->Sg.DataBufferAddress = (UINT32)(UINTN) Packet->OutDataBuffer;
+ Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE;
+ Request->Sg.BufferContainsData = 1;
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiSendRequest (
+ IN MPT_SCSI_DEV *Dev,
+ IN MPT_SCSI_REQUEST_WITH_SG *Request,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+
+ // Make sure Request is fully written
+ MemoryFence ();
+
+ ASSERT ((UINTN) Request <= MAX_UINT32);
+ Status = Out32 (Dev, MPT_REG_REQ_Q, (UINT32)(UINTN) Request);
+ if (EFI_ERROR (Status)) {
+ // We couldn't enqueue the request, report it as an adapter error
+ Packet->InTransferLength = 0;
+ Packet->OutTransferLength = 0;
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+ Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+ Packet->SenseDataLength = 0;
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiGetReply (
+ IN MPT_SCSI_DEV *Dev,
+ OUT UINT32 *Reply
+ )
+{
+ EFI_STATUS Status;
+
+ for (;;) {
+ UINT32 Istatus;
+ Status = In32 (Dev, MPT_REG_ISTATUS, &Istatus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Interrupt raised
+ if (Istatus & MPT_IMASK_REPLY) {
+ break;
+ }
+
+ gBS->Stall (Dev->StallPerPollUsec);
+ }
+
+ Status = In32 (Dev, MPT_REG_REP_Q, Reply);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // The driver is supposed to fetch replies until 0xffffffff is returned, which
+ // will reset the interrupt status. We put only one request, so we expect the
+ // next read reply to be the last.
+ UINT32 EmptyReply;
+ Status = In32 (Dev, MPT_REG_REP_Q, &EmptyReply);
+ if (EFI_ERROR (Status) || EmptyReply != MAX_UINT32) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MptScsiHandleReply (
+ IN MPT_SCSI_DEV *Dev,
+ IN MPT_SCSI_REQUEST_WITH_SG *Request,
+ IN UINT32 Reply,
+ OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ if (Reply == Request->Header.MessageContext) {
+ // Everything is good
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+ Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+
+ } else if (Reply & (1 << 31)) {
+ DEBUG ((EFI_D_ERROR, "MptScsi request failed\n"));
+ // When reply MSB is set, it's an error frame.
+
+ switch (Dev->IoErrorReply.IOCStatus) {
+ case MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE:
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
+ break;
+ default:
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+ break;
+ }
+
+ // Resubmit the reply frame to the reply queue
+ EFI_STATUS Status;
+ Status = Out32 (Dev, MPT_REG_REP_Q, (UINT32)(UINTN) &Dev->IoErrorReply);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ } else {
+ DEBUG ((EFI_D_ERROR, "MptScsi unexpected reply\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
return EFI_SUCCESS;
}
@@ -291,10 +496,52 @@ MptScsiPassThru (
IN EFI_EVENT Event OPTIONAL
)
{
- DEBUG ((EFI_D_INFO, "MptScsiPassThru Direction %d In %d Out %d\n",
- Packet->DataDirection,
- Packet->InTransferLength, Packet->OutTransferLength));
- return EFI_UNSUPPORTED;
+ EFI_STATUS Status;
+ MPT_SCSI_DEV *Dev = MPT_SCSI_FROM_PASS_THRU (This);
+
+ // Noisy print, but useful when debugging this area
+ // DEBUG ((EFI_D_INFO, "MptScsiPassThru Direction %d In %d Out %d\n",
+ // Packet->DataDirection,
+ // Packet->InTransferLength, Packet->OutTransferLength));
+
+ Status = MptScsiPopulateRequest (*Target, Lun, Packet, &Dev->IoRequest);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = MptScsiSendRequest (Dev, &Dev->IoRequest, Packet);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+
+ UINT32 Reply;
+ Status = MptScsiGetReply (Dev, &Reply);
+ if (EFI_ERROR (Status)) {
+ goto Fatal;
+ }
+
+ Status = MptScsiHandleReply (Dev, &Dev->IoRequest, Reply, Packet);
+ if (EFI_ERROR (Status)) {
+ goto Fatal;
+ }
+
+ return Status;
+
+Fatal:
+ // We erred in the middle of a transaction, a very serious problem has occured
+ // and it's not clear if it's possible to recover without leaving the hardware
+ // in an inconsistent state. Perhaps we would want to reset the device...
+ DEBUG ((EFI_D_ERROR, "MptScsi fatal error in scsi request\n"));
+ Packet->InTransferLength = 0;
+ Packet->OutTransferLength = 0;
+ if (Packet->HostAdapterStatus == EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK) {
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+ }
+ Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED;
+ Packet->SenseDataLength = 0;
+ return EFI_DEVICE_ERROR;
}
STATIC
diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
index 5d424606a5..5875d6c94b 100644
--- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
+++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf
@@ -40,3 +40,6 @@
[Protocols]
gEfiPciIoProtocolGuid ## TO_START
gEfiExtScsiPassThruProtocolGuid ## BY_START
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec ## CONSUMES
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 7666297cf8..4ca98ff09f 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -122,6 +122,8 @@
gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize|0x0|UINT32|0x1a
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd|0x0|UINT32|0x1f
+ gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x28
+
[PcdsDynamic, PcdsDynamicEx]
gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
--
2.20.1
next prev parent reply other threads:[~2019-01-31 10:08 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-31 10:07 OvmfPkg: Support booting from Fusion-MPT SCSI controllers Nikita Leshenko
2019-01-31 10:07 ` [PATCH 01/14] OvmfPkg/MptScsiDxe: Create empty driver Nikita Leshenko
2019-02-01 2:16 ` Bi, Dandan
2019-02-01 10:07 ` Laszlo Ersek
2019-02-01 9:57 ` Laszlo Ersek
2019-01-31 10:07 ` [PATCH 02/14] OvmfPkg/MptScsiDxe: Install DriverBinding Protocol Nikita Leshenko
2019-02-01 10:21 ` Laszlo Ersek
2019-01-31 10:07 ` [PATCH 03/14] OvmfPkg/MptScsiDxe: Report name of driver Nikita Leshenko
2019-02-01 10:25 ` Laszlo Ersek
2019-02-01 15:13 ` Carsey, Jaben
2019-01-31 10:07 ` [PATCH 04/14] OvmfPkg/MptScsiDxe: Probe PCI devices and look for MptScsi Nikita Leshenko
2019-02-01 11:48 ` Laszlo Ersek
2019-01-31 10:07 ` [PATCH 05/14] OvmfPkg/MptScsiDxe: Install stubbed EXT_SCSI_PASS_THRU Nikita Leshenko
2019-01-31 10:07 ` [PATCH 06/14] OvmfPkg/MptScsiDxe: Report one Target and one LUN Nikita Leshenko
2019-01-31 10:07 ` [PATCH 07/14] OvmfPkg/MptScsiDxe: Build DevicePath for discovered devices Nikita Leshenko
2019-01-31 10:07 ` [PATCH 08/14] OvmfPkg/MptScsiDxe: Implement GetTargetLun Nikita Leshenko
2019-01-31 10:07 ` [PATCH 09/14] OvmfPkg/MptScsiDxe: Open PciIo protocol for later use Nikita Leshenko
2019-01-31 10:07 ` [PATCH 10/14] OvmfPkg/MptScsiDxe: Set and restore PCI attributes Nikita Leshenko
2019-01-31 10:07 ` [PATCH 11/14] OvmfPkg/MptScsiDxe: Initialize hardware Nikita Leshenko
2019-02-01 12:14 ` Laszlo Ersek
2019-01-31 10:07 ` Nikita Leshenko [this message]
2019-02-01 12:55 ` [PATCH 12/14] OvmfPkg/MptScsiDxe: Implement the PassThru method Laszlo Ersek
2019-01-31 10:07 ` [PATCH 13/14] OvmfPkg/MptScsiDxe: Report multiple targets Nikita Leshenko
2019-01-31 10:07 ` [PATCH 14/14] OvmfPkg/MptScsiDxe: Support packets with pointers above 4G Nikita Leshenko
2019-02-01 9:48 ` OvmfPkg: Support booting from Fusion-MPT SCSI controllers Laszlo Ersek
2019-02-01 10:43 ` 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=20190131100724.20907-13-nikita.leshchenko@oracle.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