vdpa: Check for iova range at mappings changes

Check vdpa device range before updating memory regions so we don't add
any outside of it, and report the invalid change if any.

Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
Message-Id: <20211014141236.923287-4-eperezma@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
This commit is contained in:
Eugenio Pérez 2021-10-14 16:12:36 +02:00 committed by Michael S. Tsirkin
parent 032e4d686e
commit 013108b6e5
3 changed files with 50 additions and 15 deletions

View File

@ -52,6 +52,7 @@ vhost_vdpa_set_vring_call(void *dev, unsigned int index, int fd) "dev: %p index:
vhost_vdpa_get_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64
vhost_vdpa_set_owner(void *dev) "dev: %p"
vhost_vdpa_vq_get_addr(void *dev, void *vq, uint64_t desc_user_addr, uint64_t avail_user_addr, uint64_t used_user_addr) "dev: %p vq: %p desc_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64
vhost_vdpa_get_iova_range(void *dev, uint64_t first, uint64_t last) "dev: %p first: 0x%"PRIx64" last: 0x%"PRIx64
# virtio.c
virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u"

View File

@ -37,20 +37,36 @@ static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section)
return llend;
}
static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section)
static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section,
uint64_t iova_min,
uint64_t iova_max)
{
return (!memory_region_is_ram(section->mr) &&
!memory_region_is_iommu(section->mr)) ||
memory_region_is_protected(section->mr) ||
/* vhost-vDPA doesn't allow MMIO to be mapped */
memory_region_is_ram_device(section->mr) ||
/*
* Sizing an enabled 64-bit BAR can cause spurious mappings to
* addresses in the upper part of the 64-bit address space. These
* are never accessed by the CPU and beyond the address width of
* some IOMMU hardware. TODO: VDPA should tell us the IOMMU width.
*/
section->offset_within_address_space & (1ULL << 63);
Int128 llend;
if ((!memory_region_is_ram(section->mr) &&
!memory_region_is_iommu(section->mr)) ||
memory_region_is_protected(section->mr) ||
/* vhost-vDPA doesn't allow MMIO to be mapped */
memory_region_is_ram_device(section->mr)) {
return true;
}
if (section->offset_within_address_space < iova_min) {
error_report("RAM section out of device range (min=0x%" PRIx64
", addr=0x%" HWADDR_PRIx ")",
iova_min, section->offset_within_address_space);
return true;
}
llend = vhost_vdpa_section_end(section);
if (int128_gt(llend, int128_make64(iova_max))) {
error_report("RAM section out of device range (max=0x%" PRIx64
", end addr=0x%" PRIx64 ")",
iova_max, int128_get64(llend));
return true;
}
return false;
}
static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
@ -162,7 +178,8 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
void *vaddr;
int ret;
if (vhost_vdpa_listener_skipped_section(section)) {
if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first,
v->iova_range.last)) {
return;
}
@ -220,7 +237,8 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener,
Int128 llend, llsize;
int ret;
if (vhost_vdpa_listener_skipped_section(section)) {
if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first,
v->iova_range.last)) {
return;
}
@ -288,6 +306,19 @@ static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status)
vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s);
}
static void vhost_vdpa_get_iova_range(struct vhost_vdpa *v)
{
int ret = vhost_vdpa_call(v->dev, VHOST_VDPA_GET_IOVA_RANGE,
&v->iova_range);
if (ret != 0) {
v->iova_range.first = 0;
v->iova_range.last = UINT64_MAX;
}
trace_vhost_vdpa_get_iova_range(v->dev, v->iova_range.first,
v->iova_range.last);
}
static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
{
struct vhost_vdpa *v;
@ -300,6 +331,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
v->listener = vhost_vdpa_memory_listener;
v->msg_type = VHOST_IOTLB_MSG_V2;
vhost_vdpa_get_iova_range(v);
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);

View File

@ -13,6 +13,7 @@
#define HW_VIRTIO_VHOST_VDPA_H
#include "hw/virtio/virtio.h"
#include "standard-headers/linux/vhost_types.h"
typedef struct VhostVDPAHostNotifier {
MemoryRegion mr;
@ -24,6 +25,7 @@ typedef struct vhost_vdpa {
uint32_t msg_type;
bool iotlb_batch_begin_sent;
MemoryListener listener;
struct vhost_vdpa_iova_range iova_range;
struct vhost_dev *dev;
VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX];
} VhostVDPA;