On 07/27/17 16:21, Brijesh Singh wrote: > Hi Laszlo, > > >> >> (5.3) All virtio driver code should treat VIRTIO_F_IOMMU_PLATFORM >> simply in parallel with VIRTIO_F_VERSION_1, and don't act upon >> VIRTIO_F_IOMMU_PLATFORM in any special shape or form. So basically >> this is just my point (3) from above. >> >> >> (5.4) There are three VIRTIO_DEVICE_PROTOCOL implementations in edk2: >> >> - "OvmfPkg/VirtioPciDeviceDxe" binds legacy-only and transitional >> virtio-pci devices, and offers virtio 0.9.5 semantics. >> >> - "ArmVirtPkg/VirtioFdtDxe" (via >> "OvmfPkg/Library/VirtioMmioDeviceLib") binds virtio-mmio devices, >> and offers virtio 0.9.5 semantics. >> >> - "OvmfPkg/Virtio10Dxe" binds modern-only virtio-pci devices, and >> offers virtio 1.0.0 semantics. >> >> The first two drivers should implement the AllocateSharedPages() and >> FreeSharedPages() member functions simply with the corresponding >> MemoryAllocationLib functions (using BootServicesData type memory), >> and implement the MapSharedPages() and UnmapSharedPages() member >> functions as no-ops (return the input addresses transparently). >> >> The third driver should implement all four new member functions by >> respectively delegating the job to: >> - EFI_PCI_IO_PROTOCOL.AllocateBuffer() -- with BootServicesData -- >> - EFI_PCI_IO_PROTOCOL.FreeBuffer() >> - EFI_PCI_IO_PROTOCOL.Map() -- with BusMasterCommonBuffer64 -- >> - EFI_PCI_IO_PROTOCOL.Unmap() >> > > I have working to implement patch per your recommendation. I assume > you mean map the buffers with EfiPciIoOperationBusMasterCommonBuffer > [1].
Yes. > If so, I see one issue with SEV guest. When SEV is active, IOMMU > driver uses a bounce buffer to map host address to a device address. > While creating bounce buffer we can map it either for > EfiPciIoOperationBusMasterRead or EfiPciIoOperationBusMasterWrite > Operation. If caller wants to map > EfiPciIoOperationBusMasterCommonBuffer then it must allocate the > buffer using EFI_PCI_IO_PROTOCOL.AllocateBuffer() [2] otherwise we > will fail to map. Correct. > I see that PciRootBridgeIo.c has similar check when using a bounce > buffer for < 4GB use cases [3]. Correct. > Do you see any issue if we use EfiPciIoOperationBusMasterRead or > EfiPciIoOperationBusMasterWrite instead of > EfiPciIoOperationBusMasterCommonBuffer ? Yes, I do. The BusMasterRead and BusMasterWrite operations are suitable for one-shot, uni-directional transfers, where the bounce buffer contents and the client code contents are exchanged on Map/Unmap. However virtio transfers, generally speaking, are not like this. While the individual memory areas pointed-to by separate virtio descriptors can me associated with one specific transfer direction (see the VRING_DESC_F_WRITE flag), the virtio ring area itself is long-lived, and it is simultaneously read and written by both host and guest, asynchronously and repeatedly. This calls for BusMasterCommonBuffer. Once we implement BusMasterCommonBuffer, we can use it for everything else. Another reason why separate allocation and mapping (and conversely, separate unmapping and deallocation) are required is the ExitBootServices() callbacks. In that callback, unmapping must happen *without* memory allocation or deallocation (because the IOMMU must be de-programmed, but the UEFI memmap must not be changed), covering all memory areas that are at that point shared between host and guest (regardless of transfer direction). Normally, Map allocates the bounce buffer internally, and Unmap releases the bounce buffer internally (for BusMasterRead and BusMasterWrite), which is not right here. If you use AllocateBuffer() separately, then Map() -- with BusMasterCommonBuffer -- will not allocate internally, and Unmap() will also not deallocate internally. So, in the ExitBootServices() callback, you will be able to call Unmap() only, and then *not* call FreeBuffer() at all. This is why I suggest introducing all four functions (Allocate, Free, Map, Unmap) to the VIRTIO_DEVICE_PROTOCOL, and why all virtio drivers should use all four functions explicitly, not just Map and Unmap. ... Actually, I think there is a bug in "OvmfPkg/IoMmuDxe/AmdSevIoMmu.c". You have the following distribution of operations at the moment: - IoMmuAllocateBuffer() allocates pages and clears the memory encryption mask - IoMmuFreeBuffer() re-sets the memory encryption mask, and deallocates pages - IoMmuMap() does nothing at all when BusMasterCommonBuffer is requested (and IoMmuAllocateBuffer() was called previously). Otherwise, IoMmuMap() allocates pages, allocates MAP_INFO, and clears the memory encryption mask. - IoMmuUnmap() does nothing when cleaning up a BusMasterCommonBuffer operation (--> NO_MAPPING). Otherwise, IoMmuUnmap() clears the encryption mask, and frees both the pages and MAP_INFO. This distribution of operations seems wrong. The key point is that AllocateBuffer() *need not* result in a buffer that is immediately usable, and that client code is required to call Map() *unconditionally*, even if BusMasterCommonBuffer is the desired operation. Therefore, the right distribution of operations is: - IoMmuAllocateBuffer() allocates pages and does not touch the encryption mask.. - IoMmuFreeBuffer() deallocates pages and does not touch the encryption mask. - IoMmuMap() does not allocate pages when BusMasterCommonBuffer is requested, and it allocates pages (bounce buffer) otherwise. *Regardless* of BusMaster operation, the following actions are carried out unconditionally: . the memory encryption mask is cleared in this function (and in this function only), . An attempt is made to grab a MAP_INFO structure from an internal free list (to be introduced!). The head of the list is a new static variable. If the free list is empty, then a MAP_INFO structure is allocated with AllocatePool(). The NO_MAPPING macro becomes unused and can be deleted from the source code. - IoMmuUnmap() clears the encryption mask unconditionally. (For this, it has to consult the MAP_INFO structure that is being passed in from the caller.) In addition: . If MapInfo->Operation is BusMasterCommonBuffer, then we know the allocation was done separately in AllocateBuffer, so we do not release the pages. Otherwise, we do release the pages. . MapInfo is linked back on the internal free list (see above). It is *never* released with FreePool(). This approach guarantees that IoMmuUnmap() can de-program the IOMMU (= re-set the memory encryption mask) without changing the UEFI memory map. (I trust that MemEncryptSevSetPageEncMask() will not split page tables internally when it *re*sets the encryption mask -- is that correct?) I'm sorry that I didn't catch this earlier in your commit f9d129e68a45 ("OvmfPkg: Add IoMmuDxe driver", 2017-07-06), but as you see, I gave you only an Acked-by on that, not a Reviewed-by. I've really had to think it through from the virtio perspective; I didn't foresee this use case back then (I only felt that I wasn't getting the full picture about the IOMMU protocol details). Thanks Laszlo _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel