| 1 | /* |
| 2 | * VFIO BASE CONTAINER |
| 3 | * |
| 4 | * Copyright (C) 2023 Intel Corporation. |
| 5 | * Copyright Red Hat, Inc. 2023 |
| 6 | * |
| 7 | * Authors: Yi Liu <yi.l.liu@intel.com> |
| 8 | * Eric Auger <eric.auger@redhat.com> |
| 9 | * |
| 10 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 11 | */ |
| 12 | |
| 13 | #ifndef HW_VFIO_VFIO_CONTAINER_H |
| 14 | #define HW_VFIO_VFIO_CONTAINER_H |
| 15 | |
| 16 | #include "system/memory.h" |
| 17 | |
| 18 | typedef struct VFIODevice VFIODevice; |
| 19 | typedef struct VFIOIOMMUClass VFIOIOMMUClass; |
| 20 | |
| 21 | typedef struct { |
| 22 | unsigned long *bitmap; |
| 23 | hwaddr size; |
| 24 | hwaddr pages; |
| 25 | } VFIOBitmap; |
| 26 | |
| 27 | typedef struct VFIOAddressSpace { |
| 28 | AddressSpace *as; |
| 29 | QLIST_HEAD(, VFIOContainer) containers; |
| 30 | QLIST_ENTRY(VFIOAddressSpace) list; |
| 31 | } VFIOAddressSpace; |
| 32 | |
| 33 | /* |
| 34 | * This is the base object for vfio container backends |
| 35 | */ |
| 36 | struct VFIOContainer { |
| 37 | Object parent_obj; |
| 38 | |
| 39 | VFIOAddressSpace *space; |
| 40 | MemoryListener listener; |
| 41 | Error *error; |
| 42 | bool initialized; |
| 43 | uint64_t dirty_pgsizes; |
| 44 | uint64_t max_dirty_bitmap_size; |
| 45 | unsigned long pgsizes; |
| 46 | unsigned int dma_max_mappings; |
| 47 | bool dirty_pages_supported; |
| 48 | bool dirty_pages_started; /* Protected by BQL */ |
| 49 | QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; |
| 50 | QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; |
| 51 | QLIST_ENTRY(VFIOContainer) next; |
| 52 | QLIST_HEAD(, VFIODevice) device_list; |
| 53 | GList *iova_ranges; |
| 54 | NotifierWithReturn cpr_reboot_notifier; |
| 55 | bool bypass_ro; |
| 56 | }; |
| 57 | |
| 58 | #define TYPE_VFIO_IOMMU "vfio-iommu" |
| 59 | OBJECT_DECLARE_TYPE(VFIOContainer, VFIOIOMMUClass, VFIO_IOMMU) |
| 60 | |
| 61 | typedef struct VFIOGuestIOMMU { |
| 62 | VFIOContainer *bcontainer; |
| 63 | IOMMUMemoryRegion *iommu_mr; |
| 64 | hwaddr iommu_offset; |
| 65 | IOMMUNotifier n; |
| 66 | QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; |
| 67 | } VFIOGuestIOMMU; |
| 68 | |
| 69 | typedef struct VFIORamDiscardListener { |
| 70 | VFIOContainer *bcontainer; |
| 71 | MemoryRegion *mr; |
| 72 | hwaddr offset_within_address_space; |
| 73 | hwaddr size; |
| 74 | uint64_t granularity; |
| 75 | RamDiscardListener listener; |
| 76 | QLIST_ENTRY(VFIORamDiscardListener) next; |
| 77 | } VFIORamDiscardListener; |
| 78 | |
| 79 | VFIOAddressSpace *vfio_address_space_get(AddressSpace *as); |
| 80 | void vfio_address_space_put(VFIOAddressSpace *space); |
| 81 | void vfio_address_space_insert(VFIOAddressSpace *space, |
| 82 | VFIOContainer *bcontainer); |
| 83 | |
| 84 | int vfio_container_dma_map(VFIOContainer *bcontainer, |
| 85 | hwaddr iova, uint64_t size, |
| 86 | void *vaddr, bool readonly, MemoryRegion *mr); |
| 87 | int vfio_container_dma_unmap(VFIOContainer *bcontainer, |
| 88 | hwaddr iova, uint64_t size, |
| 89 | IOMMUTLBEntry *iotlb, bool unmap_all); |
| 90 | bool vfio_container_add_section_window(VFIOContainer *bcontainer, |
| 91 | MemoryRegionSection *section, |
| 92 | Error **errp); |
| 93 | void vfio_container_del_section_window(VFIOContainer *bcontainer, |
| 94 | MemoryRegionSection *section); |
| 95 | int vfio_container_set_dirty_page_tracking(VFIOContainer *bcontainer, |
| 96 | bool start, Error **errp); |
| 97 | bool vfio_container_dirty_tracking_is_started( |
| 98 | const VFIOContainer *bcontainer); |
| 99 | bool vfio_container_devices_dirty_tracking_is_supported( |
| 100 | const VFIOContainer *bcontainer); |
| 101 | int vfio_container_query_dirty_bitmap(const VFIOContainer *bcontainer, |
| 102 | uint64_t iova, uint64_t size, |
| 103 | uint64_t backend_flag, |
| 104 | hwaddr translated_addr, |
| 105 | Error **errp); |
| 106 | |
| 107 | GList *vfio_container_get_iova_ranges(const VFIOContainer *bcontainer); |
| 108 | |
| 109 | static inline uint64_t |
| 110 | vfio_container_get_page_size_mask(const VFIOContainer *bcontainer) |
| 111 | { |
| 112 | assert(bcontainer); |
| 113 | return bcontainer->pgsizes; |
| 114 | } |
| 115 | |
| 116 | #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" |
| 117 | #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" |
| 118 | #define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" |
| 119 | #define TYPE_VFIO_IOMMU_USER TYPE_VFIO_IOMMU "-user" |
| 120 | |
| 121 | struct VFIOIOMMUClass { |
| 122 | ObjectClass parent_class; |
| 123 | |
| 124 | /** |
| 125 | * @setup |
| 126 | * |
| 127 | * Perform basic setup of the container, including configuring IOMMU |
| 128 | * capabilities, IOVA ranges, supported page sizes, etc. |
| 129 | * |
| 130 | * @bcontainer: #VFIOContainer |
| 131 | * @errp: pointer to Error*, to store an error if it happens. |
| 132 | * |
| 133 | * Returns true to indicate success and false for error. |
| 134 | */ |
| 135 | bool (*setup)(VFIOContainer *bcontainer, Error **errp); |
| 136 | |
| 137 | /** |
| 138 | * @listener_begin |
| 139 | * |
| 140 | * Called at the beginning of an address space update transaction. |
| 141 | * See #MemoryListener. |
| 142 | * |
| 143 | * @bcontainer: #VFIOContainer |
| 144 | */ |
| 145 | void (*listener_begin)(VFIOContainer *bcontainer); |
| 146 | |
| 147 | /** |
| 148 | * @listener_commit |
| 149 | * |
| 150 | * Called at the end of an address space update transaction, |
| 151 | * See #MemoryListener. |
| 152 | * |
| 153 | * @bcontainer: #VFIOContainer |
| 154 | */ |
| 155 | void (*listener_commit)(VFIOContainer *bcontainer); |
| 156 | |
| 157 | /** |
| 158 | * @dma_map |
| 159 | * |
| 160 | * Map an address range into the container. Note that the memory region is |
| 161 | * referenced within an RCU read lock region across this call. |
| 162 | * |
| 163 | * @bcontainer: #VFIOContainer to use |
| 164 | * @iova: start address to map |
| 165 | * @size: size of the range to map |
| 166 | * @vaddr: process virtual address of mapping |
| 167 | * @readonly: true if mapping should be readonly |
| 168 | * @mr: the memory region for this mapping |
| 169 | * |
| 170 | * Returns 0 to indicate success and -errno otherwise. |
| 171 | */ |
| 172 | int (*dma_map)(const VFIOContainer *bcontainer, |
| 173 | hwaddr iova, uint64_t size, |
| 174 | void *vaddr, bool readonly, MemoryRegion *mr); |
| 175 | /** |
| 176 | * @dma_unmap |
| 177 | * |
| 178 | * Unmap an address range from the container. |
| 179 | * |
| 180 | * @bcontainer: #VFIOContainer to use for unmap |
| 181 | * @iova: start address to unmap |
| 182 | * @size: size of the range to unmap |
| 183 | * @iotlb: The IOMMU TLB mapping entry (or NULL) |
| 184 | * @unmap_all: if set, unmap the entire address space |
| 185 | * |
| 186 | * Returns 0 to indicate success and -errno otherwise. |
| 187 | */ |
| 188 | int (*dma_unmap)(const VFIOContainer *bcontainer, |
| 189 | hwaddr iova, uint64_t size, |
| 190 | IOMMUTLBEntry *iotlb, bool unmap_all); |
| 191 | |
| 192 | |
| 193 | /** |
| 194 | * @attach_device |
| 195 | * |
| 196 | * Associate the given device with a container and do some related |
| 197 | * initialization of the device context. |
| 198 | * |
| 199 | * @name: name of the device |
| 200 | * @vbasedev: the device |
| 201 | * @as: address space to use |
| 202 | * @errp: pointer to Error*, to store an error if it happens. |
| 203 | * |
| 204 | * Returns true to indicate success and false for error. |
| 205 | */ |
| 206 | bool (*attach_device)(const char *name, VFIODevice *vbasedev, |
| 207 | AddressSpace *as, Error **errp); |
| 208 | |
| 209 | /* |
| 210 | * @detach_device |
| 211 | * |
| 212 | * Detach the given device from its container and clean up any necessary |
| 213 | * state. |
| 214 | * |
| 215 | * @vbasedev: the device to disassociate |
| 216 | */ |
| 217 | void (*detach_device)(VFIODevice *vbasedev); |
| 218 | |
| 219 | /* migration feature */ |
| 220 | |
| 221 | /** |
| 222 | * @set_dirty_page_tracking |
| 223 | * |
| 224 | * Start or stop dirty pages tracking on VFIO container |
| 225 | * |
| 226 | * @bcontainer: #VFIOContainer on which to de/activate dirty |
| 227 | * page tracking |
| 228 | * @start: indicates whether to start or stop dirty pages tracking |
| 229 | * @errp: pointer to Error*, to store an error if it happens. |
| 230 | * |
| 231 | * Returns zero to indicate success and negative for error. |
| 232 | */ |
| 233 | int (*set_dirty_page_tracking)(const VFIOContainer *bcontainer, |
| 234 | bool start, Error **errp); |
| 235 | /** |
| 236 | * @query_dirty_bitmap |
| 237 | * |
| 238 | * Get bitmap of dirty pages from container |
| 239 | * |
| 240 | * @bcontainer: #VFIOContainer from which to get dirty pages |
| 241 | * @vbmap: #VFIOBitmap internal bitmap structure |
| 242 | * @iova: iova base address |
| 243 | * @size: size of iova range |
| 244 | * @backend_flag: flags for backend, opaque to upper layer container |
| 245 | * @errp: pointer to Error*, to store an error if it happens. |
| 246 | * |
| 247 | * Returns zero to indicate success and negative for error. |
| 248 | */ |
| 249 | int (*query_dirty_bitmap)(const VFIOContainer *bcontainer, |
| 250 | VFIOBitmap *vbmap, hwaddr iova, hwaddr size, |
| 251 | uint64_t backend_flag, Error **errp); |
| 252 | /* PCI specific */ |
| 253 | int (*pci_hot_reset)(VFIODevice *vbasedev, bool single); |
| 254 | |
| 255 | /* SPAPR specific */ |
| 256 | bool (*add_window)(VFIOContainer *bcontainer, |
| 257 | MemoryRegionSection *section, |
| 258 | Error **errp); |
| 259 | void (*del_window)(VFIOContainer *bcontainer, |
| 260 | MemoryRegionSection *section); |
| 261 | void (*release)(VFIOContainer *bcontainer); |
| 262 | }; |
| 263 | |
| 264 | VFIORamDiscardListener *vfio_find_ram_discard_listener( |
| 265 | VFIOContainer *bcontainer, const MemoryRegionSection *section); |
| 266 | |
| 267 | void vfio_container_region_add(VFIOContainer *bcontainer, |
| 268 | MemoryRegionSection *section, bool cpr_remap); |
| 269 | |
| 270 | #endif /* HW_VFIO_VFIO_CONTAINER_H */ |