162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright © 2014 Intel Corporation
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1362306a36Sopenharmony_ci * Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2162306a36Sopenharmony_ci * IN THE SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/mm.h>
2662306a36Sopenharmony_ci#include <linux/io-mapping.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "i915_drv.h"
3062306a36Sopenharmony_ci#include "i915_mm.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct remap_pfn {
3362306a36Sopenharmony_ci	struct mm_struct *mm;
3462306a36Sopenharmony_ci	unsigned long pfn;
3562306a36Sopenharmony_ci	pgprot_t prot;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	struct sgt_iter sgt;
3862306a36Sopenharmony_ci	resource_size_t iobase;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define use_dma(io) ((io) != -1)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic inline unsigned long sgt_pfn(const struct remap_pfn *r)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	if (use_dma(r->iobase))
4662306a36Sopenharmony_ci		return (r->sgt.dma + r->sgt.curr + r->iobase) >> PAGE_SHIFT;
4762306a36Sopenharmony_ci	else
4862306a36Sopenharmony_ci		return r->sgt.pfn + (r->sgt.curr >> PAGE_SHIFT);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int remap_sg(pte_t *pte, unsigned long addr, void *data)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct remap_pfn *r = data;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (GEM_WARN_ON(!r->sgt.sgp))
5662306a36Sopenharmony_ci		return -EINVAL;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Special PTE are not associated with any struct page */
5962306a36Sopenharmony_ci	set_pte_at(r->mm, addr, pte,
6062306a36Sopenharmony_ci		   pte_mkspecial(pfn_pte(sgt_pfn(r), r->prot)));
6162306a36Sopenharmony_ci	r->pfn++; /* track insertions in case we need to unwind later */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	r->sgt.curr += PAGE_SIZE;
6462306a36Sopenharmony_ci	if (r->sgt.curr >= r->sgt.max)
6562306a36Sopenharmony_ci		r->sgt = __sgt_iter(__sg_next(r->sgt.sgp), use_dma(r->iobase));
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define EXPECTED_FLAGS (VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_X86)
7362306a36Sopenharmony_cistatic int remap_pfn(pte_t *pte, unsigned long addr, void *data)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct remap_pfn *r = data;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Special PTE are not associated with any struct page */
7862306a36Sopenharmony_ci	set_pte_at(r->mm, addr, pte, pte_mkspecial(pfn_pte(r->pfn, r->prot)));
7962306a36Sopenharmony_ci	r->pfn++;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * remap_io_mapping - remap an IO mapping to userspace
8662306a36Sopenharmony_ci * @vma: user vma to map to
8762306a36Sopenharmony_ci * @addr: target user address to start at
8862306a36Sopenharmony_ci * @pfn: physical address of kernel memory
8962306a36Sopenharmony_ci * @size: size of map area
9062306a36Sopenharmony_ci * @iomap: the source io_mapping
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci *  Note: this is only safe if the mm semaphore is held when called.
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ciint remap_io_mapping(struct vm_area_struct *vma,
9562306a36Sopenharmony_ci		     unsigned long addr, unsigned long pfn, unsigned long size,
9662306a36Sopenharmony_ci		     struct io_mapping *iomap)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct remap_pfn r;
9962306a36Sopenharmony_ci	int err;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	GEM_BUG_ON((vma->vm_flags & EXPECTED_FLAGS) != EXPECTED_FLAGS);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* We rely on prevalidation of the io-mapping to skip track_pfn(). */
10462306a36Sopenharmony_ci	r.mm = vma->vm_mm;
10562306a36Sopenharmony_ci	r.pfn = pfn;
10662306a36Sopenharmony_ci	r.prot = __pgprot((pgprot_val(iomap->prot) & _PAGE_CACHE_MASK) |
10762306a36Sopenharmony_ci			  (pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK));
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	err = apply_to_page_range(r.mm, addr, size, remap_pfn, &r);
11062306a36Sopenharmony_ci	if (unlikely(err)) {
11162306a36Sopenharmony_ci		zap_vma_ptes(vma, addr, (r.pfn - pfn) << PAGE_SHIFT);
11262306a36Sopenharmony_ci		return err;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * remap_io_sg - remap an IO mapping to userspace
12162306a36Sopenharmony_ci * @vma: user vma to map to
12262306a36Sopenharmony_ci * @addr: target user address to start at
12362306a36Sopenharmony_ci * @size: size of map area
12462306a36Sopenharmony_ci * @sgl: Start sg entry
12562306a36Sopenharmony_ci * @iobase: Use stored dma address offset by this address or pfn if -1
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci *  Note: this is only safe if the mm semaphore is held when called.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ciint remap_io_sg(struct vm_area_struct *vma,
13062306a36Sopenharmony_ci		unsigned long addr, unsigned long size,
13162306a36Sopenharmony_ci		struct scatterlist *sgl, resource_size_t iobase)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct remap_pfn r = {
13462306a36Sopenharmony_ci		.mm = vma->vm_mm,
13562306a36Sopenharmony_ci		.prot = vma->vm_page_prot,
13662306a36Sopenharmony_ci		.sgt = __sgt_iter(sgl, use_dma(iobase)),
13762306a36Sopenharmony_ci		.iobase = iobase,
13862306a36Sopenharmony_ci	};
13962306a36Sopenharmony_ci	int err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* We rely on prevalidation of the io-mapping to skip track_pfn(). */
14262306a36Sopenharmony_ci	GEM_BUG_ON((vma->vm_flags & EXPECTED_FLAGS) != EXPECTED_FLAGS);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (!use_dma(iobase))
14562306a36Sopenharmony_ci		flush_cache_range(vma, addr, size);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	err = apply_to_page_range(r.mm, addr, size, remap_sg, &r);
14862306a36Sopenharmony_ci	if (unlikely(err)) {
14962306a36Sopenharmony_ci		zap_vma_ptes(vma, addr, r.pfn << PAGE_SHIFT);
15062306a36Sopenharmony_ci		return err;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
155