2 * DMA memory preregistration
5 * Alexey Kardashevskiy <aik@ozlabs.ru>
7 * This work is licensed under the terms of the GNU GPL, version 2. See
8 * the COPYING file in the top-level directory.
11 #include "qemu/osdep.h"
13 #include <sys/ioctl.h>
14 #include <linux/vfio.h>
16 #include "hw/vfio/vfio-common.h"
18 #include "exec/ram_addr.h"
19 #include "qemu/error-report.h"
22 static bool vfio_prereg_listener_skipped_section(MemoryRegionSection
*section
)
24 if (memory_region_is_iommu(section
->mr
)) {
25 hw_error("Cannot possibly preregister IOMMU memory");
28 return !memory_region_is_ram(section
->mr
) ||
29 memory_region_is_ram_device(section
->mr
);
32 static void *vfio_prereg_gpa_to_vaddr(MemoryRegionSection
*section
, hwaddr gpa
)
34 return memory_region_get_ram_ptr(section
->mr
) +
35 section
->offset_within_region
+
36 (gpa
- section
->offset_within_address_space
);
39 static void vfio_prereg_listener_region_add(MemoryListener
*listener
,
40 MemoryRegionSection
*section
)
42 VFIOContainer
*container
= container_of(listener
, VFIOContainer
,
44 const hwaddr gpa
= section
->offset_within_address_space
;
47 hwaddr page_mask
= qemu_real_host_page_mask
;
48 struct vfio_iommu_spapr_register_memory reg
= {
53 if (vfio_prereg_listener_skipped_section(section
)) {
54 trace_vfio_prereg_listener_region_add_skip(
55 section
->offset_within_address_space
,
56 section
->offset_within_address_space
+
57 int128_get64(int128_sub(section
->size
, int128_one())));
61 if (unlikely((section
->offset_within_address_space
& ~page_mask
) ||
62 (section
->offset_within_region
& ~page_mask
) ||
63 (int128_get64(section
->size
) & ~page_mask
))) {
64 error_report("%s received unaligned region", __func__
);
68 end
= section
->offset_within_address_space
+ int128_get64(section
->size
);
73 memory_region_ref(section
->mr
);
75 reg
.vaddr
= (uintptr_t) vfio_prereg_gpa_to_vaddr(section
, gpa
);
78 ret
= ioctl(container
->fd
, VFIO_IOMMU_SPAPR_REGISTER_MEMORY
, ®
);
79 trace_vfio_prereg_register(reg
.vaddr
, reg
.size
, ret
? -errno
: 0);
82 * On the initfn path, store the first error in the container so we
83 * can gracefully fail. Runtime, there's not much we can do other
84 * than throw a hardware error.
86 if (!container
->initialized
) {
87 if (!container
->error
) {
88 container
->error
= ret
;
91 hw_error("vfio: Memory registering failed, unable to continue");
96 static void vfio_prereg_listener_region_del(MemoryListener
*listener
,
97 MemoryRegionSection
*section
)
99 VFIOContainer
*container
= container_of(listener
, VFIOContainer
,
101 const hwaddr gpa
= section
->offset_within_address_space
;
104 hwaddr page_mask
= qemu_real_host_page_mask
;
105 struct vfio_iommu_spapr_register_memory reg
= {
106 .argsz
= sizeof(reg
),
110 if (vfio_prereg_listener_skipped_section(section
)) {
111 trace_vfio_prereg_listener_region_del_skip(
112 section
->offset_within_address_space
,
113 section
->offset_within_address_space
+
114 int128_get64(int128_sub(section
->size
, int128_one())));
118 if (unlikely((section
->offset_within_address_space
& ~page_mask
) ||
119 (section
->offset_within_region
& ~page_mask
) ||
120 (int128_get64(section
->size
) & ~page_mask
))) {
121 error_report("%s received unaligned region", __func__
);
125 end
= section
->offset_within_address_space
+ int128_get64(section
->size
);
130 reg
.vaddr
= (uintptr_t) vfio_prereg_gpa_to_vaddr(section
, gpa
);
131 reg
.size
= end
- gpa
;
133 ret
= ioctl(container
->fd
, VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY
, ®
);
134 trace_vfio_prereg_unregister(reg
.vaddr
, reg
.size
, ret
? -errno
: 0);
137 const MemoryListener vfio_prereg_listener
= {
138 .region_add
= vfio_prereg_listener_region_add
,
139 .region_del
= vfio_prereg_listener_region_del
,
142 int vfio_spapr_create_window(VFIOContainer
*container
,
143 MemoryRegionSection
*section
,
147 IOMMUMemoryRegion
*iommu_mr
= IOMMU_MEMORY_REGION(section
->mr
);
148 uint64_t pagesize
= memory_region_iommu_get_min_page_size(iommu_mr
);
149 unsigned entries
, pages
;
150 struct vfio_iommu_spapr_tce_create create
= { .argsz
= sizeof(create
) };
151 long systempagesize
= qemu_getrampagesize();
154 * The host might not support the guest supported IOMMU page size,
155 * so we will use smaller physical IOMMU pages to back them.
157 if (pagesize
> systempagesize
) {
158 pagesize
= systempagesize
;
160 pagesize
= 1ULL << (63 - clz64(container
->pgsizes
&
161 (pagesize
| (pagesize
- 1))));
163 error_report("Host doesn't support page size 0x%"PRIx64
164 ", the supported mask is 0x%lx",
165 memory_region_iommu_get_min_page_size(iommu_mr
),
171 * FIXME: For VFIO iommu types which have KVM acceleration to
172 * avoid bouncing all map/unmaps through qemu this way, this
173 * would be the right place to wire that up (tell the KVM
174 * device emulation the VFIO iommu handles to use).
176 create
.window_size
= int128_get64(section
->size
);
177 create
.page_shift
= ctz64(pagesize
);
179 * SPAPR host supports multilevel TCE tables, there is some
180 * heuristic to decide how many levels we want for our table:
181 * 0..64 = 1; 65..4096 = 2; 4097..262144 = 3; 262145.. = 4
183 entries
= create
.window_size
>> create
.page_shift
;
184 pages
= MAX((entries
* sizeof(uint64_t)) / getpagesize(), 1);
185 pages
= MAX(pow2ceil(pages
), 1); /* Round up */
186 create
.levels
= ctz64(pages
) / 6 + 1;
188 ret
= ioctl(container
->fd
, VFIO_IOMMU_SPAPR_TCE_CREATE
, &create
);
190 error_report("Failed to create a window, ret = %d (%m)", ret
);
194 if (create
.start_addr
!= section
->offset_within_address_space
) {
195 vfio_spapr_remove_window(container
, create
.start_addr
);
197 error_report("Host doesn't support DMA window at %"HWADDR_PRIx
", must be %"PRIx64
,
198 section
->offset_within_address_space
,
199 (uint64_t)create
.start_addr
);
202 trace_vfio_spapr_create_window(create
.page_shift
,
210 int vfio_spapr_remove_window(VFIOContainer
*container
,
211 hwaddr offset_within_address_space
)
213 struct vfio_iommu_spapr_tce_remove remove
= {
214 .argsz
= sizeof(remove
),
215 .start_addr
= offset_within_address_space
,
219 ret
= ioctl(container
->fd
, VFIO_IOMMU_SPAPR_TCE_REMOVE
, &remove
);
221 error_report("Failed to remove window at %"PRIx64
,
222 (uint64_t)remove
.start_addr
);
226 trace_vfio_spapr_remove_window(offset_within_address_space
);