diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 171c6ad..6d2ee2d 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -326,13 +326,15 @@ static void vfio_listener_region_add(VFIOMemoryListener *vlistener,
                                      MemoryRegionSection *section)
 {
     VFIOContainer *container = vlistener->container;
+    bool is_prereg = (vlistener == &container->prereg_listener);
     hwaddr iova, end;
     Int128 llend;
     void *vaddr;
     int ret;
     hwaddr page_mask = vfio_iommu_page_mask(section->mr);
 
-    if (vfio_listener_skipped_section(section)) {
+    if (vfio_listener_skipped_section(section) ||
+        (is_prereg && memory_region_is_skip_dump(section->mr))) {
         trace_vfio_listener_region_add_skip(
                 section->offset_within_address_space,
                 section->offset_within_address_space +
@@ -357,7 +359,7 @@ static void vfio_listener_region_add(VFIOMemoryListener *vlistener,
 
     memory_region_ref(section->mr);
 
-    if (memory_region_is_iommu(section->mr)) {
+    if (!is_prereg && memory_region_is_iommu(section->mr)) {
         VFIOGuestIOMMU *giommu;
 
         trace_vfio_listener_region_add_iommu(iova,
@@ -405,6 +407,33 @@ static void vfio_listener_region_add(VFIOMemoryListener *vlistener,
 
     trace_vfio_listener_region_add_ram(iova, end - 1, vaddr);
 
+    if (is_prereg) {
+        struct vfio_iommu_spapr_register_memory reg = {
+            .argsz = sizeof(reg),
+            .flags = 0,
+            .vaddr = (uint64_t) vaddr,
+            .size = end - iova
+        };
+
+        ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_REGISTER_MEMORY, &reg);
+        trace_vfio_ram_register(reg.vaddr, reg.size, ret ? -errno : 0);
+        if (ret) {
+            /*
+             * 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->initialized) {
+                if (!container->error) {
+                    container->error = ret;
+                }
+            } else {
+                hw_error("vfio: DMA mapping failed, unable to continue");
+            }
+        }
+        return;
+    }
+
     ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly);
     if (ret) {
         error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
@@ -430,11 +459,13 @@ static void vfio_listener_region_del(VFIOMemoryListener *vlistener,
                                      MemoryRegionSection *section)
 {
     VFIOContainer *container = vlistener->container;
+    bool is_prereg = (vlistener == &container->prereg_listener);
     hwaddr iova, end;
     int ret;
     hwaddr page_mask = vfio_iommu_page_mask(section->mr);
 
-    if (vfio_listener_skipped_section(section)) {
+    if (vfio_listener_skipped_section(section) ||
+        (is_prereg && memory_region_is_skip_dump(section->mr))) {
         trace_vfio_listener_region_del_skip(
                 section->offset_within_address_space,
                 section->offset_within_address_space +
@@ -448,7 +479,7 @@ static void vfio_listener_region_del(VFIOMemoryListener *vlistener,
         return;
     }
 
-    if (memory_region_is_iommu(section->mr)) {
+    if (!is_prereg && memory_region_is_iommu(section->mr)) {
         VFIOGuestIOMMU *giommu;
 
         QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) {
@@ -477,8 +508,24 @@ static void vfio_listener_region_del(VFIOMemoryListener *vlistener,
         return;
     }
 
+    if (is_prereg) {
+        void *vaddr = memory_region_get_ram_ptr(section->mr) +
+            section->offset_within_region +
+            (iova - section->offset_within_address_space);
+        struct vfio_iommu_spapr_register_memory reg = {
+            .argsz = sizeof(reg),
+            .flags = 0,
+            .vaddr = (uint64_t) vaddr,
+            .size = end - iova
+        };
+
+        ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY,
+                    &reg);
+        trace_vfio_ram_unregister(reg.vaddr, reg.size, ret ? -errno : 0);
+        return;
+    }
+
     trace_vfio_listener_region_del(iova, end - 1);
-
     ret = vfio_dma_unmap(container, iova, end - iova);
     memory_region_unref(section->mr);
     if (ret) {
@@ -512,9 +559,35 @@ static const MemoryListener vfio_iommu_listener = {
     .region_del = vfio_iommu_listener_region_del,
 };
 
+static void vfio_spapr_prereg_listener_region_add(MemoryListener *listener,
+                                                  MemoryRegionSection *section)
+{
+    VFIOMemoryListener *vlistener = container_of(listener, VFIOMemoryListener,
+                                                 listener);
+
+    vfio_listener_region_add(vlistener, section);
+}
+
+static void vfio_spapr_prereg_listener_region_del(MemoryListener *listener,
+                                                  MemoryRegionSection *section)
+{
+    VFIOMemoryListener *vlistener = container_of(listener, VFIOMemoryListener,
+                                                 listener);
+
+    vfio_listener_region_del(vlistener, section);
+}
+
+static const MemoryListener vfio_spapr_prereg_listener = {
+    .region_add = vfio_spapr_prereg_listener_region_add,
+    .region_del = vfio_spapr_prereg_listener_region_del,
+};
+
 static void vfio_listener_release(VFIOContainer *container)
 {
     memory_listener_unregister(&container->iommu_listener.listener);
+    if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+        memory_listener_unregister(&container->prereg_listener.listener);
+    }
 }
 
 int vfio_mmap_region(Object *obj, VFIORegion *region,
@@ -728,14 +801,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
 
         container->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;
         }
-        container->iommu_type = VFIO_SPAPR_TCE_IOMMU;
+        container->iommu_type =
+            v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU;
         ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
         if (ret) {
             error_report("vfio: failed to set iommu for container: %m");
@@ -748,11 +825,27 @@ 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;
+            }
+        } else {
+            container->prereg_listener.container = container;
+            container->prereg_listener.listener = vfio_spapr_prereg_listener;
+
+            memory_listener_register(&container->prereg_listener.listener,
+                                     &address_space_memory);
+            if (container->error) {
+                error_report("vfio: RAM memory listener initialization failed for container");
+                memory_listener_unregister(
+                    &container->prereg_listener.listener);
+                goto free_container_exit;
+            }
+
+            container->initialized = true;
         }
 
         container->iommu_listener.container = container;
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index a0f9d36..6ba8e67 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -78,6 +78,7 @@ typedef struct VFIOContainer {
     int error;
     bool initialized;
     VFIOMemoryListener iommu_listener;
+    VFIOMemoryListener prereg_listener;
     void (*release)(struct VFIOContainer *);
     QLIST_HEAD(, VFIOGuestIOMMU) giommu_list;
     QLIST_HEAD(, VFIOGroup) group_list;
diff --git a/trace-events b/trace-events
index d24d80a..f859ad0 100644
--- a/trace-events
+++ b/trace-events
@@ -1582,6 +1582,8 @@ 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(uint64_t va, uint64_t size, int ret) "va=%"PRIx64" size=%"PRIx64" ret=%d"
+vfio_ram_unregister(uint64_t va, uint64_t size, int ret) "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"
