cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ipu3-dmamap.c (5703B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2018 Intel Corporation
      4 * Copyright 2018 Google LLC.
      5 *
      6 * Author: Tomasz Figa <tfiga@chromium.org>
      7 * Author: Yong Zhi <yong.zhi@intel.com>
      8 */
      9
     10#include <linux/vmalloc.h>
     11
     12#include "ipu3.h"
     13#include "ipu3-css-pool.h"
     14#include "ipu3-mmu.h"
     15#include "ipu3-dmamap.h"
     16
     17/*
     18 * Free a buffer allocated by imgu_dmamap_alloc_buffer()
     19 */
     20static void imgu_dmamap_free_buffer(struct page **pages,
     21				    size_t size)
     22{
     23	int count = size >> PAGE_SHIFT;
     24
     25	while (count--)
     26		__free_page(pages[count]);
     27	kvfree(pages);
     28}
     29
     30/*
     31 * Based on the implementation of __iommu_dma_alloc_pages()
     32 * defined in drivers/iommu/dma-iommu.c
     33 */
     34static struct page **imgu_dmamap_alloc_buffer(size_t size, gfp_t gfp)
     35{
     36	struct page **pages;
     37	unsigned int i = 0, count = size >> PAGE_SHIFT;
     38	unsigned int order_mask = 1;
     39	const gfp_t high_order_gfp = __GFP_NOWARN | __GFP_NORETRY;
     40
     41	/* Allocate mem for array of page ptrs */
     42	pages = kvmalloc_array(count, sizeof(*pages), GFP_KERNEL);
     43
     44	if (!pages)
     45		return NULL;
     46
     47	gfp |= __GFP_HIGHMEM | __GFP_ZERO;
     48
     49	while (count) {
     50		struct page *page = NULL;
     51		unsigned int order_size;
     52
     53		for (order_mask &= (2U << __fls(count)) - 1;
     54		     order_mask; order_mask &= ~order_size) {
     55			unsigned int order = __fls(order_mask);
     56
     57			order_size = 1U << order;
     58			page = alloc_pages((order_mask - order_size) ?
     59					   gfp | high_order_gfp : gfp, order);
     60			if (!page)
     61				continue;
     62			if (!order)
     63				break;
     64			if (!PageCompound(page)) {
     65				split_page(page, order);
     66				break;
     67			}
     68
     69			__free_pages(page, order);
     70		}
     71		if (!page) {
     72			imgu_dmamap_free_buffer(pages, i << PAGE_SHIFT);
     73			return NULL;
     74		}
     75		count -= order_size;
     76		while (order_size--)
     77			pages[i++] = page++;
     78	}
     79
     80	return pages;
     81}
     82
     83/**
     84 * imgu_dmamap_alloc - allocate and map a buffer into KVA
     85 * @imgu: struct device pointer
     86 * @map: struct to store mapping variables
     87 * @len: size required
     88 *
     89 * Returns:
     90 *  KVA on success
     91 *  %NULL on failure
     92 */
     93void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map,
     94			size_t len)
     95{
     96	unsigned long shift = iova_shift(&imgu->iova_domain);
     97	struct device *dev = &imgu->pci_dev->dev;
     98	size_t size = PAGE_ALIGN(len);
     99	int count = size >> PAGE_SHIFT;
    100	struct page **pages;
    101	dma_addr_t iovaddr;
    102	struct iova *iova;
    103	int i, rval;
    104
    105	dev_dbg(dev, "%s: allocating %zu\n", __func__, size);
    106
    107	iova = alloc_iova(&imgu->iova_domain, size >> shift,
    108			  imgu->mmu->aperture_end >> shift, 0);
    109	if (!iova)
    110		return NULL;
    111
    112	pages = imgu_dmamap_alloc_buffer(size, GFP_KERNEL);
    113	if (!pages)
    114		goto out_free_iova;
    115
    116	/* Call IOMMU driver to setup pgt */
    117	iovaddr = iova_dma_addr(&imgu->iova_domain, iova);
    118	for (i = 0; i < count; ++i) {
    119		rval = imgu_mmu_map(imgu->mmu, iovaddr,
    120				    page_to_phys(pages[i]), PAGE_SIZE);
    121		if (rval)
    122			goto out_unmap;
    123
    124		iovaddr += PAGE_SIZE;
    125	}
    126
    127	map->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
    128	if (!map->vaddr)
    129		goto out_unmap;
    130
    131	map->pages = pages;
    132	map->size = size;
    133	map->daddr = iova_dma_addr(&imgu->iova_domain, iova);
    134
    135	dev_dbg(dev, "%s: allocated %zu @ IOVA %pad @ VA %p\n", __func__,
    136		size, &map->daddr, map->vaddr);
    137
    138	return map->vaddr;
    139
    140out_unmap:
    141	imgu_dmamap_free_buffer(pages, size);
    142	imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
    143		       i * PAGE_SIZE);
    144
    145out_free_iova:
    146	__free_iova(&imgu->iova_domain, iova);
    147
    148	return NULL;
    149}
    150
    151void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map)
    152{
    153	struct iova *iova;
    154
    155	iova = find_iova(&imgu->iova_domain,
    156			 iova_pfn(&imgu->iova_domain, map->daddr));
    157	if (WARN_ON(!iova))
    158		return;
    159
    160	imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
    161		       iova_size(iova) << iova_shift(&imgu->iova_domain));
    162
    163	__free_iova(&imgu->iova_domain, iova);
    164}
    165
    166/*
    167 * Counterpart of imgu_dmamap_alloc
    168 */
    169void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map)
    170{
    171	dev_dbg(&imgu->pci_dev->dev, "%s: freeing %zu @ IOVA %pad @ VA %p\n",
    172		__func__, map->size, &map->daddr, map->vaddr);
    173
    174	if (!map->vaddr)
    175		return;
    176
    177	imgu_dmamap_unmap(imgu, map);
    178
    179	vunmap(map->vaddr);
    180	imgu_dmamap_free_buffer(map->pages, map->size);
    181	map->vaddr = NULL;
    182}
    183
    184int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist,
    185		       int nents, struct imgu_css_map *map)
    186{
    187	unsigned long shift = iova_shift(&imgu->iova_domain);
    188	struct scatterlist *sg;
    189	struct iova *iova;
    190	size_t size = 0;
    191	int i;
    192
    193	for_each_sg(sglist, sg, nents, i) {
    194		if (sg->offset)
    195			return -EINVAL;
    196
    197		if (i != nents - 1 && !PAGE_ALIGNED(sg->length))
    198			return -EINVAL;
    199
    200		size += sg->length;
    201	}
    202
    203	size = iova_align(&imgu->iova_domain, size);
    204	dev_dbg(&imgu->pci_dev->dev, "dmamap: mapping sg %d entries, %zu pages\n",
    205		nents, size >> shift);
    206
    207	iova = alloc_iova(&imgu->iova_domain, size >> shift,
    208			  imgu->mmu->aperture_end >> shift, 0);
    209	if (!iova)
    210		return -ENOMEM;
    211
    212	dev_dbg(&imgu->pci_dev->dev, "dmamap: iova low pfn %lu, high pfn %lu\n",
    213		iova->pfn_lo, iova->pfn_hi);
    214
    215	if (imgu_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
    216			    sglist, nents) < size)
    217		goto out_fail;
    218
    219	memset(map, 0, sizeof(*map));
    220	map->daddr = iova_dma_addr(&imgu->iova_domain, iova);
    221	map->size = size;
    222
    223	return 0;
    224
    225out_fail:
    226	__free_iova(&imgu->iova_domain, iova);
    227
    228	return -EFAULT;
    229}
    230
    231int imgu_dmamap_init(struct imgu_device *imgu)
    232{
    233	unsigned long order, base_pfn;
    234	int ret = iova_cache_get();
    235
    236	if (ret)
    237		return ret;
    238
    239	order = __ffs(IPU3_PAGE_SIZE);
    240	base_pfn = max_t(unsigned long, 1, imgu->mmu->aperture_start >> order);
    241	init_iova_domain(&imgu->iova_domain, 1UL << order, base_pfn);
    242
    243	return 0;
    244}
    245
    246void imgu_dmamap_exit(struct imgu_device *imgu)
    247{
    248	put_iova_domain(&imgu->iova_domain);
    249	iova_cache_put();
    250}