iommu.c 3.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
 * Author: Joerg Roedel <joerg.roedel@amd.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

19
#include <linux/device.h>
20
#include <linux/kernel.h>
21 22
#include <linux/bug.h>
#include <linux/types.h>
23 24
#include <linux/module.h>
#include <linux/slab.h>
25 26 27 28 29 30 31 32 33 34 35 36 37
#include <linux/errno.h>
#include <linux/iommu.h>

static struct iommu_ops *iommu_ops;

void register_iommu(struct iommu_ops *ops)
{
	if (iommu_ops)
		BUG();

	iommu_ops = ops;
}

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
{
}

/**
 * bus_set_iommu - set iommu-callbacks for the bus
 * @bus: bus.
 * @ops: the callbacks provided by the iommu-driver
 *
 * This function is called by an iommu driver to set the iommu methods
 * used for a particular bus. Drivers for devices on that bus can use
 * the iommu-api after these ops are registered.
 * This special function is needed because IOMMUs are usually devices on
 * the bus itself, so the iommu drivers are not initialized when the bus
 * is set up. With this function the iommu-driver can set the iommu-ops
 * afterwards.
 */
int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops)
{
	if (bus->iommu_ops != NULL)
		return -EBUSY;

	bus->iommu_ops = ops;

	/* Do IOMMU specific setup for this bus-type */
	iommu_bus_init(bus, ops);

	return 0;
}
EXPORT_SYMBOL_GPL(bus_set_iommu);

69
bool iommu_found(void)
70 71 72 73 74
{
	return iommu_ops != NULL;
}
EXPORT_SYMBOL_GPL(iommu_found);

75
struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
76 77
{
	struct iommu_domain *domain;
78
	struct iommu_ops *ops;
79 80
	int ret;

81 82 83 84 85 86 87 88
	if (bus->iommu_ops)
		ops = bus->iommu_ops;
	else
		ops = iommu_ops;

	if (ops == NULL)
		return NULL;

89 90 91 92
	domain = kmalloc(sizeof(*domain), GFP_KERNEL);
	if (!domain)
		return NULL;

93 94
	domain->ops = ops;

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	ret = iommu_ops->domain_init(domain);
	if (ret)
		goto out_free;

	return domain;

out_free:
	kfree(domain);

	return NULL;
}
EXPORT_SYMBOL_GPL(iommu_domain_alloc);

void iommu_domain_free(struct iommu_domain *domain)
{
	iommu_ops->domain_destroy(domain);
	kfree(domain);
}
EXPORT_SYMBOL_GPL(iommu_domain_free);

int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
{
	return iommu_ops->attach_dev(domain, dev);
}
EXPORT_SYMBOL_GPL(iommu_attach_device);

void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
{
	iommu_ops->detach_dev(domain, dev);
}
EXPORT_SYMBOL_GPL(iommu_detach_device);

phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
			       unsigned long iova)
{
	return iommu_ops->iova_to_phys(domain, iova);
}
EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
S
Sheng Yang 已提交
133 134 135 136 137 138 139

int iommu_domain_has_cap(struct iommu_domain *domain,
			 unsigned long cap)
{
	return iommu_ops->domain_has_cap(domain, cap);
}
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
140 141 142 143 144 145

int iommu_map(struct iommu_domain *domain, unsigned long iova,
	      phys_addr_t paddr, int gfp_order, int prot)
{
	size_t size;

146
	size         = PAGE_SIZE << gfp_order;
147

148
	BUG_ON(!IS_ALIGNED(iova | paddr, size));
149

150
	return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
151 152 153 154 155 156 157
}
EXPORT_SYMBOL_GPL(iommu_map);

int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
{
	size_t size;

158
	size         = PAGE_SIZE << gfp_order;
159

160
	BUG_ON(!IS_ALIGNED(iova, size));
161

162
	return iommu_ops->unmap(domain, iova, gfp_order);
163 164
}
EXPORT_SYMBOL_GPL(iommu_unmap);