1d6aca350SJuergen Gross // SPDX-License-Identifier: GPL-2.0-only 2d6aca350SJuergen Gross /* 3d6aca350SJuergen Gross * Xen grant DMA-mapping layer - contains special DMA-mapping routines 4d6aca350SJuergen Gross * for providing grant references as DMA addresses to be used by frontends 5d6aca350SJuergen Gross * (e.g. virtio) in Xen guests 6d6aca350SJuergen Gross * 7d6aca350SJuergen Gross * Copyright (c) 2021, Juergen Gross <jgross@suse.com> 8d6aca350SJuergen Gross */ 9d6aca350SJuergen Gross 10d6aca350SJuergen Gross #include <linux/module.h> 11d6aca350SJuergen Gross #include <linux/dma-map-ops.h> 12d6aca350SJuergen Gross #include <linux/of.h> 13d6aca350SJuergen Gross #include <linux/pfn.h> 14d6aca350SJuergen Gross #include <linux/xarray.h> 15251e90e7SJuergen Gross #include <linux/virtio_anchor.h> 16251e90e7SJuergen Gross #include <linux/virtio.h> 17d6aca350SJuergen Gross #include <xen/xen.h> 18d6aca350SJuergen Gross #include <xen/xen-ops.h> 19d6aca350SJuergen Gross #include <xen/grant_table.h> 20d6aca350SJuergen Gross 21d6aca350SJuergen Gross struct xen_grant_dma_data { 22d6aca350SJuergen Gross /* The ID of backend domain */ 23d6aca350SJuergen Gross domid_t backend_domid; 24d6aca350SJuergen Gross /* Is device behaving sane? */ 25d6aca350SJuergen Gross bool broken; 26d6aca350SJuergen Gross }; 27d6aca350SJuergen Gross 28d6aca350SJuergen Gross static DEFINE_XARRAY(xen_grant_dma_devices); 29d6aca350SJuergen Gross 30d6aca350SJuergen Gross #define XEN_GRANT_DMA_ADDR_OFF (1ULL << 63) 31d6aca350SJuergen Gross 32d6aca350SJuergen Gross static inline dma_addr_t grant_to_dma(grant_ref_t grant) 33d6aca350SJuergen Gross { 34d6aca350SJuergen Gross return XEN_GRANT_DMA_ADDR_OFF | ((dma_addr_t)grant << PAGE_SHIFT); 35d6aca350SJuergen Gross } 36d6aca350SJuergen Gross 37d6aca350SJuergen Gross static inline grant_ref_t dma_to_grant(dma_addr_t dma) 38d6aca350SJuergen Gross { 39d6aca350SJuergen Gross return (grant_ref_t)((dma & ~XEN_GRANT_DMA_ADDR_OFF) >> PAGE_SHIFT); 40d6aca350SJuergen Gross } 41d6aca350SJuergen Gross 42d6aca350SJuergen Gross static struct xen_grant_dma_data *find_xen_grant_dma_data(struct device *dev) 43d6aca350SJuergen Gross { 44d6aca350SJuergen Gross struct xen_grant_dma_data *data; 45d6aca350SJuergen Gross 46d6aca350SJuergen Gross xa_lock(&xen_grant_dma_devices); 47d6aca350SJuergen Gross data = xa_load(&xen_grant_dma_devices, (unsigned long)dev); 48d6aca350SJuergen Gross xa_unlock(&xen_grant_dma_devices); 49d6aca350SJuergen Gross 50d6aca350SJuergen Gross return data; 51d6aca350SJuergen Gross } 52d6aca350SJuergen Gross 53d6aca350SJuergen Gross /* 54d6aca350SJuergen Gross * DMA ops for Xen frontends (e.g. virtio). 55d6aca350SJuergen Gross * 56d6aca350SJuergen Gross * Used to act as a kind of software IOMMU for Xen guests by using grants as 57d6aca350SJuergen Gross * DMA addresses. 58d6aca350SJuergen Gross * Such a DMA address is formed by using the grant reference as a frame 59d6aca350SJuergen Gross * number and setting the highest address bit (this bit is for the backend 60d6aca350SJuergen Gross * to be able to distinguish it from e.g. a mmio address). 61d6aca350SJuergen Gross */ 62d6aca350SJuergen Gross static void *xen_grant_dma_alloc(struct device *dev, size_t size, 63d6aca350SJuergen Gross dma_addr_t *dma_handle, gfp_t gfp, 64d6aca350SJuergen Gross unsigned long attrs) 65d6aca350SJuergen Gross { 66d6aca350SJuergen Gross struct xen_grant_dma_data *data; 67d6aca350SJuergen Gross unsigned int i, n_pages = PFN_UP(size); 68d6aca350SJuergen Gross unsigned long pfn; 69d6aca350SJuergen Gross grant_ref_t grant; 70d6aca350SJuergen Gross void *ret; 71d6aca350SJuergen Gross 72d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 73d6aca350SJuergen Gross if (!data) 74d6aca350SJuergen Gross return NULL; 75d6aca350SJuergen Gross 76d6aca350SJuergen Gross if (unlikely(data->broken)) 77d6aca350SJuergen Gross return NULL; 78d6aca350SJuergen Gross 79d6aca350SJuergen Gross ret = alloc_pages_exact(n_pages * PAGE_SIZE, gfp); 80d6aca350SJuergen Gross if (!ret) 81d6aca350SJuergen Gross return NULL; 82d6aca350SJuergen Gross 83d6aca350SJuergen Gross pfn = virt_to_pfn(ret); 84d6aca350SJuergen Gross 85d6aca350SJuergen Gross if (gnttab_alloc_grant_reference_seq(n_pages, &grant)) { 86d6aca350SJuergen Gross free_pages_exact(ret, n_pages * PAGE_SIZE); 87d6aca350SJuergen Gross return NULL; 88d6aca350SJuergen Gross } 89d6aca350SJuergen Gross 90d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 91d6aca350SJuergen Gross gnttab_grant_foreign_access_ref(grant + i, data->backend_domid, 92d6aca350SJuergen Gross pfn_to_gfn(pfn + i), 0); 93d6aca350SJuergen Gross } 94d6aca350SJuergen Gross 95d6aca350SJuergen Gross *dma_handle = grant_to_dma(grant); 96d6aca350SJuergen Gross 97d6aca350SJuergen Gross return ret; 98d6aca350SJuergen Gross } 99d6aca350SJuergen Gross 100d6aca350SJuergen Gross static void xen_grant_dma_free(struct device *dev, size_t size, void *vaddr, 101d6aca350SJuergen Gross dma_addr_t dma_handle, unsigned long attrs) 102d6aca350SJuergen Gross { 103d6aca350SJuergen Gross struct xen_grant_dma_data *data; 104d6aca350SJuergen Gross unsigned int i, n_pages = PFN_UP(size); 105d6aca350SJuergen Gross grant_ref_t grant; 106d6aca350SJuergen Gross 107d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 108d6aca350SJuergen Gross if (!data) 109d6aca350SJuergen Gross return; 110d6aca350SJuergen Gross 111d6aca350SJuergen Gross if (unlikely(data->broken)) 112d6aca350SJuergen Gross return; 113d6aca350SJuergen Gross 114d6aca350SJuergen Gross grant = dma_to_grant(dma_handle); 115d6aca350SJuergen Gross 116d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 117d6aca350SJuergen Gross if (unlikely(!gnttab_end_foreign_access_ref(grant + i))) { 118d6aca350SJuergen Gross dev_alert(dev, "Grant still in use by backend domain, disabled for further use\n"); 119d6aca350SJuergen Gross data->broken = true; 120d6aca350SJuergen Gross return; 121d6aca350SJuergen Gross } 122d6aca350SJuergen Gross } 123d6aca350SJuergen Gross 124d6aca350SJuergen Gross gnttab_free_grant_reference_seq(grant, n_pages); 125d6aca350SJuergen Gross 126d6aca350SJuergen Gross free_pages_exact(vaddr, n_pages * PAGE_SIZE); 127d6aca350SJuergen Gross } 128d6aca350SJuergen Gross 129d6aca350SJuergen Gross static struct page *xen_grant_dma_alloc_pages(struct device *dev, size_t size, 130d6aca350SJuergen Gross dma_addr_t *dma_handle, 131d6aca350SJuergen Gross enum dma_data_direction dir, 132d6aca350SJuergen Gross gfp_t gfp) 133d6aca350SJuergen Gross { 134d6aca350SJuergen Gross void *vaddr; 135d6aca350SJuergen Gross 136d6aca350SJuergen Gross vaddr = xen_grant_dma_alloc(dev, size, dma_handle, gfp, 0); 137d6aca350SJuergen Gross if (!vaddr) 138d6aca350SJuergen Gross return NULL; 139d6aca350SJuergen Gross 140d6aca350SJuergen Gross return virt_to_page(vaddr); 141d6aca350SJuergen Gross } 142d6aca350SJuergen Gross 143d6aca350SJuergen Gross static void xen_grant_dma_free_pages(struct device *dev, size_t size, 144d6aca350SJuergen Gross struct page *vaddr, dma_addr_t dma_handle, 145d6aca350SJuergen Gross enum dma_data_direction dir) 146d6aca350SJuergen Gross { 147d6aca350SJuergen Gross xen_grant_dma_free(dev, size, page_to_virt(vaddr), dma_handle, 0); 148d6aca350SJuergen Gross } 149d6aca350SJuergen Gross 150d6aca350SJuergen Gross static dma_addr_t xen_grant_dma_map_page(struct device *dev, struct page *page, 151d6aca350SJuergen Gross unsigned long offset, size_t size, 152d6aca350SJuergen Gross enum dma_data_direction dir, 153d6aca350SJuergen Gross unsigned long attrs) 154d6aca350SJuergen Gross { 155d6aca350SJuergen Gross struct xen_grant_dma_data *data; 156*e433715bSOleksandr Tyshchenko unsigned int i, n_pages = PFN_UP(offset + size); 157d6aca350SJuergen Gross grant_ref_t grant; 158d6aca350SJuergen Gross dma_addr_t dma_handle; 159d6aca350SJuergen Gross 160d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 161d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 162d6aca350SJuergen Gross 163d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 164d6aca350SJuergen Gross if (!data) 165d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 166d6aca350SJuergen Gross 167d6aca350SJuergen Gross if (unlikely(data->broken)) 168d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 169d6aca350SJuergen Gross 170d6aca350SJuergen Gross if (gnttab_alloc_grant_reference_seq(n_pages, &grant)) 171d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 172d6aca350SJuergen Gross 173d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 174d6aca350SJuergen Gross gnttab_grant_foreign_access_ref(grant + i, data->backend_domid, 175d6aca350SJuergen Gross xen_page_to_gfn(page) + i, dir == DMA_TO_DEVICE); 176d6aca350SJuergen Gross } 177d6aca350SJuergen Gross 178d6aca350SJuergen Gross dma_handle = grant_to_dma(grant) + offset; 179d6aca350SJuergen Gross 180d6aca350SJuergen Gross return dma_handle; 181d6aca350SJuergen Gross } 182d6aca350SJuergen Gross 183d6aca350SJuergen Gross static void xen_grant_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, 184d6aca350SJuergen Gross size_t size, enum dma_data_direction dir, 185d6aca350SJuergen Gross unsigned long attrs) 186d6aca350SJuergen Gross { 187d6aca350SJuergen Gross struct xen_grant_dma_data *data; 188*e433715bSOleksandr Tyshchenko unsigned long offset = dma_handle & (PAGE_SIZE - 1); 189*e433715bSOleksandr Tyshchenko unsigned int i, n_pages = PFN_UP(offset + size); 190d6aca350SJuergen Gross grant_ref_t grant; 191d6aca350SJuergen Gross 192d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 193d6aca350SJuergen Gross return; 194d6aca350SJuergen Gross 195d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 196d6aca350SJuergen Gross if (!data) 197d6aca350SJuergen Gross return; 198d6aca350SJuergen Gross 199d6aca350SJuergen Gross if (unlikely(data->broken)) 200d6aca350SJuergen Gross return; 201d6aca350SJuergen Gross 202d6aca350SJuergen Gross grant = dma_to_grant(dma_handle); 203d6aca350SJuergen Gross 204d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 205d6aca350SJuergen Gross if (unlikely(!gnttab_end_foreign_access_ref(grant + i))) { 206d6aca350SJuergen Gross dev_alert(dev, "Grant still in use by backend domain, disabled for further use\n"); 207d6aca350SJuergen Gross data->broken = true; 208d6aca350SJuergen Gross return; 209d6aca350SJuergen Gross } 210d6aca350SJuergen Gross } 211d6aca350SJuergen Gross 212d6aca350SJuergen Gross gnttab_free_grant_reference_seq(grant, n_pages); 213d6aca350SJuergen Gross } 214d6aca350SJuergen Gross 215d6aca350SJuergen Gross static void xen_grant_dma_unmap_sg(struct device *dev, struct scatterlist *sg, 216d6aca350SJuergen Gross int nents, enum dma_data_direction dir, 217d6aca350SJuergen Gross unsigned long attrs) 218d6aca350SJuergen Gross { 219d6aca350SJuergen Gross struct scatterlist *s; 220d6aca350SJuergen Gross unsigned int i; 221d6aca350SJuergen Gross 222d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 223d6aca350SJuergen Gross return; 224d6aca350SJuergen Gross 225d6aca350SJuergen Gross for_each_sg(sg, s, nents, i) 226d6aca350SJuergen Gross xen_grant_dma_unmap_page(dev, s->dma_address, sg_dma_len(s), dir, 227d6aca350SJuergen Gross attrs); 228d6aca350SJuergen Gross } 229d6aca350SJuergen Gross 230d6aca350SJuergen Gross static int xen_grant_dma_map_sg(struct device *dev, struct scatterlist *sg, 231d6aca350SJuergen Gross int nents, enum dma_data_direction dir, 232d6aca350SJuergen Gross unsigned long attrs) 233d6aca350SJuergen Gross { 234d6aca350SJuergen Gross struct scatterlist *s; 235d6aca350SJuergen Gross unsigned int i; 236d6aca350SJuergen Gross 237d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 238d6aca350SJuergen Gross return -EINVAL; 239d6aca350SJuergen Gross 240d6aca350SJuergen Gross for_each_sg(sg, s, nents, i) { 241d6aca350SJuergen Gross s->dma_address = xen_grant_dma_map_page(dev, sg_page(s), s->offset, 242d6aca350SJuergen Gross s->length, dir, attrs); 243d6aca350SJuergen Gross if (s->dma_address == DMA_MAPPING_ERROR) 244d6aca350SJuergen Gross goto out; 245d6aca350SJuergen Gross 246d6aca350SJuergen Gross sg_dma_len(s) = s->length; 247d6aca350SJuergen Gross } 248d6aca350SJuergen Gross 249d6aca350SJuergen Gross return nents; 250d6aca350SJuergen Gross 251d6aca350SJuergen Gross out: 252d6aca350SJuergen Gross xen_grant_dma_unmap_sg(dev, sg, i, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC); 253d6aca350SJuergen Gross sg_dma_len(sg) = 0; 254d6aca350SJuergen Gross 255d6aca350SJuergen Gross return -EIO; 256d6aca350SJuergen Gross } 257d6aca350SJuergen Gross 258d6aca350SJuergen Gross static int xen_grant_dma_supported(struct device *dev, u64 mask) 259d6aca350SJuergen Gross { 260d6aca350SJuergen Gross return mask == DMA_BIT_MASK(64); 261d6aca350SJuergen Gross } 262d6aca350SJuergen Gross 263d6aca350SJuergen Gross static const struct dma_map_ops xen_grant_dma_ops = { 264d6aca350SJuergen Gross .alloc = xen_grant_dma_alloc, 265d6aca350SJuergen Gross .free = xen_grant_dma_free, 266d6aca350SJuergen Gross .alloc_pages = xen_grant_dma_alloc_pages, 267d6aca350SJuergen Gross .free_pages = xen_grant_dma_free_pages, 268d6aca350SJuergen Gross .mmap = dma_common_mmap, 269d6aca350SJuergen Gross .get_sgtable = dma_common_get_sgtable, 270d6aca350SJuergen Gross .map_page = xen_grant_dma_map_page, 271d6aca350SJuergen Gross .unmap_page = xen_grant_dma_unmap_page, 272d6aca350SJuergen Gross .map_sg = xen_grant_dma_map_sg, 273d6aca350SJuergen Gross .unmap_sg = xen_grant_dma_unmap_sg, 274d6aca350SJuergen Gross .dma_supported = xen_grant_dma_supported, 275d6aca350SJuergen Gross }; 276d6aca350SJuergen Gross 277625ab90eSOleksandr Tyshchenko bool xen_is_grant_dma_device(struct device *dev) 278625ab90eSOleksandr Tyshchenko { 279625ab90eSOleksandr Tyshchenko struct device_node *iommu_np; 280625ab90eSOleksandr Tyshchenko bool has_iommu; 281625ab90eSOleksandr Tyshchenko 282625ab90eSOleksandr Tyshchenko /* XXX Handle only DT devices for now */ 283625ab90eSOleksandr Tyshchenko if (!dev->of_node) 284625ab90eSOleksandr Tyshchenko return false; 285625ab90eSOleksandr Tyshchenko 286625ab90eSOleksandr Tyshchenko iommu_np = of_parse_phandle(dev->of_node, "iommus", 0); 287625ab90eSOleksandr Tyshchenko has_iommu = iommu_np && of_device_is_compatible(iommu_np, "xen,grant-dma"); 288625ab90eSOleksandr Tyshchenko of_node_put(iommu_np); 289625ab90eSOleksandr Tyshchenko 290625ab90eSOleksandr Tyshchenko return has_iommu; 291625ab90eSOleksandr Tyshchenko } 292625ab90eSOleksandr Tyshchenko 293251e90e7SJuergen Gross bool xen_virtio_mem_acc(struct virtio_device *dev) 294251e90e7SJuergen Gross { 295251e90e7SJuergen Gross if (IS_ENABLED(CONFIG_XEN_VIRTIO_FORCE_GRANT)) 296251e90e7SJuergen Gross return true; 297251e90e7SJuergen Gross 298251e90e7SJuergen Gross return xen_is_grant_dma_device(dev->dev.parent); 299251e90e7SJuergen Gross } 300251e90e7SJuergen Gross 301d6aca350SJuergen Gross void xen_grant_setup_dma_ops(struct device *dev) 302d6aca350SJuergen Gross { 303d6aca350SJuergen Gross struct xen_grant_dma_data *data; 304625ab90eSOleksandr Tyshchenko struct of_phandle_args iommu_spec; 305d6aca350SJuergen Gross 306d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 307d6aca350SJuergen Gross if (data) { 308d6aca350SJuergen Gross dev_err(dev, "Xen grant DMA data is already created\n"); 309d6aca350SJuergen Gross return; 310d6aca350SJuergen Gross } 311d6aca350SJuergen Gross 312625ab90eSOleksandr Tyshchenko /* XXX ACPI device unsupported for now */ 313625ab90eSOleksandr Tyshchenko if (!dev->of_node) 314625ab90eSOleksandr Tyshchenko goto err; 315625ab90eSOleksandr Tyshchenko 316625ab90eSOleksandr Tyshchenko if (of_parse_phandle_with_args(dev->of_node, "iommus", "#iommu-cells", 317625ab90eSOleksandr Tyshchenko 0, &iommu_spec)) { 318625ab90eSOleksandr Tyshchenko dev_err(dev, "Cannot parse iommus property\n"); 319625ab90eSOleksandr Tyshchenko goto err; 320625ab90eSOleksandr Tyshchenko } 321625ab90eSOleksandr Tyshchenko 322625ab90eSOleksandr Tyshchenko if (!of_device_is_compatible(iommu_spec.np, "xen,grant-dma") || 323625ab90eSOleksandr Tyshchenko iommu_spec.args_count != 1) { 324625ab90eSOleksandr Tyshchenko dev_err(dev, "Incompatible IOMMU node\n"); 325625ab90eSOleksandr Tyshchenko of_node_put(iommu_spec.np); 326625ab90eSOleksandr Tyshchenko goto err; 327625ab90eSOleksandr Tyshchenko } 328625ab90eSOleksandr Tyshchenko 329625ab90eSOleksandr Tyshchenko of_node_put(iommu_spec.np); 330625ab90eSOleksandr Tyshchenko 331d6aca350SJuergen Gross data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 332d6aca350SJuergen Gross if (!data) 333d6aca350SJuergen Gross goto err; 334d6aca350SJuergen Gross 335625ab90eSOleksandr Tyshchenko /* 336625ab90eSOleksandr Tyshchenko * The endpoint ID here means the ID of the domain where the corresponding 337625ab90eSOleksandr Tyshchenko * backend is running 338625ab90eSOleksandr Tyshchenko */ 339625ab90eSOleksandr Tyshchenko data->backend_domid = iommu_spec.args[0]; 340d6aca350SJuergen Gross 341d6aca350SJuergen Gross if (xa_err(xa_store(&xen_grant_dma_devices, (unsigned long)dev, data, 342d6aca350SJuergen Gross GFP_KERNEL))) { 343d6aca350SJuergen Gross dev_err(dev, "Cannot store Xen grant DMA data\n"); 344d6aca350SJuergen Gross goto err; 345d6aca350SJuergen Gross } 346d6aca350SJuergen Gross 347d6aca350SJuergen Gross dev->dma_ops = &xen_grant_dma_ops; 348d6aca350SJuergen Gross 349d6aca350SJuergen Gross return; 350d6aca350SJuergen Gross 351d6aca350SJuergen Gross err: 352d6aca350SJuergen Gross dev_err(dev, "Cannot set up Xen grant DMA ops, retain platform DMA ops\n"); 353d6aca350SJuergen Gross } 354d6aca350SJuergen Gross 355d6aca350SJuergen Gross MODULE_DESCRIPTION("Xen grant DMA-mapping layer"); 356d6aca350SJuergen Gross MODULE_AUTHOR("Juergen Gross <jgross@suse.com>"); 357d6aca350SJuergen Gross MODULE_LICENSE("GPL"); 358