vfio: move implement of vfio_get_xlat_addr() to memory.c

- Move the implement vfio_get_xlat_addr to softmmu/memory.c, and
  change the name to memory_get_xlat_addr(). So we can use this
  function on other devices, such as vDPA device.
- Add a new function vfio_get_xlat_addr in vfio/common.c, and it will check
  whether the memory is backed by a discard manager. then device can
  have its own warning.

Signed-off-by: Cindy Lu <lulu@redhat.com>
Message-Id: <20221031031020.1405111-2-lulu@redhat.com>
Acked-by: Alex Williamson <alex.williamson@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Cindy Lu 2022-10-31 11:10:19 +08:00 committed by Michael S. Tsirkin
parent be3afe8151
commit baa44bce87
3 changed files with 81 additions and 61 deletions

View file

@ -578,45 +578,11 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
ram_addr_t *ram_addr, bool *read_only)
{
MemoryRegion *mr;
hwaddr xlat;
hwaddr len = iotlb->addr_mask + 1;
bool writable = iotlb->perm & IOMMU_WO;
/*
* The IOMMU TLB entry we have just covers translation through
* this IOMMU to its immediate target. We need to translate
* it the rest of the way through to memory.
*/
mr = address_space_translate(&address_space_memory,
iotlb->translated_addr,
&xlat, &len, writable,
MEMTXATTRS_UNSPECIFIED);
if (!memory_region_is_ram(mr)) {
error_report("iommu map to non memory area %"HWADDR_PRIx"",
xlat);
return false;
} else if (memory_region_has_ram_discard_manager(mr)) {
RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
MemoryRegionSection tmp = {
.mr = mr,
.offset_within_region = xlat,
.size = int128_make64(len),
};
/*
* Malicious VMs can map memory into the IOMMU, which is expected
* to remain discarded. vfio will pin all pages, populating memory.
* Disallow that. vmstate priorities make sure any RamDiscardManager
* were already restored before IOMMUs are restored.
*/
if (!ram_discard_manager_is_populated(rdm, &tmp)) {
error_report("iommu map to discarded memory (e.g., unplugged via"
" virtio-mem): %"HWADDR_PRIx"",
iotlb->translated_addr);
return false;
}
bool ret, mr_has_discard_manager;
ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only,
&mr_has_discard_manager);
if (ret && mr_has_discard_manager) {
/*
* Malicious VMs might trigger discarding of IOMMU-mapped memory. The
* pages will remain pinned inside vfio until unmapped, resulting in a
@ -635,29 +601,7 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
" intended via an IOMMU. It's possible to mitigate "
" by setting/adjusting RLIMIT_MEMLOCK.");
}
/*
* Translation truncates length to the IOMMU page size,
* check that it did not truncate too much.
*/
if (len & iotlb->addr_mask) {
error_report("iommu has granularity incompatible with target AS");
return false;
}
if (vaddr) {
*vaddr = memory_region_get_ram_ptr(mr) + xlat;
}
if (ram_addr) {
*ram_addr = memory_region_get_ram_addr(mr) + xlat;
}
if (read_only) {
*read_only = !writable || mr->readonly;
}
return true;
return ret;
}
static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)

View file

@ -713,6 +713,10 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm,
void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
RamDiscardListener *rdl);
bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
ram_addr_t *ram_addr, bool *read_only,
bool *mr_has_discard_manager);
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;

View file

@ -33,6 +33,7 @@
#include "qemu/accel.h"
#include "hw/boards.h"
#include "migration/vmstate.h"
#include "exec/address-spaces.h"
//#define DEBUG_UNASSIGNED
@ -2121,6 +2122,77 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
rdmc->unregister_listener(rdm, rdl);
}
/* Called with rcu_read_lock held. */
bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
ram_addr_t *ram_addr, bool *read_only,
bool *mr_has_discard_manager)
{
MemoryRegion *mr;
hwaddr xlat;
hwaddr len = iotlb->addr_mask + 1;
bool writable = iotlb->perm & IOMMU_WO;
if (mr_has_discard_manager) {
*mr_has_discard_manager = false;
}
/*
* The IOMMU TLB entry we have just covers translation through
* this IOMMU to its immediate target. We need to translate
* it the rest of the way through to memory.
*/
mr = address_space_translate(&address_space_memory, iotlb->translated_addr,
&xlat, &len, writable, MEMTXATTRS_UNSPECIFIED);
if (!memory_region_is_ram(mr)) {
error_report("iommu map to non memory area %" HWADDR_PRIx "", xlat);
return false;
} else if (memory_region_has_ram_discard_manager(mr)) {
RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
MemoryRegionSection tmp = {
.mr = mr,
.offset_within_region = xlat,
.size = int128_make64(len),
};
if (mr_has_discard_manager) {
*mr_has_discard_manager = true;
}
/*
* Malicious VMs can map memory into the IOMMU, which is expected
* to remain discarded. vfio will pin all pages, populating memory.
* Disallow that. vmstate priorities make sure any RamDiscardManager
* were already restored before IOMMUs are restored.
*/
if (!ram_discard_manager_is_populated(rdm, &tmp)) {
error_report("iommu map to discarded memory (e.g., unplugged via"
" virtio-mem): %" HWADDR_PRIx "",
iotlb->translated_addr);
return false;
}
}
/*
* Translation truncates length to the IOMMU page size,
* check that it did not truncate too much.
*/
if (len & iotlb->addr_mask) {
error_report("iommu has granularity incompatible with target AS");
return false;
}
if (vaddr) {
*vaddr = memory_region_get_ram_ptr(mr) + xlat;
}
if (ram_addr) {
*ram_addr = memory_region_get_ram_addr(mr) + xlat;
}
if (read_only) {
*read_only = !writable || mr->readonly;
}
return true;
}
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
{
uint8_t mask = 1 << client;