VirtioFsAsVoid, VirtioFs->Label));\r
VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);\r
}\r
+\r
+/**\r
+ Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request\r
+ buffers, list of response buffers -- together.\r
+\r
+ On input, the caller is required to populate the following fields:\r
+ - VIRTIO_FS_IO_VECTOR.Buffer,\r
+ - VIRTIO_FS_IO_VECTOR.Size,\r
+ - VIRTIO_FS_SCATTER_GATHER_LIST.IoVec,\r
+ - VIRTIO_FS_SCATTER_GATHER_LIST.NumVec.\r
+\r
+ On output (on successful return), the following fields will be\r
+ zero-initialized:\r
+ - VIRTIO_FS_IO_VECTOR.Mapped,\r
+ - VIRTIO_FS_IO_VECTOR.MappedAddress,\r
+ - VIRTIO_FS_IO_VECTOR.Mapping,\r
+ - VIRTIO_FS_IO_VECTOR.Transferred.\r
+\r
+ On output (on successful return), the following fields will be calculated:\r
+ - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.\r
+\r
+ The function may only be called after VirtioFsInit() returns successfully and\r
+ before VirtioFsUninit() is called.\r
+\r
+ @param[in] VirtioFs The Virtio Filesystem device that the\r
+ request-response exchange, expressed via\r
+ RequestSgList and ResponseSgList, will be\r
+ submitted to.\r
+\r
+ @param[in,out] RequestSgList The scatter-gather list that describes the\r
+ request part of the exchange -- the buffers\r
+ that should be sent to the Virtio Filesystem\r
+ device in the virtio transfer.\r
+\r
+ @param[in,out] ResponseSgList The scatter-gather list that describes the\r
+ response part of the exchange -- the buffers\r
+ that the Virtio Filesystem device should\r
+ populate in the virtio transfer. May be NULL\r
+ if the exchange with the Virtio Filesystem\r
+ device consists of a request only, with the\r
+ response part omitted altogether.\r
+\r
+ @retval EFI_SUCCESS RequestSgList and ResponseSgList have been\r
+ validated, output fields have been set.\r
+\r
+ @retval EFI_INVALID_PARAMETER RequestSgList is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER On input, a\r
+ VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is\r
+ NULL, or a\r
+ VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is\r
+ zero.\r
+\r
+ @retval EFI_INVALID_PARAMETER On input, a VIRTIO_FS_IO_VECTOR.Buffer field\r
+ is NULL, or a VIRTIO_FS_IO_VECTOR.Size field\r
+ is zero.\r
+\r
+ @retval EFI_UNSUPPORTED (RequestSgList->NumVec +\r
+ ResponseSgList->NumVec) exceeds\r
+ VirtioFs->QueueSize, meaning that the total\r
+ list of buffers cannot be placed on the virtio\r
+ queue in a single descriptor chain (with one\r
+ descriptor per buffer).\r
+\r
+ @retval EFI_UNSUPPORTED One of the\r
+ VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields\r
+ would exceed MAX_UINT32.\r
+**/\r
+EFI_STATUS\r
+VirtioFsSgListsValidate (\r
+ IN VIRTIO_FS *VirtioFs,\r
+ IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,\r
+ IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL\r
+ )\r
+{\r
+ VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];\r
+ UINT16 DescriptorsNeeded;\r
+ UINTN ListId;\r
+\r
+ if (RequestSgList == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ SgListParam[0] = RequestSgList;\r
+ SgListParam[1] = ResponseSgList;\r
+\r
+ DescriptorsNeeded = 0;\r
+ for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {\r
+ VIRTIO_FS_SCATTER_GATHER_LIST *SgList;\r
+ UINT32 SgListTotalSize;\r
+ UINTN IoVecIdx;\r
+\r
+ SgList = SgListParam[ListId];\r
+ if (SgList == NULL) {\r
+ continue;\r
+ }\r
+ //\r
+ // Sanity-check SgList -- it must provide at least one IO Vector.\r
+ //\r
+ if (SgList->IoVec == NULL || SgList->NumVec == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ //\r
+ // Make sure that, for each IO Vector in this SgList, a virtio descriptor\r
+ // can be added to the virtio queue, after the other descriptors added\r
+ // previously.\r
+ //\r
+ if (SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded) ||\r
+ DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ DescriptorsNeeded += (UINT16)SgList->NumVec;\r
+\r
+ SgListTotalSize = 0;\r
+ for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {\r
+ VIRTIO_FS_IO_VECTOR *IoVec;\r
+\r
+ IoVec = &SgList->IoVec[IoVecIdx];\r
+ //\r
+ // Sanity-check this IoVec -- it must describe a non-empty buffer.\r
+ //\r
+ if (IoVec->Buffer == NULL || IoVec->Size == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ //\r
+ // Make sure the cumulative size of all IO Vectors in this SgList remains\r
+ // expressible as a UINT32.\r
+ //\r
+ if (IoVec->Size > MAX_UINT32 - SgListTotalSize) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ SgListTotalSize += (UINT32)IoVec->Size;\r
+\r
+ //\r
+ // Initialize those fields in this IO Vector that will be updated in\r
+ // relation to mapping / transfer.\r
+ //\r
+ IoVec->Mapped = FALSE;\r
+ IoVec->MappedAddress = 0;\r
+ IoVec->Mapping = NULL;\r
+ IoVec->Transferred = 0;\r
+ }\r
+\r
+ //\r
+ // Store the cumulative size of all IO Vectors that we have calculated in\r
+ // this SgList.\r
+ //\r
+ SgList->TotalSize = SgListTotalSize;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Submit a validated pair of (request buffer list, response buffer list) to the\r
+ Virtio Filesystem device.\r
+\r
+ On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been\r
+ validated together, using the VirtioFsSgListsValidate() function.\r
+\r
+ On output (on successful return), the following fields will be re-initialized\r
+ to zero (after temporarily setting them to different values):\r
+ - VIRTIO_FS_IO_VECTOR.Mapped,\r
+ - VIRTIO_FS_IO_VECTOR.MappedAddress,\r
+ - VIRTIO_FS_IO_VECTOR.Mapping.\r
+\r
+ On output (on successful return), the following fields will be calculated:\r
+ - VIRTIO_FS_IO_VECTOR.Transferred.\r
+\r
+ The function may only be called after VirtioFsInit() returns successfully and\r
+ before VirtioFsUninit() is called.\r
+\r
+ @param[in,out] VirtioFs The Virtio Filesystem device that the\r
+ request-response exchange, expressed via\r
+ RequestSgList and ResponseSgList, should now\r
+ be submitted to.\r
+\r
+ @param[in,out] RequestSgList The scatter-gather list that describes the\r
+ request part of the exchange -- the buffers\r
+ that should be sent to the Virtio Filesystem\r
+ device in the virtio transfer.\r
+\r
+ @param[in,out] ResponseSgList The scatter-gather list that describes the\r
+ response part of the exchange -- the buffers\r
+ that the Virtio Filesystem device should\r
+ populate in the virtio transfer. May be NULL\r
+ if and only if NULL was passed to\r
+ VirtioFsSgListsValidate() as ResponseSgList.\r
+\r
+ @retval EFI_SUCCESS Transfer complete. The caller should investigate\r
+ the VIRTIO_FS_IO_VECTOR.Transferred fields in\r
+ ResponseSgList, to ensure coverage of the relevant\r
+ response buffers. Subsequently, the caller should\r
+ investigate the contents of those buffers.\r
+\r
+ @retval EFI_DEVICE_ERROR The Virtio Filesystem device reported populating\r
+ more response bytes than ResponseSgList->TotalSize.\r
+\r
+ @return Error codes propagated from\r
+ VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),\r
+ or VirtioFs->Virtio->UnmapSharedBuffer().\r
+**/\r
+EFI_STATUS\r
+VirtioFsSgListsSubmit (\r
+ IN OUT VIRTIO_FS *VirtioFs,\r
+ IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,\r
+ IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL\r
+ )\r
+{\r
+ VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];\r
+ VIRTIO_MAP_OPERATION SgListVirtioMapOp[ARRAY_SIZE (SgListParam)];\r
+ UINT16 SgListDescriptorFlag[ARRAY_SIZE (SgListParam)];\r
+ UINTN ListId;\r
+ VIRTIO_FS_SCATTER_GATHER_LIST *SgList;\r
+ UINTN IoVecIdx;\r
+ VIRTIO_FS_IO_VECTOR *IoVec;\r
+ EFI_STATUS Status;\r
+ DESC_INDICES Indices;\r
+ UINT32 TotalBytesWrittenByDevice;\r
+ UINT32 BytesPermittedForWrite;\r
+\r
+ SgListParam[0] = RequestSgList;\r
+ SgListVirtioMapOp[0] = VirtioOperationBusMasterRead;\r
+ SgListDescriptorFlag[0] = 0;\r
+\r
+ SgListParam[1] = ResponseSgList;\r
+ SgListVirtioMapOp[1] = VirtioOperationBusMasterWrite;\r
+ SgListDescriptorFlag[1] = VRING_DESC_F_WRITE;\r
+\r
+ //\r
+ // Map all IO Vectors.\r
+ //\r
+ for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {\r
+ SgList = SgListParam[ListId];\r
+ if (SgList == NULL) {\r
+ continue;\r
+ }\r
+ for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {\r
+ IoVec = &SgList->IoVec[IoVecIdx];\r
+ //\r
+ // Map this IO Vector.\r
+ //\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ VirtioFs->Virtio,\r
+ SgListVirtioMapOp[ListId],\r
+ IoVec->Buffer,\r
+ IoVec->Size,\r
+ &IoVec->MappedAddress,\r
+ &IoVec->Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Unmap;\r
+ }\r
+ IoVec->Mapped = TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Compose the descriptor chain.\r
+ //\r
+ VirtioPrepare (&VirtioFs->Ring, &Indices);\r
+ for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {\r
+ SgList = SgListParam[ListId];\r
+ if (SgList == NULL) {\r
+ continue;\r
+ }\r
+ for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {\r
+ UINT16 NextFlag;\r
+\r
+ IoVec = &SgList->IoVec[IoVecIdx];\r
+ //\r
+ // Set VRING_DESC_F_NEXT on all except the very last descriptor.\r
+ //\r
+ NextFlag = VRING_DESC_F_NEXT;\r
+ if (ListId == ARRAY_SIZE (SgListParam) - 1 &&\r
+ IoVecIdx == SgList->NumVec - 1) {\r
+ NextFlag = 0;\r
+ }\r
+ VirtioAppendDesc (\r
+ &VirtioFs->Ring,\r
+ IoVec->MappedAddress,\r
+ (UINT32)IoVec->Size,\r
+ SgListDescriptorFlag[ListId] | NextFlag,\r
+ &Indices\r
+ );\r
+ }\r
+ }\r
+\r
+ //\r
+ // Submit the descriptor chain.\r
+ //\r
+ Status = VirtioFlush (VirtioFs->Virtio, VIRTIO_FS_REQUEST_QUEUE,\r
+ &VirtioFs->Ring, &Indices, &TotalBytesWrittenByDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Unmap;\r
+ }\r
+\r
+ //\r
+ // Sanity-check: the Virtio Filesystem device should not have written more\r
+ // bytes than what we offered buffers for.\r
+ //\r
+ if (ResponseSgList == NULL) {\r
+ BytesPermittedForWrite = 0;\r
+ } else {\r
+ BytesPermittedForWrite = ResponseSgList->TotalSize;\r
+ }\r
+ if (TotalBytesWrittenByDevice > BytesPermittedForWrite) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Unmap;\r
+ }\r
+\r
+ //\r
+ // Update the transfer sizes in the IO Vectors.\r
+ //\r
+ for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {\r
+ SgList = SgListParam[ListId];\r
+ if (SgList == NULL) {\r
+ continue;\r
+ }\r
+ for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {\r
+ IoVec = &SgList->IoVec[IoVecIdx];\r
+ if (SgListVirtioMapOp[ListId] == VirtioOperationBusMasterRead) {\r
+ //\r
+ // We report that the Virtio Filesystem device has read all buffers in\r
+ // the request.\r
+ //\r
+ IoVec->Transferred = IoVec->Size;\r
+ } else {\r
+ //\r
+ // Regarding the response, calculate how much of the current IO Vector\r
+ // has been populated by the Virtio Filesystem device. In\r
+ // "TotalBytesWrittenByDevice", VirtioFlush() reported the total count\r
+ // across all device-writeable descriptors, in the order they were\r
+ // chained on the ring.\r
+ //\r
+ IoVec->Transferred = MIN ((UINTN)TotalBytesWrittenByDevice,\r
+ IoVec->Size);\r
+ TotalBytesWrittenByDevice -= (UINT32)IoVec->Transferred;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // By now, "TotalBytesWrittenByDevice" has been exhausted.\r
+ //\r
+ ASSERT (TotalBytesWrittenByDevice == 0);\r
+\r
+ //\r
+ // We've succeeded; fall through.\r
+ //\r
+Unmap:\r
+ //\r
+ // Unmap all mapped IO Vectors on both the success and the error paths. The\r
+ // unmapping occurs in reverse order of mapping, in an attempt to avoid\r
+ // memory fragmentation.\r
+ //\r
+ ListId = ARRAY_SIZE (SgListParam);\r
+ while (ListId > 0) {\r
+ --ListId;\r
+ SgList = SgListParam[ListId];\r
+ if (SgList == NULL) {\r
+ continue;\r
+ }\r
+ IoVecIdx = SgList->NumVec;\r
+ while (IoVecIdx > 0) {\r
+ EFI_STATUS UnmapStatus;\r
+\r
+ --IoVecIdx;\r
+ IoVec = &SgList->IoVec[IoVecIdx];\r
+ //\r
+ // Unmap this IO Vector, if it has been mapped.\r
+ //\r
+ if (!IoVec->Mapped) {\r
+ continue;\r
+ }\r
+ UnmapStatus = VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio,\r
+ IoVec->Mapping);\r
+ //\r
+ // Re-set the following fields to the values they initially got from\r
+ // VirtioFsSgListsValidate() -- the above unmapping attempt is considered\r
+ // final, even if it fails.\r
+ //\r
+ IoVec->Mapped = FALSE;\r
+ IoVec->MappedAddress = 0;\r
+ IoVec->Mapping = NULL;\r
+\r
+ //\r
+ // If we are on the success path, but the unmapping failed, we need to\r
+ // transparently flip to the failure path -- the caller must learn they\r
+ // should not consult the response buffers.\r
+ //\r
+ // The branch below can be taken at most once.\r
+ //\r
+ if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {\r
+ Status = UnmapStatus;\r
+ }\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r