On 08/27/17 22:40, Laszlo Ersek wrote:
> This patch is functionally correct. I'd like to request three stylistic
> improvements:
>
> On 08/25/17 23:43, Brijesh Singh wrote:
>> When device is behind the IOMMU, driver is require to pass the device
>> address of virtio request, response and any memory referenced by those
>> request/response to the bus master.
>>
>> The patch uses IOMMU-like member functions from VIRTIO_DEVICE_PROTOCOL to
>> map request and response buffers system physical address to the device
>> address.
>>
>> - If the buffer need to be accessed by both the processor and a bus
>> master then map with BusMasterCommonBuffer.
>>
>> - If the buffer need to be accessed for a write operation by a bus master
>> then map with BusMasterWrite.
>>
>> - If the buffer need to be accessed for a read operation by a bus master
>> then map with BusMasterRead.
>>
>> Cc: Ard Biesheuvel <[email protected]>
>> Cc: Jordan Justen <[email protected]>
>> Cc: Tom Lendacky <[email protected]>
>> Cc: Laszlo Ersek <[email protected]>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Brijesh Singh <[email protected]>
>> ---
>> OvmfPkg/VirtioBlkDxe/VirtioBlk.c | 157 ++++++++++++++++++--
>> 1 file changed, 143 insertions(+), 14 deletions(-)
>>
>> diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
>> b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
>> index 663ba281ab73..ab69cb08a625 100644
>> --- a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
>> +++ b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
>> @@ -232,7 +232,8 @@ VerifyReadWriteRequest (
>>
>> @retval EFI_DEVICE_ERROR Failed to notify host side via VirtIo write,
>> or
>> unable to parse host response, or host
>> response
>> - is not VIRTIO_BLK_S_OK.
>> + is not VIRTIO_BLK_S_OK or failed to map
>> Buffer
>> + for a bus master operation.
>>
>> **/
>>
>> @@ -249,8 +250,16 @@ SynchronousRequest (
>> {
>> UINT32 BlockSize;
>> volatile VIRTIO_BLK_REQ Request;
>> - volatile UINT8 HostStatus;
>> + volatile UINT8 *HostStatus;
>> + VOID *HostStatusBuffer;
>> DESC_INDICES Indices;
>> + VOID *RequestMapping;
>> + VOID *StatusMapping;
>> + VOID *BufferMapping;
>> + EFI_PHYSICAL_ADDRESS BufferDeviceAddress;
>> + EFI_PHYSICAL_ADDRESS HostStatusDeviceAddress;
>> + EFI_PHYSICAL_ADDRESS RequestDeviceAddress;
>> + EFI_STATUS Status, Ret;
>
> (1) Please rename the "Ret" variable to "UnmapStatus", and define it
> separately.
>
>>
>> BlockSize = Dev->BlockIoMedia.BlockSize;
>>
>> @@ -278,9 +287,89 @@ SynchronousRequest (
>> VirtioPrepare (&Dev->Ring, &Indices);
>>
>> //
>> + // Host status is bi-directional (we preset with a value and expect the
>> + // device to update it). Allocate a host status buffer which can be mapped
>> + // to access equally by both processor and the device.
>> + //
>> + Status = Dev->VirtIo->AllocateSharedPages (
>> + Dev->VirtIo,
>> + EFI_SIZE_TO_PAGES (sizeof *HostStatus),
>> + &HostStatusBuffer
>> + );
>> + if (EFI_ERROR (Status)) {
>> + return EFI_DEVICE_ERROR;
>> + }
>> +
>> + //
>> + // Map virtio-blk request header (must be done after request header is
>> + // populated)
>> + //
>> + Status = VirtioMapAllBytesInSharedBuffer (
>> + Dev->VirtIo,
>> + VirtioOperationBusMasterRead,
>> + (VOID *) &Request,
>> + sizeof Request,
>> + &RequestDeviceAddress,
>> + &RequestMapping
>> + );
>> + if (EFI_ERROR (Status)) {
>> + Status = EFI_DEVICE_ERROR;
>> + goto FreeHostStatusBuffer;
>> + }
>> +
>> + //
>> + // Map data buffer
>> + //
>> + if (BufferSize > 0) {
>> + if (RequestIsWrite) {
>> + Status = VirtioMapAllBytesInSharedBuffer (
>> + Dev->VirtIo,
>> + VirtioOperationBusMasterRead,
>> + (VOID *) Buffer,
>> + BufferSize,
>> + &BufferDeviceAddress,
>> + &BufferMapping
>> + );
>> + } else {
>> + Status = VirtioMapAllBytesInSharedBuffer (
>> + Dev->VirtIo,
>> + VirtioOperationBusMasterWrite,
>> + (VOID *) Buffer,
>> + BufferSize,
>> + &BufferDeviceAddress,
>> + &BufferMapping
>> + );
>> + }
>
> (2) Please squash these two branches into one, and determine the
> Operation argument like this:
>
> (RequestIsWrite ?
> VirtioOperationBusMasterRead :
> VirtioOperationBusMasterWrite),
>
> (The conditional operator (? :) should be used sparingly, but when it
> improves readability, it should be used.)
>
>> +
>> + if (EFI_ERROR (Status)) {
>> + Status = EFI_DEVICE_ERROR;
>> + goto UnmapRequestBuffer;
>> + }
>> + }
>> +
>> + //
>> + // Map the Status Buffer with VirtioOperationBusMasterCommonBuffer so that
>> + // both processor and device can access it.
>> + //
>> + Status = VirtioMapAllBytesInSharedBuffer (
>> + Dev->VirtIo,
>> + VirtioOperationBusMasterCommonBuffer,
>> + HostStatusBuffer,
>> + sizeof *HostStatus,
>> + &HostStatusDeviceAddress,
>> + &StatusMapping
>> + );
>> + if (EFI_ERROR (Status)) {
>> + Status = EFI_DEVICE_ERROR;
>> + goto UnmapDataBuffer;
>> + }
>> +
>> + HostStatus = HostStatusBuffer;
>> +
>> + //
>> // preset a host status for ourselves that we do not accept as success
>> //
>> - HostStatus = VIRTIO_BLK_S_IOERR;
>> + *HostStatus = VIRTIO_BLK_S_IOERR;
(4) I think we should be careful with BusMasterCommonBuffer operations
similarly to BusMasterWrite operations -- populate first, map second.
This is to avoid exposing any stale data, even for a short window, to
the device.
Speaking in SEV terms, IoMmuMap() will decrypt NumberOfBytes in-place --
which is by-design, but then it should decrypt what we just put there,
not whatever used to be there (until we overwrite it).
IOW, please move the mapping just under the *HostStatus assignment.
... I've now checked all the VirtioOperationBusMasterCommonBuffer
mappings in the tree, and the rest is fine -- in fact there is only one
(outside of this patch) at the moment: in VirtioRingMap(). And that is
fine, because VirtioRingInit() zeroes out the entire ring, after
allocating it.
Probably a good idea to attend to this in the other drivers (wherever
they use BusMasterCommonBuffer).
Thanks!
Laszlo
>>
>> //
>> // ensured by VirtioBlkInit() -- this predicate, in combination with the
>> @@ -291,8 +380,13 @@ SynchronousRequest (
>> //
>> // virtio-blk header in first desc
>> //
>> - VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,
>> - VRING_DESC_F_NEXT, &Indices);
>> + VirtioAppendDesc (
>> + &Dev->Ring,
>> + RequestDeviceAddress,
>> + sizeof Request,
>> + VRING_DESC_F_NEXT,
>> + &Indices
>> + );
>>
>> //
>> // data buffer for read/write in second desc
>> @@ -311,27 +405,62 @@ SynchronousRequest (
>> //
>> // VRING_DESC_F_WRITE is interpreted from the host's point of view.
>> //
>> - VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,
>> + VirtioAppendDesc (
>> + &Dev->Ring,
>> + BufferDeviceAddress,
>> + (UINT32) BufferSize,
>> VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),
>> - &Indices);
>> + &Indices
>> + );
>> }
>>
>> //
>> // host status in last (second or third) desc
>> //
>> - VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,
>> - VRING_DESC_F_WRITE, &Indices);
>> + VirtioAppendDesc (
>> + &Dev->Ring,
>> + HostStatusDeviceAddress,
>> + sizeof *HostStatus,
>> + VRING_DESC_F_WRITE,
>> + &Indices
>> + );
>>
>> //
>> // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).
>> //
>> - if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices,
>> - NULL) == EFI_SUCCESS &&
>> - HostStatus == VIRTIO_BLK_S_OK) {
>> - return EFI_SUCCESS;
>> + Status = VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, NULL);
>> +
>> + //
>> + // Unmap the HostStatus buffer before accessing it
>> + //
>> + Ret = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, StatusMapping);
>
> (3) Okay, so here you will assign UnmapStatus. And then, the rest:
>
>> + if (EFI_ERROR (Ret)) {
>> + Status = EFI_DEVICE_ERROR;
>> + }
>> +
>> + if (!EFI_ERROR (Status) &&
>> + *HostStatus == VIRTIO_BLK_S_OK) {
>> + Status = EFI_SUCCESS;
>> + } else {
>> + Status = EFI_DEVICE_ERROR;
>> }
>
> should be written like this:
>
> if (EFI_ERROR (Status) || EFI_ERROR (UnmapStatus) ||
> *HostStatus != VIRTIO_BLK_S_OK) {
> Status = EFI_DEVICE_ERROR;
> }
>
> Namely,
>
> - this block will ensure that we only look at *HostStatus when we're
> allowed to (i.e., after both VirtioFlush() and Unmap succeeded),
>
> - If VirtioFlush() fails and sets Status to some error code, then we
> coerce Status to EFI_DEVICE_ERROR,
>
> - If the entire condition evaluates to FALSE, then Status is already
> EFI_SUCCESS, so no need to touch it.
>
>
> Regarding the VirtioScsiDxe driver... in this patch, we get away with
> the above shortcut (without making a mess of the code), but in the
> VirtioScsiDxe driver, we won't. In that driver, the bus master device
> can produce *two* output buffers, so you will have to unmap at least one
> of those under an error-handling label -- when you mapped the first
> successfully, and failed to map the second.
>
> (In this patch you managed to unmap StatusMapping in shared code, but
> that only worked because you had only one output buffer, and you could
> put its mapping last.)
>
> And once you unmap an output buffer on both the success path and the
> failure path, things get more complex. I think that's OK, we should just
> be consistent about it. So, for VirtioScsiDxe, I suggest the pattern I
> laid out in
> <[email protected]">http://mid.mail-archive.com/[email protected]>.
>
> Thank you,
> Laszlo
>
>>
>> - return EFI_DEVICE_ERROR;
>> +UnmapDataBuffer:
>> + if (BufferSize > 0) {
>> + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, BufferMapping);
>> + }
>> +
>> +UnmapRequestBuffer:
>> + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);
>> +
>> +FreeHostStatusBuffer:
>> + Dev->VirtIo->FreeSharedPages (
>> + Dev->VirtIo,
>> + EFI_SIZE_TO_PAGES (sizeof *HostStatus),
>> + HostStatusBuffer
>> + );
>> +
>> + return Status;
>> }
>>
>>
>>
>
> _______________________________________________
> edk2-devel mailing list
> [email protected]
> https://lists.01.org/mailman/listinfo/edk2-devel
>
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel