1*bd72d4acSLizhi Hou // SPDX-License-Identifier: GPL-2.0 2*bd72d4acSLizhi Hou /* 3*bd72d4acSLizhi Hou * Copyright (C) 2025, Advanced Micro Devices, Inc. 4*bd72d4acSLizhi Hou */ 5*bd72d4acSLizhi Hou 6*bd72d4acSLizhi Hou #include <drm/amdxdna_accel.h> 7*bd72d4acSLizhi Hou #include <drm/drm_device.h> 8*bd72d4acSLizhi Hou #include <drm/drm_print.h> 9*bd72d4acSLizhi Hou #include <linux/dma-buf.h> 10*bd72d4acSLizhi Hou #include <linux/pagemap.h> 11*bd72d4acSLizhi Hou #include <linux/vmalloc.h> 12*bd72d4acSLizhi Hou 13*bd72d4acSLizhi Hou #include "amdxdna_pci_drv.h" 14*bd72d4acSLizhi Hou #include "amdxdna_ubuf.h" 15*bd72d4acSLizhi Hou 16*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv { 17*bd72d4acSLizhi Hou struct page **pages; 18*bd72d4acSLizhi Hou u64 nr_pages; 19*bd72d4acSLizhi Hou enum amdxdna_ubuf_flag flags; 20*bd72d4acSLizhi Hou struct mm_struct *mm; 21*bd72d4acSLizhi Hou }; 22*bd72d4acSLizhi Hou 23*bd72d4acSLizhi Hou static struct sg_table *amdxdna_ubuf_map(struct dma_buf_attachment *attach, 24*bd72d4acSLizhi Hou enum dma_data_direction direction) 25*bd72d4acSLizhi Hou { 26*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; 27*bd72d4acSLizhi Hou struct sg_table *sg; 28*bd72d4acSLizhi Hou int ret; 29*bd72d4acSLizhi Hou 30*bd72d4acSLizhi Hou sg = kzalloc(sizeof(*sg), GFP_KERNEL); 31*bd72d4acSLizhi Hou if (!sg) 32*bd72d4acSLizhi Hou return ERR_PTR(-ENOMEM); 33*bd72d4acSLizhi Hou 34*bd72d4acSLizhi Hou ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->nr_pages, 0, 35*bd72d4acSLizhi Hou ubuf->nr_pages << PAGE_SHIFT, GFP_KERNEL); 36*bd72d4acSLizhi Hou if (ret) 37*bd72d4acSLizhi Hou return ERR_PTR(ret); 38*bd72d4acSLizhi Hou 39*bd72d4acSLizhi Hou if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) { 40*bd72d4acSLizhi Hou ret = dma_map_sgtable(attach->dev, sg, direction, 0); 41*bd72d4acSLizhi Hou if (ret) 42*bd72d4acSLizhi Hou return ERR_PTR(ret); 43*bd72d4acSLizhi Hou } 44*bd72d4acSLizhi Hou 45*bd72d4acSLizhi Hou return sg; 46*bd72d4acSLizhi Hou } 47*bd72d4acSLizhi Hou 48*bd72d4acSLizhi Hou static void amdxdna_ubuf_unmap(struct dma_buf_attachment *attach, 49*bd72d4acSLizhi Hou struct sg_table *sg, 50*bd72d4acSLizhi Hou enum dma_data_direction direction) 51*bd72d4acSLizhi Hou { 52*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; 53*bd72d4acSLizhi Hou 54*bd72d4acSLizhi Hou if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) 55*bd72d4acSLizhi Hou dma_unmap_sgtable(attach->dev, sg, direction, 0); 56*bd72d4acSLizhi Hou 57*bd72d4acSLizhi Hou sg_free_table(sg); 58*bd72d4acSLizhi Hou kfree(sg); 59*bd72d4acSLizhi Hou } 60*bd72d4acSLizhi Hou 61*bd72d4acSLizhi Hou static void amdxdna_ubuf_release(struct dma_buf *dbuf) 62*bd72d4acSLizhi Hou { 63*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf = dbuf->priv; 64*bd72d4acSLizhi Hou 65*bd72d4acSLizhi Hou unpin_user_pages(ubuf->pages, ubuf->nr_pages); 66*bd72d4acSLizhi Hou kvfree(ubuf->pages); 67*bd72d4acSLizhi Hou atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm); 68*bd72d4acSLizhi Hou mmdrop(ubuf->mm); 69*bd72d4acSLizhi Hou kfree(ubuf); 70*bd72d4acSLizhi Hou } 71*bd72d4acSLizhi Hou 72*bd72d4acSLizhi Hou static vm_fault_t amdxdna_ubuf_vm_fault(struct vm_fault *vmf) 73*bd72d4acSLizhi Hou { 74*bd72d4acSLizhi Hou struct vm_area_struct *vma = vmf->vma; 75*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf; 76*bd72d4acSLizhi Hou unsigned long pfn; 77*bd72d4acSLizhi Hou pgoff_t pgoff; 78*bd72d4acSLizhi Hou 79*bd72d4acSLizhi Hou ubuf = vma->vm_private_data; 80*bd72d4acSLizhi Hou pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; 81*bd72d4acSLizhi Hou 82*bd72d4acSLizhi Hou pfn = page_to_pfn(ubuf->pages[pgoff]); 83*bd72d4acSLizhi Hou return vmf_insert_pfn(vma, vmf->address, pfn); 84*bd72d4acSLizhi Hou } 85*bd72d4acSLizhi Hou 86*bd72d4acSLizhi Hou static const struct vm_operations_struct amdxdna_ubuf_vm_ops = { 87*bd72d4acSLizhi Hou .fault = amdxdna_ubuf_vm_fault, 88*bd72d4acSLizhi Hou }; 89*bd72d4acSLizhi Hou 90*bd72d4acSLizhi Hou static int amdxdna_ubuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) 91*bd72d4acSLizhi Hou { 92*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf = dbuf->priv; 93*bd72d4acSLizhi Hou 94*bd72d4acSLizhi Hou vma->vm_ops = &amdxdna_ubuf_vm_ops; 95*bd72d4acSLizhi Hou vma->vm_private_data = ubuf; 96*bd72d4acSLizhi Hou vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); 97*bd72d4acSLizhi Hou 98*bd72d4acSLizhi Hou return 0; 99*bd72d4acSLizhi Hou } 100*bd72d4acSLizhi Hou 101*bd72d4acSLizhi Hou static int amdxdna_ubuf_vmap(struct dma_buf *dbuf, struct iosys_map *map) 102*bd72d4acSLizhi Hou { 103*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf = dbuf->priv; 104*bd72d4acSLizhi Hou void *kva; 105*bd72d4acSLizhi Hou 106*bd72d4acSLizhi Hou kva = vmap(ubuf->pages, ubuf->nr_pages, VM_MAP, PAGE_KERNEL); 107*bd72d4acSLizhi Hou if (!kva) 108*bd72d4acSLizhi Hou return -EINVAL; 109*bd72d4acSLizhi Hou 110*bd72d4acSLizhi Hou iosys_map_set_vaddr(map, kva); 111*bd72d4acSLizhi Hou return 0; 112*bd72d4acSLizhi Hou } 113*bd72d4acSLizhi Hou 114*bd72d4acSLizhi Hou static void amdxdna_ubuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map) 115*bd72d4acSLizhi Hou { 116*bd72d4acSLizhi Hou vunmap(map->vaddr); 117*bd72d4acSLizhi Hou } 118*bd72d4acSLizhi Hou 119*bd72d4acSLizhi Hou static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = { 120*bd72d4acSLizhi Hou .map_dma_buf = amdxdna_ubuf_map, 121*bd72d4acSLizhi Hou .unmap_dma_buf = amdxdna_ubuf_unmap, 122*bd72d4acSLizhi Hou .release = amdxdna_ubuf_release, 123*bd72d4acSLizhi Hou .mmap = amdxdna_ubuf_mmap, 124*bd72d4acSLizhi Hou .vmap = amdxdna_ubuf_vmap, 125*bd72d4acSLizhi Hou .vunmap = amdxdna_ubuf_vunmap, 126*bd72d4acSLizhi Hou }; 127*bd72d4acSLizhi Hou 128*bd72d4acSLizhi Hou struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, 129*bd72d4acSLizhi Hou enum amdxdna_ubuf_flag flags, 130*bd72d4acSLizhi Hou u32 num_entries, void __user *va_entries) 131*bd72d4acSLizhi Hou { 132*bd72d4acSLizhi Hou struct amdxdna_dev *xdna = to_xdna_dev(dev); 133*bd72d4acSLizhi Hou unsigned long lock_limit, new_pinned; 134*bd72d4acSLizhi Hou struct amdxdna_drm_va_entry *va_ent; 135*bd72d4acSLizhi Hou struct amdxdna_ubuf_priv *ubuf; 136*bd72d4acSLizhi Hou u32 npages, start = 0; 137*bd72d4acSLizhi Hou struct dma_buf *dbuf; 138*bd72d4acSLizhi Hou int i, ret; 139*bd72d4acSLizhi Hou DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 140*bd72d4acSLizhi Hou 141*bd72d4acSLizhi Hou if (!can_do_mlock()) 142*bd72d4acSLizhi Hou return ERR_PTR(-EPERM); 143*bd72d4acSLizhi Hou 144*bd72d4acSLizhi Hou ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL); 145*bd72d4acSLizhi Hou if (!ubuf) 146*bd72d4acSLizhi Hou return ERR_PTR(-ENOMEM); 147*bd72d4acSLizhi Hou 148*bd72d4acSLizhi Hou ubuf->flags = flags; 149*bd72d4acSLizhi Hou ubuf->mm = current->mm; 150*bd72d4acSLizhi Hou mmgrab(ubuf->mm); 151*bd72d4acSLizhi Hou 152*bd72d4acSLizhi Hou va_ent = kvcalloc(num_entries, sizeof(*va_ent), GFP_KERNEL); 153*bd72d4acSLizhi Hou if (!va_ent) { 154*bd72d4acSLizhi Hou ret = -ENOMEM; 155*bd72d4acSLizhi Hou goto free_ubuf; 156*bd72d4acSLizhi Hou } 157*bd72d4acSLizhi Hou 158*bd72d4acSLizhi Hou if (copy_from_user(va_ent, va_entries, sizeof(*va_ent) * num_entries)) { 159*bd72d4acSLizhi Hou XDNA_DBG(xdna, "Access va entries failed"); 160*bd72d4acSLizhi Hou ret = -EINVAL; 161*bd72d4acSLizhi Hou goto free_ent; 162*bd72d4acSLizhi Hou } 163*bd72d4acSLizhi Hou 164*bd72d4acSLizhi Hou for (i = 0, exp_info.size = 0; i < num_entries; i++) { 165*bd72d4acSLizhi Hou if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) || 166*bd72d4acSLizhi Hou !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) { 167*bd72d4acSLizhi Hou XDNA_ERR(xdna, "Invalid address or len %llx, %llx", 168*bd72d4acSLizhi Hou va_ent[i].vaddr, va_ent[i].len); 169*bd72d4acSLizhi Hou ret = -EINVAL; 170*bd72d4acSLizhi Hou goto free_ent; 171*bd72d4acSLizhi Hou } 172*bd72d4acSLizhi Hou 173*bd72d4acSLizhi Hou exp_info.size += va_ent[i].len; 174*bd72d4acSLizhi Hou } 175*bd72d4acSLizhi Hou 176*bd72d4acSLizhi Hou ubuf->nr_pages = exp_info.size >> PAGE_SHIFT; 177*bd72d4acSLizhi Hou lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; 178*bd72d4acSLizhi Hou new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm); 179*bd72d4acSLizhi Hou if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) { 180*bd72d4acSLizhi Hou XDNA_DBG(xdna, "New pin %ld, limit %ld, cap %d", 181*bd72d4acSLizhi Hou new_pinned, lock_limit, capable(CAP_IPC_LOCK)); 182*bd72d4acSLizhi Hou ret = -ENOMEM; 183*bd72d4acSLizhi Hou goto sub_pin_cnt; 184*bd72d4acSLizhi Hou } 185*bd72d4acSLizhi Hou 186*bd72d4acSLizhi Hou ubuf->pages = kvmalloc_array(ubuf->nr_pages, sizeof(*ubuf->pages), GFP_KERNEL); 187*bd72d4acSLizhi Hou if (!ubuf->pages) { 188*bd72d4acSLizhi Hou ret = -ENOMEM; 189*bd72d4acSLizhi Hou goto sub_pin_cnt; 190*bd72d4acSLizhi Hou } 191*bd72d4acSLizhi Hou 192*bd72d4acSLizhi Hou for (i = 0; i < num_entries; i++) { 193*bd72d4acSLizhi Hou npages = va_ent[i].len >> PAGE_SHIFT; 194*bd72d4acSLizhi Hou 195*bd72d4acSLizhi Hou ret = pin_user_pages_fast(va_ent[i].vaddr, npages, 196*bd72d4acSLizhi Hou FOLL_WRITE | FOLL_LONGTERM, 197*bd72d4acSLizhi Hou &ubuf->pages[start]); 198*bd72d4acSLizhi Hou if (ret < 0 || ret != npages) { 199*bd72d4acSLizhi Hou ret = -ENOMEM; 200*bd72d4acSLizhi Hou XDNA_ERR(xdna, "Failed to pin pages ret %d", ret); 201*bd72d4acSLizhi Hou goto destroy_pages; 202*bd72d4acSLizhi Hou } 203*bd72d4acSLizhi Hou 204*bd72d4acSLizhi Hou start += ret; 205*bd72d4acSLizhi Hou } 206*bd72d4acSLizhi Hou 207*bd72d4acSLizhi Hou exp_info.ops = &amdxdna_ubuf_dmabuf_ops; 208*bd72d4acSLizhi Hou exp_info.priv = ubuf; 209*bd72d4acSLizhi Hou exp_info.flags = O_RDWR | O_CLOEXEC; 210*bd72d4acSLizhi Hou 211*bd72d4acSLizhi Hou dbuf = dma_buf_export(&exp_info); 212*bd72d4acSLizhi Hou if (IS_ERR(dbuf)) { 213*bd72d4acSLizhi Hou ret = PTR_ERR(dbuf); 214*bd72d4acSLizhi Hou goto destroy_pages; 215*bd72d4acSLizhi Hou } 216*bd72d4acSLizhi Hou kvfree(va_ent); 217*bd72d4acSLizhi Hou 218*bd72d4acSLizhi Hou return dbuf; 219*bd72d4acSLizhi Hou 220*bd72d4acSLizhi Hou destroy_pages: 221*bd72d4acSLizhi Hou if (start) 222*bd72d4acSLizhi Hou unpin_user_pages(ubuf->pages, start); 223*bd72d4acSLizhi Hou kvfree(ubuf->pages); 224*bd72d4acSLizhi Hou sub_pin_cnt: 225*bd72d4acSLizhi Hou atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm); 226*bd72d4acSLizhi Hou free_ent: 227*bd72d4acSLizhi Hou kvfree(va_ent); 228*bd72d4acSLizhi Hou free_ubuf: 229*bd72d4acSLizhi Hou mmdrop(ubuf->mm); 230*bd72d4acSLizhi Hou kfree(ubuf); 231*bd72d4acSLizhi Hou return ERR_PTR(ret); 232*bd72d4acSLizhi Hou } 233