@@ -577,14 +577,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
container->iommu_data.type1.initialized = true;
- } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) {
+ } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) ||
+ ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) {
+ bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU);
+
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
error_report("vfio: failed to set group container: %m");
ret = -errno;
goto free_container_exit;
}
- ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_SPAPR_TCE_IOMMU);
+ ret = ioctl(fd, VFIO_SET_IOMMU,
+ v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU);
if (ret) {
error_report("vfio: failed to set iommu for container: %m");
ret = -errno;
@@ -596,14 +600,20 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
* when container fd is closed so we do not call it explicitly
* in this file.
*/
- ret = ioctl(fd, VFIO_IOMMU_ENABLE);
- if (ret) {
- error_report("vfio: failed to enable container: %m");
- ret = -errno;
- goto free_container_exit;
+ if (!v2) {
+ ret = ioctl(fd, VFIO_IOMMU_ENABLE);
+ if (ret) {
+ error_report("vfio: failed to enable container: %m");
+ ret = -errno;
+ goto free_container_exit;
+ }
}
- spapr_memory_listener_register(container);
+ ret = spapr_memory_listener_register(container, v2 ? 2 : 1);
+ if (ret) {
+ error_report("vfio: RAM memory listener initialization failed for container");
+ goto listener_release_exit;
+ }
} else {
error_report("vfio: No available IOMMU models");
@@ -17,6 +17,9 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <sys/ioctl.h>
+#include <linux/vfio.h>
+
#include "hw/vfio/vfio-common.h"
#include "qemu/error-report.h"
#include "trace.h"
@@ -214,16 +217,99 @@ static const MemoryListener vfio_spapr_memory_listener = {
.region_del = vfio_spapr_listener_region_del,
};
+static void vfio_ram_do_region(VFIOContainer *container,
+ MemoryRegionSection *section, unsigned long req)
+{
+ int ret;
+ struct vfio_iommu_spapr_register_memory reg = { .argsz = sizeof(reg) };
+
+ if (!memory_region_is_ram(section->mr) ||
+ memory_region_is_skip_dump(section->mr)) {
+ return;
+ }
+
+ if (unlikely((section->offset_within_region & (getpagesize() - 1)))) {
+ error_report("%s received unaligned region", __func__);
+ return;
+ }
+
+ reg.vaddr = (__u64) memory_region_get_ram_ptr(section->mr) +
+ section->offset_within_region;
+ reg.size = ROUND_UP(int128_get64(section->size), TARGET_PAGE_SIZE);
+
+ ret = ioctl(container->fd, req, ®);
+ trace_vfio_ram_register(_IOC_NR(req) - VFIO_BASE, reg.vaddr, reg.size,
+ ret ? -errno : 0);
+ if (!ret) {
+ return;
+ }
+
+ /*
+ * On the initfn path, store the first error in the container so we
+ * can gracefully fail. Runtime, there's not much we can do other
+ * than throw a hardware error.
+ */
+ if (!container->iommu_data.spapr.ram_reg_initialized) {
+ if (!container->iommu_data.spapr.ram_reg_error) {
+ container->iommu_data.spapr.ram_reg_error = -errno;
+ }
+ } else {
+ hw_error("vfio: RAM registering failed, unable to continue");
+ }
+}
+
+static void vfio_spapr_ram_listener_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer,
+ iommu_data.spapr.register_listener);
+ memory_region_ref(section->mr);
+ vfio_ram_do_region(container, section, VFIO_IOMMU_SPAPR_REGISTER_MEMORY);
+}
+
+static void vfio_spapr_ram_listener_region_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer,
+ iommu_data.spapr.register_listener);
+ vfio_ram_do_region(container, section, VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY);
+ memory_region_unref(section->mr);
+}
+
+static const MemoryListener vfio_spapr_ram_memory_listener = {
+ .region_add = vfio_spapr_ram_listener_region_add,
+ .region_del = vfio_spapr_ram_listener_region_del,
+};
+
static void vfio_spapr_listener_release(VFIOContainer *container)
{
memory_listener_unregister(&container->iommu_data.spapr.listener);
}
-void spapr_memory_listener_register(VFIOContainer *container)
+static void vfio_spapr_listener_release_v2(VFIOContainer *container)
+{
+ memory_listener_unregister(&container->iommu_data.spapr.listener);
+ vfio_spapr_listener_release(container);
+}
+
+int spapr_memory_listener_register(VFIOContainer *container, int ver)
{
container->iommu_data.spapr.listener = vfio_spapr_memory_listener;
container->iommu_data.release = vfio_spapr_listener_release;
memory_listener_register(&container->iommu_data.spapr.listener,
container->space->as);
+ if (ver < 2) {
+ return 0;
+ }
+
+ container->iommu_data.spapr.register_listener =
+ vfio_spapr_ram_memory_listener;
+ container->iommu_data.release = vfio_spapr_listener_release_v2;
+ memory_listener_register(&container->iommu_data.spapr.register_listener,
+ &address_space_memory);
+
+ container->iommu_data.spapr.ram_reg_initialized = true;
+
+ return container->iommu_data.spapr.ram_reg_error;
}
@@ -72,6 +72,9 @@ typedef struct VFIOType1 {
typedef struct VFIOSPAPR {
MemoryListener listener;
+ MemoryListener register_listener;
+ int ram_reg_error;
+ bool ram_reg_initialized;
} VFIOSPAPR;
typedef struct VFIOContainer {
@@ -157,6 +160,6 @@ extern int vfio_dma_unmap(VFIOContainer *container,
hwaddr iova, ram_addr_t size);
bool vfio_listener_skipped_section(MemoryRegionSection *section);
-extern void spapr_memory_listener_register(VFIOContainer *container);
+extern int spapr_memory_listener_register(VFIOContainer *container, int ver);
#endif /* !HW_VFIO_VFIO_COMMON_H */
@@ -1584,6 +1584,7 @@ vfio_disconnect_container(int fd) "close container->fd=%d"
vfio_put_group(int fd) "close group->fd=%d"
vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u"
vfio_put_base_device(int fd) "close vdev->fd=%d"
+vfio_ram_register(int req, uint64_t va, uint64_t size, int ret) "req=%d va=%"PRIx64" size=%"PRIx64" ret=%d"
# hw/vfio/platform.c
vfio_platform_populate_regions(int region_index, unsigned long flag, unsigned long size, int fd, unsigned long offset) "- region %d flags = 0x%lx, size = 0x%lx, fd= %d, offset = 0x%lx"