unreasonable for now.\r
\r
Copyright (C) 2012, Red Hat, Inc.\r
- Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR>\r
Copyright (c) 2017, AMD Inc, All rights reserved.<BR>\r
\r
- This program and the accompanying materials are licensed and made available\r
- under the terms and conditions of the BSD License which accompanies this\r
- distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
- WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
UINT16 TargetValue;\r
EFI_STATUS Status;\r
volatile VIRTIO_SCSI_REQ Request;\r
- volatile VIRTIO_SCSI_RESP Response;\r
+ volatile VIRTIO_SCSI_RESP *Response;\r
+ VOID *ResponseBuffer;\r
DESC_INDICES Indices;\r
+ VOID *RequestMapping;\r
+ VOID *ResponseMapping;\r
+ VOID *InDataMapping;\r
+ VOID *OutDataMapping;\r
+ EFI_PHYSICAL_ADDRESS RequestDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS ResponseDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS InDataDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS OutDataDeviceAddress;\r
+ VOID *InDataBuffer;\r
+ UINTN InDataNumPages;\r
+ BOOLEAN OutDataBufferIsMapped;\r
+\r
+ //\r
+ // Set InDataMapping,OutDataMapping,InDataDeviceAddress and OutDataDeviceAddress to\r
+ // suppress incorrect compiler/analyzer warnings.\r
+ //\r
+ InDataMapping = NULL;\r
+ OutDataMapping = NULL;\r
+ InDataDeviceAddress = 0;\r
+ OutDataDeviceAddress = 0;\r
\r
ZeroMem ((VOID*) &Request, sizeof (Request));\r
- ZeroMem ((VOID*) &Response, sizeof (Response));\r
\r
Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
CopyMem (&TargetValue, Target, sizeof TargetValue);\r
\r
+ InDataBuffer = NULL;\r
+ OutDataBufferIsMapped = FALSE;\r
+ InDataNumPages = 0;\r
+\r
Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- VirtioPrepare (&Dev->Ring, &Indices);\r
+ //\r
+ // Map the virtio-scsi Request header buffer\r
+ //\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterRead,\r
+ (VOID *) &Request,\r
+ sizeof Request,\r
+ &RequestDeviceAddress,\r
+ &RequestMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return ReportHostAdapterError (Packet);\r
+ }\r
+\r
+ //\r
+ // Map the input buffer\r
+ //\r
+ if (Packet->InTransferLength > 0) {\r
+ //\r
+ // Allocate a intermediate input buffer. This is mainly to handle the\r
+ // following case:\r
+ // * caller submits a bi-directional request\r
+ // * we perform the request fine\r
+ // * but we fail to unmap the "InDataMapping"\r
+ //\r
+ // In that case simply returing the EFI_DEVICE_ERROR is not sufficient. In\r
+ // addition to the error code we also need to update Packet fields\r
+ // accordingly so that we report the full loss of the incoming transfer.\r
+ //\r
+ // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If\r
+ // the Virtio request is successful then we copy the data from temporary\r
+ // buffer into Packet->InDataBuffer.\r
+ //\r
+ InDataNumPages = EFI_SIZE_TO_PAGES ((UINTN)Packet->InTransferLength);\r
+ Status = Dev->VirtIo->AllocateSharedPages (\r
+ Dev->VirtIo,\r
+ InDataNumPages,\r
+ &InDataBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapRequestBuffer;\r
+ }\r
+\r
+ ZeroMem (InDataBuffer, Packet->InTransferLength);\r
+\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterCommonBuffer,\r
+ InDataBuffer,\r
+ Packet->InTransferLength,\r
+ &InDataDeviceAddress,\r
+ &InDataMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto FreeInDataBuffer;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Map the output buffer\r
+ //\r
+ if (Packet->OutTransferLength > 0) {\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterRead,\r
+ Packet->OutDataBuffer,\r
+ Packet->OutTransferLength,\r
+ &OutDataDeviceAddress,\r
+ &OutDataMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapInDataBuffer;\r
+ }\r
+\r
+ OutDataBufferIsMapped = TRUE;\r
+ }\r
+\r
+ //\r
+ // Response header is bi-direction (we preset with host status and expect\r
+ // the device to update it). Allocate a response buffer which can be mapped\r
+ // to access equally by both processor and device.\r
+ //\r
+ Status = Dev->VirtIo->AllocateSharedPages (\r
+ Dev->VirtIo,\r
+ EFI_SIZE_TO_PAGES (sizeof *Response),\r
+ &ResponseBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapOutDataBuffer;\r
+ }\r
+\r
+ Response = ResponseBuffer;\r
+\r
+ ZeroMem ((VOID *)Response, sizeof (*Response));\r
\r
//\r
// preset a host status for ourselves that we do not accept as success\r
//\r
- Response.Response = VIRTIO_SCSI_S_FAILURE;\r
+ Response->Response = VIRTIO_SCSI_S_FAILURE;\r
+\r
+ //\r
+ // Map the response buffer with BusMasterCommonBuffer so that response\r
+ // buffer can be accessed by both host and device.\r
+ //\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterCommonBuffer,\r
+ ResponseBuffer,\r
+ sizeof (*Response),\r
+ &ResponseDeviceAddress,\r
+ &ResponseMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto FreeResponseBuffer;\r
+ }\r
+\r
+ VirtioPrepare (&Dev->Ring, &Indices);\r
\r
//\r
// ensured by VirtioScsiInit() -- this predicate, in combination with the\r
//\r
// enqueue Request\r
//\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,\r
- VRING_DESC_F_NEXT, &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ RequestDeviceAddress,\r
+ sizeof Request,\r
+ VRING_DESC_F_NEXT,\r
+ &Indices\r
+ );\r
\r
//\r
// enqueue "dataout" if any\r
//\r
if (Packet->OutTransferLength > 0) {\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->OutDataBuffer,\r
- Packet->OutTransferLength, VRING_DESC_F_NEXT, &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ OutDataDeviceAddress,\r
+ Packet->OutTransferLength,\r
+ VRING_DESC_F_NEXT,\r
+ &Indices\r
+ );\r
}\r
\r
//\r
// enqueue Response, to be written by the host\r
//\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) &Response, sizeof Response,\r
- VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ?\r
- VRING_DESC_F_NEXT : 0),\r
- &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ ResponseDeviceAddress,\r
+ sizeof *Response,\r
+ VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ? VRING_DESC_F_NEXT : 0),\r
+ &Indices\r
+ );\r
\r
//\r
// enqueue "datain" if any, to be written by the host\r
//\r
if (Packet->InTransferLength > 0) {\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->InDataBuffer,\r
- Packet->InTransferLength, VRING_DESC_F_WRITE, &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ InDataDeviceAddress,\r
+ Packet->InTransferLength,\r
+ VRING_DESC_F_WRITE,\r
+ &Indices\r
+ );\r
}\r
\r
// If kicking the host fails, we must fake a host adapter error.\r
//\r
if (VirtioFlush (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,\r
&Indices, NULL) != EFI_SUCCESS) {\r
- return ReportHostAdapterError (Packet);\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapResponseBuffer;\r
}\r
\r
- return ParseResponse (Packet, &Response);\r
+ Status = ParseResponse (Packet, Response);\r
+\r
+ //\r
+ // If virtio request was successful and it was a CPU read request then we\r
+ // have used an intermediate buffer. Copy the data from intermediate buffer\r
+ // to the final buffer.\r
+ //\r
+ if (InDataBuffer != NULL) {\r
+ CopyMem (Packet->InDataBuffer, InDataBuffer, Packet->InTransferLength);\r
+ }\r
+\r
+UnmapResponseBuffer:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, ResponseMapping);\r
+\r
+FreeResponseBuffer:\r
+ Dev->VirtIo->FreeSharedPages (\r
+ Dev->VirtIo,\r
+ EFI_SIZE_TO_PAGES (sizeof *Response),\r
+ ResponseBuffer\r
+ );\r
+\r
+UnmapOutDataBuffer:\r
+ if (OutDataBufferIsMapped) {\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, OutDataMapping);\r
+ }\r
+\r
+UnmapInDataBuffer:\r
+ if (InDataBuffer != NULL) {\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, InDataMapping);\r
+ }\r
+\r
+FreeInDataBuffer:\r
+ if (InDataBuffer != NULL) {\r
+ Dev->VirtIo->FreeSharedPages (Dev->VirtIo, InDataNumPages, InDataBuffer);\r
+ }\r
+\r
+UnmapRequestBuffer:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);\r
+\r
+ return Status;\r
}\r
\r
\r
goto Failed;\r
}\r
\r
- Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1;\r
+ Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1 |\r
+ VIRTIO_F_IOMMU_PLATFORM;\r
\r
//\r
// In virtio-1.0, feature negotiation is expected to complete before queue\r
// step 5 -- Report understood features and guest-tuneables.\r
//\r
if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
- Features &= ~(UINT64)VIRTIO_F_VERSION_1;\r
+ Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
if (EFI_ERROR (Status)) {\r
goto UnmapQueue;\r
{\r
VSCSI_DEV *Dev;\r
\r
+ DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
//\r
// Reset the device. This causes the hypervisor to forget about the virtio\r
// ring.\r
//\r
Dev = Context;\r
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
-\r
- //\r
- // Unmap the ring buffer so that hypervisor will not be able to get\r
- // readable data after device reset.\r
- //\r
- Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
}\r
\r
\r