// SPDX-License-Identifier: GPL-2.0-only
/*
* VFIO: IOMMU DMA mapping support for TCE on POWER
*
* Copyright (C) 2013 IBM Corp. All rights reserved.
* Author: Alexey Kardashevskiy <aik@ozlabs.ru>
* Copyright Gavin Shan, IBM Corporation 2014.
*
* Derived from original vfio_iommu_type1.c:
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
* Author: Alex Williamson <alex.williamson@redhat.com>
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/err.h>
#include <linux/vfio.h>
#include <linux/vmalloc.h>
#include <linux/sched/mm.h>
#include <linux/sched/signal.h>
#include <linux/mm.h>
#include "vfio.h"
#include <asm/iommu.h>
#include <asm/tce.h>
#include <asm/mmu_context.h>
#define DRIVER_VERSION "0.1"
#define DRIVER_AUTHOR "aik@ozlabs.ru"
#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
static void tce_iommu_detach_group(void *iommu_data,
struct iommu_group *iommu_group);
/*
* VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
*
* This code handles mapping and unmapping of user data buffers
* into DMA'ble space using the IOMMU
*/
struct tce_iommu_group {
struct list_head next;
struct iommu_group *grp;
};
/*
* A container needs to remember which preregistered region it has
* referenced to do proper cleanup at the userspace process exit.
*/
struct tce_iommu_prereg {
struct list_head next;
struct mm_iommu_table_group_mem_t *mem;
};
/*
* The container descriptor supports only a single group per container.
* Required by the API as the container is not supplied with the IOMMU group
* at the moment of initialization.
*/
struct tce_container {
struct mutex lock;
bool enabled;
bool v2;
bool def_window_pending;
unsigned long locked_pages;
struct mm_struct *mm;
struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES];
struct list_head group_list;
struct list_head prereg_list;
};
static long tce_iommu_mm_set(struct tce_container *container)
{
if (container->mm) {
if (container->mm == current->mm)
return 0;
return -EPERM;
}
BUG_ON(!current->mm);
container->mm = current->mm;
mmgrab(container->mm);
return 0;
}
static long tce_iommu_prereg_free(struct tce_container *container,
struct tce_iommu_prereg *tcemem)
{
long ret;
ret = mm_iommu_put(container->mm, tcemem->mem);
if (ret)
return ret;
list_del(&tcemem->next);
kfree(tcemem