18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2014 Intel Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 218c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/mm.h> 268c2ecf20Sopenharmony_ci#include <linux/io-mapping.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "i915_drv.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct remap_pfn { 328c2ecf20Sopenharmony_ci struct mm_struct *mm; 338c2ecf20Sopenharmony_ci unsigned long pfn; 348c2ecf20Sopenharmony_ci pgprot_t prot; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci struct sgt_iter sgt; 378c2ecf20Sopenharmony_ci resource_size_t iobase; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int remap_pfn(pte_t *pte, unsigned long addr, void *data) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct remap_pfn *r = data; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Special PTE are not associated with any struct page */ 458c2ecf20Sopenharmony_ci set_pte_at(r->mm, addr, pte, pte_mkspecial(pfn_pte(r->pfn, r->prot))); 468c2ecf20Sopenharmony_ci r->pfn++; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define use_dma(io) ((io) != -1) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic inline unsigned long sgt_pfn(const struct remap_pfn *r) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci if (use_dma(r->iobase)) 568c2ecf20Sopenharmony_ci return (r->sgt.dma + r->sgt.curr + r->iobase) >> PAGE_SHIFT; 578c2ecf20Sopenharmony_ci else 588c2ecf20Sopenharmony_ci return r->sgt.pfn + (r->sgt.curr >> PAGE_SHIFT); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int remap_sg(pte_t *pte, unsigned long addr, void *data) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct remap_pfn *r = data; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (GEM_WARN_ON(!r->sgt.pfn)) 668c2ecf20Sopenharmony_ci return -EINVAL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* Special PTE are not associated with any struct page */ 698c2ecf20Sopenharmony_ci set_pte_at(r->mm, addr, pte, 708c2ecf20Sopenharmony_ci pte_mkspecial(pfn_pte(sgt_pfn(r), r->prot))); 718c2ecf20Sopenharmony_ci r->pfn++; /* track insertions in case we need to unwind later */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci r->sgt.curr += PAGE_SIZE; 748c2ecf20Sopenharmony_ci if (r->sgt.curr >= r->sgt.max) 758c2ecf20Sopenharmony_ci r->sgt = __sgt_iter(__sg_next(r->sgt.sgp), use_dma(r->iobase)); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * remap_io_mapping - remap an IO mapping to userspace 828c2ecf20Sopenharmony_ci * @vma: user vma to map to 838c2ecf20Sopenharmony_ci * @addr: target user address to start at 848c2ecf20Sopenharmony_ci * @pfn: physical address of kernel memory 858c2ecf20Sopenharmony_ci * @size: size of map area 868c2ecf20Sopenharmony_ci * @iomap: the source io_mapping 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Note: this is only safe if the mm semaphore is held when called. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ciint remap_io_mapping(struct vm_area_struct *vma, 918c2ecf20Sopenharmony_ci unsigned long addr, unsigned long pfn, unsigned long size, 928c2ecf20Sopenharmony_ci struct io_mapping *iomap) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct remap_pfn r; 958c2ecf20Sopenharmony_ci int err; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define EXPECTED_FLAGS (VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP) 988c2ecf20Sopenharmony_ci GEM_BUG_ON((vma->vm_flags & EXPECTED_FLAGS) != EXPECTED_FLAGS); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* We rely on prevalidation of the io-mapping to skip track_pfn(). */ 1018c2ecf20Sopenharmony_ci r.mm = vma->vm_mm; 1028c2ecf20Sopenharmony_ci r.pfn = pfn; 1038c2ecf20Sopenharmony_ci r.prot = __pgprot((pgprot_val(iomap->prot) & _PAGE_CACHE_MASK) | 1048c2ecf20Sopenharmony_ci (pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK)); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci err = apply_to_page_range(r.mm, addr, size, remap_pfn, &r); 1078c2ecf20Sopenharmony_ci if (unlikely(err)) { 1088c2ecf20Sopenharmony_ci zap_vma_ptes(vma, addr, (r.pfn - pfn) << PAGE_SHIFT); 1098c2ecf20Sopenharmony_ci return err; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * remap_io_sg - remap an IO mapping to userspace 1178c2ecf20Sopenharmony_ci * @vma: user vma to map to 1188c2ecf20Sopenharmony_ci * @addr: target user address to start at 1198c2ecf20Sopenharmony_ci * @size: size of map area 1208c2ecf20Sopenharmony_ci * @sgl: Start sg entry 1218c2ecf20Sopenharmony_ci * @iobase: Use stored dma address offset by this address or pfn if -1 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * Note: this is only safe if the mm semaphore is held when called. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ciint remap_io_sg(struct vm_area_struct *vma, 1268c2ecf20Sopenharmony_ci unsigned long addr, unsigned long size, 1278c2ecf20Sopenharmony_ci struct scatterlist *sgl, resource_size_t iobase) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct remap_pfn r = { 1308c2ecf20Sopenharmony_ci .mm = vma->vm_mm, 1318c2ecf20Sopenharmony_ci .prot = vma->vm_page_prot, 1328c2ecf20Sopenharmony_ci .sgt = __sgt_iter(sgl, use_dma(iobase)), 1338c2ecf20Sopenharmony_ci .iobase = iobase, 1348c2ecf20Sopenharmony_ci }; 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* We rely on prevalidation of the io-mapping to skip track_pfn(). */ 1388c2ecf20Sopenharmony_ci GEM_BUG_ON((vma->vm_flags & EXPECTED_FLAGS) != EXPECTED_FLAGS); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!use_dma(iobase)) 1418c2ecf20Sopenharmony_ci flush_cache_range(vma, addr, size); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = apply_to_page_range(r.mm, addr, size, remap_sg, &r); 1448c2ecf20Sopenharmony_ci if (unlikely(err)) { 1458c2ecf20Sopenharmony_ci zap_vma_ptes(vma, addr, r.pfn << PAGE_SHIFT); 1468c2ecf20Sopenharmony_ci return err; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 151