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 2877be00f1SOleksandr Tyshchenko static DEFINE_XARRAY_FLAGS(xen_grant_dma_devices, XA_FLAGS_LOCK_IRQ); 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; 4577be00f1SOleksandr Tyshchenko unsigned long flags; 46d6aca350SJuergen Gross 4777be00f1SOleksandr Tyshchenko xa_lock_irqsave(&xen_grant_dma_devices, flags); 48d6aca350SJuergen Gross data = xa_load(&xen_grant_dma_devices, (unsigned long)dev); 4977be00f1SOleksandr Tyshchenko xa_unlock_irqrestore(&xen_grant_dma_devices, flags); 50d6aca350SJuergen Gross 51d6aca350SJuergen Gross return data; 52d6aca350SJuergen Gross } 53d6aca350SJuergen Gross 5477be00f1SOleksandr Tyshchenko static int store_xen_grant_dma_data(struct device *dev, 5577be00f1SOleksandr Tyshchenko struct xen_grant_dma_data *data) 5677be00f1SOleksandr Tyshchenko { 5777be00f1SOleksandr Tyshchenko unsigned long flags; 5877be00f1SOleksandr Tyshchenko int ret; 5977be00f1SOleksandr Tyshchenko 6077be00f1SOleksandr Tyshchenko xa_lock_irqsave(&xen_grant_dma_devices, flags); 6177be00f1SOleksandr Tyshchenko ret = xa_err(__xa_store(&xen_grant_dma_devices, (unsigned long)dev, data, 6277be00f1SOleksandr Tyshchenko GFP_ATOMIC)); 6377be00f1SOleksandr Tyshchenko xa_unlock_irqrestore(&xen_grant_dma_devices, flags); 6477be00f1SOleksandr Tyshchenko 6577be00f1SOleksandr Tyshchenko return ret; 6677be00f1SOleksandr Tyshchenko } 6777be00f1SOleksandr Tyshchenko 68d6aca350SJuergen Gross /* 69d6aca350SJuergen Gross * DMA ops for Xen frontends (e.g. virtio). 70d6aca350SJuergen Gross * 71d6aca350SJuergen Gross * Used to act as a kind of software IOMMU for Xen guests by using grants as 72d6aca350SJuergen Gross * DMA addresses. 73d6aca350SJuergen Gross * Such a DMA address is formed by using the grant reference as a frame 74d6aca350SJuergen Gross * number and setting the highest address bit (this bit is for the backend 75d6aca350SJuergen Gross * to be able to distinguish it from e.g. a mmio address). 76d6aca350SJuergen Gross */ 77d6aca350SJuergen Gross static void *xen_grant_dma_alloc(struct device *dev, size_t size, 78d6aca350SJuergen Gross dma_addr_t *dma_handle, gfp_t gfp, 79d6aca350SJuergen Gross unsigned long attrs) 80d6aca350SJuergen Gross { 81d6aca350SJuergen Gross struct xen_grant_dma_data *data; 82d6aca350SJuergen Gross unsigned int i, n_pages = PFN_UP(size); 83d6aca350SJuergen Gross unsigned long pfn; 84d6aca350SJuergen Gross grant_ref_t grant; 85d6aca350SJuergen Gross void *ret; 86d6aca350SJuergen Gross 87d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 88d6aca350SJuergen Gross if (!data) 89d6aca350SJuergen Gross return NULL; 90d6aca350SJuergen Gross 91d6aca350SJuergen Gross if (unlikely(data->broken)) 92d6aca350SJuergen Gross return NULL; 93d6aca350SJuergen Gross 94d6aca350SJuergen Gross ret = alloc_pages_exact(n_pages * PAGE_SIZE, gfp); 95d6aca350SJuergen Gross if (!ret) 96d6aca350SJuergen Gross return NULL; 97d6aca350SJuergen Gross 98d6aca350SJuergen Gross pfn = virt_to_pfn(ret); 99d6aca350SJuergen Gross 100d6aca350SJuergen Gross if (gnttab_alloc_grant_reference_seq(n_pages, &grant)) { 101d6aca350SJuergen Gross free_pages_exact(ret, n_pages * PAGE_SIZE); 102d6aca350SJuergen Gross return NULL; 103d6aca350SJuergen Gross } 104d6aca350SJuergen Gross 105d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 106d6aca350SJuergen Gross gnttab_grant_foreign_access_ref(grant + i, data->backend_domid, 107d6aca350SJuergen Gross pfn_to_gfn(pfn + i), 0); 108d6aca350SJuergen Gross } 109d6aca350SJuergen Gross 110d6aca350SJuergen Gross *dma_handle = grant_to_dma(grant); 111d6aca350SJuergen Gross 112d6aca350SJuergen Gross return ret; 113d6aca350SJuergen Gross } 114d6aca350SJuergen Gross 115d6aca350SJuergen Gross static void xen_grant_dma_free(struct device *dev, size_t size, void *vaddr, 116d6aca350SJuergen Gross dma_addr_t dma_handle, unsigned long attrs) 117d6aca350SJuergen Gross { 118d6aca350SJuergen Gross struct xen_grant_dma_data *data; 119d6aca350SJuergen Gross unsigned int i, n_pages = PFN_UP(size); 120d6aca350SJuergen Gross grant_ref_t grant; 121d6aca350SJuergen Gross 122d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 123d6aca350SJuergen Gross if (!data) 124d6aca350SJuergen Gross return; 125d6aca350SJuergen Gross 126d6aca350SJuergen Gross if (unlikely(data->broken)) 127d6aca350SJuergen Gross return; 128d6aca350SJuergen Gross 129d6aca350SJuergen Gross grant = dma_to_grant(dma_handle); 130d6aca350SJuergen Gross 131d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 132d6aca350SJuergen Gross if (unlikely(!gnttab_end_foreign_access_ref(grant + i))) { 133d6aca350SJuergen Gross dev_alert(dev, "Grant still in use by backend domain, disabled for further use\n"); 134d6aca350SJuergen Gross data->broken = true; 135d6aca350SJuergen Gross return; 136d6aca350SJuergen Gross } 137d6aca350SJuergen Gross } 138d6aca350SJuergen Gross 139d6aca350SJuergen Gross gnttab_free_grant_reference_seq(grant, n_pages); 140d6aca350SJuergen Gross 141d6aca350SJuergen Gross free_pages_exact(vaddr, n_pages * PAGE_SIZE); 142d6aca350SJuergen Gross } 143d6aca350SJuergen Gross 144d6aca350SJuergen Gross static struct page *xen_grant_dma_alloc_pages(struct device *dev, size_t size, 145d6aca350SJuergen Gross dma_addr_t *dma_handle, 146d6aca350SJuergen Gross enum dma_data_direction dir, 147d6aca350SJuergen Gross gfp_t gfp) 148d6aca350SJuergen Gross { 149d6aca350SJuergen Gross void *vaddr; 150d6aca350SJuergen Gross 151d6aca350SJuergen Gross vaddr = xen_grant_dma_alloc(dev, size, dma_handle, gfp, 0); 152d6aca350SJuergen Gross if (!vaddr) 153d6aca350SJuergen Gross return NULL; 154d6aca350SJuergen Gross 155d6aca350SJuergen Gross return virt_to_page(vaddr); 156d6aca350SJuergen Gross } 157d6aca350SJuergen Gross 158d6aca350SJuergen Gross static void xen_grant_dma_free_pages(struct device *dev, size_t size, 159d6aca350SJuergen Gross struct page *vaddr, dma_addr_t dma_handle, 160d6aca350SJuergen Gross enum dma_data_direction dir) 161d6aca350SJuergen Gross { 162d6aca350SJuergen Gross xen_grant_dma_free(dev, size, page_to_virt(vaddr), dma_handle, 0); 163d6aca350SJuergen Gross } 164d6aca350SJuergen Gross 165d6aca350SJuergen Gross static dma_addr_t xen_grant_dma_map_page(struct device *dev, struct page *page, 166d6aca350SJuergen Gross unsigned long offset, size_t size, 167d6aca350SJuergen Gross enum dma_data_direction dir, 168d6aca350SJuergen Gross unsigned long attrs) 169d6aca350SJuergen Gross { 170d6aca350SJuergen Gross struct xen_grant_dma_data *data; 171e433715bSOleksandr Tyshchenko unsigned int i, n_pages = PFN_UP(offset + size); 172d6aca350SJuergen Gross grant_ref_t grant; 173d6aca350SJuergen Gross dma_addr_t dma_handle; 174d6aca350SJuergen Gross 175d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 176d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 177d6aca350SJuergen Gross 178d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 179d6aca350SJuergen Gross if (!data) 180d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 181d6aca350SJuergen Gross 182d6aca350SJuergen Gross if (unlikely(data->broken)) 183d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 184d6aca350SJuergen Gross 185d6aca350SJuergen Gross if (gnttab_alloc_grant_reference_seq(n_pages, &grant)) 186d6aca350SJuergen Gross return DMA_MAPPING_ERROR; 187d6aca350SJuergen Gross 188d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 189d6aca350SJuergen Gross gnttab_grant_foreign_access_ref(grant + i, data->backend_domid, 190d6aca350SJuergen Gross xen_page_to_gfn(page) + i, dir == DMA_TO_DEVICE); 191d6aca350SJuergen Gross } 192d6aca350SJuergen Gross 193d6aca350SJuergen Gross dma_handle = grant_to_dma(grant) + offset; 194d6aca350SJuergen Gross 195d6aca350SJuergen Gross return dma_handle; 196d6aca350SJuergen Gross } 197d6aca350SJuergen Gross 198d6aca350SJuergen Gross static void xen_grant_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, 199d6aca350SJuergen Gross size_t size, enum dma_data_direction dir, 200d6aca350SJuergen Gross unsigned long attrs) 201d6aca350SJuergen Gross { 202d6aca350SJuergen Gross struct xen_grant_dma_data *data; 203e433715bSOleksandr Tyshchenko unsigned long offset = dma_handle & (PAGE_SIZE - 1); 204e433715bSOleksandr Tyshchenko unsigned int i, n_pages = PFN_UP(offset + size); 205d6aca350SJuergen Gross grant_ref_t grant; 206d6aca350SJuergen Gross 207d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 208d6aca350SJuergen Gross return; 209d6aca350SJuergen Gross 210d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 211d6aca350SJuergen Gross if (!data) 212d6aca350SJuergen Gross return; 213d6aca350SJuergen Gross 214d6aca350SJuergen Gross if (unlikely(data->broken)) 215d6aca350SJuergen Gross return; 216d6aca350SJuergen Gross 217d6aca350SJuergen Gross grant = dma_to_grant(dma_handle); 218d6aca350SJuergen Gross 219d6aca350SJuergen Gross for (i = 0; i < n_pages; i++) { 220d6aca350SJuergen Gross if (unlikely(!gnttab_end_foreign_access_ref(grant + i))) { 221d6aca350SJuergen Gross dev_alert(dev, "Grant still in use by backend domain, disabled for further use\n"); 222d6aca350SJuergen Gross data->broken = true; 223d6aca350SJuergen Gross return; 224d6aca350SJuergen Gross } 225d6aca350SJuergen Gross } 226d6aca350SJuergen Gross 227d6aca350SJuergen Gross gnttab_free_grant_reference_seq(grant, n_pages); 228d6aca350SJuergen Gross } 229d6aca350SJuergen Gross 230d6aca350SJuergen Gross static void xen_grant_dma_unmap_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; 239d6aca350SJuergen Gross 240d6aca350SJuergen Gross for_each_sg(sg, s, nents, i) 241d6aca350SJuergen Gross xen_grant_dma_unmap_page(dev, s->dma_address, sg_dma_len(s), dir, 242d6aca350SJuergen Gross attrs); 243d6aca350SJuergen Gross } 244d6aca350SJuergen Gross 245d6aca350SJuergen Gross static int xen_grant_dma_map_sg(struct device *dev, struct scatterlist *sg, 246d6aca350SJuergen Gross int nents, enum dma_data_direction dir, 247d6aca350SJuergen Gross unsigned long attrs) 248d6aca350SJuergen Gross { 249d6aca350SJuergen Gross struct scatterlist *s; 250d6aca350SJuergen Gross unsigned int i; 251d6aca350SJuergen Gross 252d6aca350SJuergen Gross if (WARN_ON(dir == DMA_NONE)) 253d6aca350SJuergen Gross return -EINVAL; 254d6aca350SJuergen Gross 255d6aca350SJuergen Gross for_each_sg(sg, s, nents, i) { 256d6aca350SJuergen Gross s->dma_address = xen_grant_dma_map_page(dev, sg_page(s), s->offset, 257d6aca350SJuergen Gross s->length, dir, attrs); 258d6aca350SJuergen Gross if (s->dma_address == DMA_MAPPING_ERROR) 259d6aca350SJuergen Gross goto out; 260d6aca350SJuergen Gross 261d6aca350SJuergen Gross sg_dma_len(s) = s->length; 262d6aca350SJuergen Gross } 263d6aca350SJuergen Gross 264d6aca350SJuergen Gross return nents; 265d6aca350SJuergen Gross 266d6aca350SJuergen Gross out: 267d6aca350SJuergen Gross xen_grant_dma_unmap_sg(dev, sg, i, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC); 268d6aca350SJuergen Gross sg_dma_len(sg) = 0; 269d6aca350SJuergen Gross 270d6aca350SJuergen Gross return -EIO; 271d6aca350SJuergen Gross } 272d6aca350SJuergen Gross 273d6aca350SJuergen Gross static int xen_grant_dma_supported(struct device *dev, u64 mask) 274d6aca350SJuergen Gross { 275d6aca350SJuergen Gross return mask == DMA_BIT_MASK(64); 276d6aca350SJuergen Gross } 277d6aca350SJuergen Gross 278d6aca350SJuergen Gross static const struct dma_map_ops xen_grant_dma_ops = { 279d6aca350SJuergen Gross .alloc = xen_grant_dma_alloc, 280d6aca350SJuergen Gross .free = xen_grant_dma_free, 281d6aca350SJuergen Gross .alloc_pages = xen_grant_dma_alloc_pages, 282d6aca350SJuergen Gross .free_pages = xen_grant_dma_free_pages, 283d6aca350SJuergen Gross .mmap = dma_common_mmap, 284d6aca350SJuergen Gross .get_sgtable = dma_common_get_sgtable, 285d6aca350SJuergen Gross .map_page = xen_grant_dma_map_page, 286d6aca350SJuergen Gross .unmap_page = xen_grant_dma_unmap_page, 287d6aca350SJuergen Gross .map_sg = xen_grant_dma_map_sg, 288d6aca350SJuergen Gross .unmap_sg = xen_grant_dma_unmap_sg, 289d6aca350SJuergen Gross .dma_supported = xen_grant_dma_supported, 290d6aca350SJuergen Gross }; 291d6aca350SJuergen Gross 292*c9133112SJuergen Gross static bool xen_is_dt_grant_dma_device(struct device *dev) 293625ab90eSOleksandr Tyshchenko { 294625ab90eSOleksandr Tyshchenko struct device_node *iommu_np; 295625ab90eSOleksandr Tyshchenko bool has_iommu; 296625ab90eSOleksandr Tyshchenko 297625ab90eSOleksandr Tyshchenko iommu_np = of_parse_phandle(dev->of_node, "iommus", 0); 298*c9133112SJuergen Gross has_iommu = iommu_np && 299*c9133112SJuergen Gross of_device_is_compatible(iommu_np, "xen,grant-dma"); 300625ab90eSOleksandr Tyshchenko of_node_put(iommu_np); 301625ab90eSOleksandr Tyshchenko 302625ab90eSOleksandr Tyshchenko return has_iommu; 303625ab90eSOleksandr Tyshchenko } 304625ab90eSOleksandr Tyshchenko 305*c9133112SJuergen Gross bool xen_is_grant_dma_device(struct device *dev) 306*c9133112SJuergen Gross { 307*c9133112SJuergen Gross /* XXX Handle only DT devices for now */ 308*c9133112SJuergen Gross if (dev->of_node) 309*c9133112SJuergen Gross return xen_is_dt_grant_dma_device(dev); 310*c9133112SJuergen Gross 311*c9133112SJuergen Gross return false; 312*c9133112SJuergen Gross } 313*c9133112SJuergen Gross 314251e90e7SJuergen Gross bool xen_virtio_mem_acc(struct virtio_device *dev) 315251e90e7SJuergen Gross { 316251e90e7SJuergen Gross if (IS_ENABLED(CONFIG_XEN_VIRTIO_FORCE_GRANT)) 317251e90e7SJuergen Gross return true; 318251e90e7SJuergen Gross 319251e90e7SJuergen Gross return xen_is_grant_dma_device(dev->dev.parent); 320251e90e7SJuergen Gross } 321251e90e7SJuergen Gross 322*c9133112SJuergen Gross static int xen_dt_grant_init_backend_domid(struct device *dev, 323*c9133112SJuergen Gross struct xen_grant_dma_data *data) 324*c9133112SJuergen Gross { 325*c9133112SJuergen Gross struct of_phandle_args iommu_spec; 326*c9133112SJuergen Gross 327*c9133112SJuergen Gross if (of_parse_phandle_with_args(dev->of_node, "iommus", "#iommu-cells", 328*c9133112SJuergen Gross 0, &iommu_spec)) { 329*c9133112SJuergen Gross dev_err(dev, "Cannot parse iommus property\n"); 330*c9133112SJuergen Gross return -ESRCH; 331*c9133112SJuergen Gross } 332*c9133112SJuergen Gross 333*c9133112SJuergen Gross if (!of_device_is_compatible(iommu_spec.np, "xen,grant-dma") || 334*c9133112SJuergen Gross iommu_spec.args_count != 1) { 335*c9133112SJuergen Gross dev_err(dev, "Incompatible IOMMU node\n"); 336*c9133112SJuergen Gross of_node_put(iommu_spec.np); 337*c9133112SJuergen Gross return -ESRCH; 338*c9133112SJuergen Gross } 339*c9133112SJuergen Gross 340*c9133112SJuergen Gross of_node_put(iommu_spec.np); 341*c9133112SJuergen Gross 342*c9133112SJuergen Gross /* 343*c9133112SJuergen Gross * The endpoint ID here means the ID of the domain where the 344*c9133112SJuergen Gross * corresponding backend is running 345*c9133112SJuergen Gross */ 346*c9133112SJuergen Gross data->backend_domid = iommu_spec.args[0]; 347*c9133112SJuergen Gross 348*c9133112SJuergen Gross return 0; 349*c9133112SJuergen Gross } 350*c9133112SJuergen Gross 351d6aca350SJuergen Gross void xen_grant_setup_dma_ops(struct device *dev) 352d6aca350SJuergen Gross { 353d6aca350SJuergen Gross struct xen_grant_dma_data *data; 354d6aca350SJuergen Gross 355d6aca350SJuergen Gross data = find_xen_grant_dma_data(dev); 356d6aca350SJuergen Gross if (data) { 357d6aca350SJuergen Gross dev_err(dev, "Xen grant DMA data is already created\n"); 358d6aca350SJuergen Gross return; 359d6aca350SJuergen Gross } 360d6aca350SJuergen Gross 361d6aca350SJuergen Gross data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 362d6aca350SJuergen Gross if (!data) 363d6aca350SJuergen Gross goto err; 364d6aca350SJuergen Gross 365*c9133112SJuergen Gross if (dev->of_node) { 366*c9133112SJuergen Gross if (xen_dt_grant_init_backend_domid(dev, data)) 367*c9133112SJuergen Gross goto err; 368*c9133112SJuergen Gross } else { 369*c9133112SJuergen Gross /* XXX ACPI device unsupported for now */ 370*c9133112SJuergen Gross goto err; 371*c9133112SJuergen Gross } 372d6aca350SJuergen Gross 37377be00f1SOleksandr Tyshchenko if (store_xen_grant_dma_data(dev, data)) { 374d6aca350SJuergen Gross dev_err(dev, "Cannot store Xen grant DMA data\n"); 375d6aca350SJuergen Gross goto err; 376d6aca350SJuergen Gross } 377d6aca350SJuergen Gross 378d6aca350SJuergen Gross dev->dma_ops = &xen_grant_dma_ops; 379d6aca350SJuergen Gross 380d6aca350SJuergen Gross return; 381d6aca350SJuergen Gross 382d6aca350SJuergen Gross err: 383*c9133112SJuergen Gross devm_kfree(dev, data); 384d6aca350SJuergen Gross dev_err(dev, "Cannot set up Xen grant DMA ops, retain platform DMA ops\n"); 385d6aca350SJuergen Gross } 386d6aca350SJuergen Gross 387d6aca350SJuergen Gross MODULE_DESCRIPTION("Xen grant DMA-mapping layer"); 388d6aca350SJuergen Gross MODULE_AUTHOR("Juergen Gross <jgross@suse.com>"); 389d6aca350SJuergen Gross MODULE_LICENSE("GPL"); 390