1fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel/*
2fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
3fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * Author: Joerg Roedel <joerg.roedel@amd.com>
4fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel *
5fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * This program is free software; you can redistribute it and/or modify it
6fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * under the terms of the GNU General Public License version 2 as published
7fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * by the Free Software Foundation.
8fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel *
9fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * This program is distributed in the hope that it will be useful,
10fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * but WITHOUT ANY WARRANTY; without even the implied warranty of
11fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * GNU General Public License for more details.
13fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel *
14fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * You should have received a copy of the GNU General Public License
15fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * along with this program; if not, write to the Free Software
16fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel */
18fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
197d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen#define pr_fmt(fmt)    "%s: " fmt, __func__
207d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
21905d66c1e5dc8149e111f04a32bb193f25da1d53Joerg Roedel#include <linux/device.h>
224099818842abd98ef2b18a8ac7a2e2ad3bc3d7c2Ohad Ben-Cohen#include <linux/kernel.h>
23fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel#include <linux/bug.h>
24fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel#include <linux/types.h>
2560db402780ec257b287de591d65157575952bb4aAndrew Morton#include <linux/module.h>
2660db402780ec257b287de591d65157575952bb4aAndrew Morton#include <linux/slab.h>
27fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel#include <linux/errno.h>
28fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel#include <linux/iommu.h>
29fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
301460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonstatic ssize_t show_iommu_group(struct device *dev,
311460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson				struct device_attribute *attr, char *buf)
321460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson{
331460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	unsigned int groupid;
341460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
351460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	if (iommu_device_group(dev, &groupid))
361460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson		return 0;
371460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
381460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	return sprintf(buf, "%u", groupid);
391460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson}
401460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonstatic DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL);
411460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
421460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonstatic int add_iommu_group(struct device *dev, void *data)
431460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson{
441460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	unsigned int groupid;
451460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
461460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	if (iommu_device_group(dev, &groupid) == 0)
471460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson		return device_create_file(dev, &dev_attr_iommu_group);
481460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
491460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	return 0;
501460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson}
511460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
521460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonstatic int remove_iommu_group(struct device *dev)
531460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson{
541460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	unsigned int groupid;
551460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
561460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	if (iommu_device_group(dev, &groupid) == 0)
571460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson		device_remove_file(dev, &dev_attr_iommu_group);
581460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
591460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	return 0;
601460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson}
611460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
621460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonstatic int iommu_device_notifier(struct notifier_block *nb,
631460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson				 unsigned long action, void *data)
641460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson{
651460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	struct device *dev = data;
661460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
671460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	if (action == BUS_NOTIFY_ADD_DEVICE)
681460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson		return add_iommu_group(dev, NULL);
691460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	else if (action == BUS_NOTIFY_DEL_DEVICE)
701460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson		return remove_iommu_group(dev);
711460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
721460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	return 0;
731460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson}
741460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
751460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonstatic struct notifier_block iommu_device_nb = {
761460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	.notifier_call = iommu_device_notifier,
771460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson};
781460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
79ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedelstatic void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
80ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel{
811460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	bus_register_notifier(bus, &iommu_device_nb);
821460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	bus_for_each_dev(bus, NULL, NULL, add_iommu_group);
83ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel}
84fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
85ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel/**
86ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * bus_set_iommu - set iommu-callbacks for the bus
87ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * @bus: bus.
88ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * @ops: the callbacks provided by the iommu-driver
89ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel *
90ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * This function is called by an iommu driver to set the iommu methods
91ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * used for a particular bus. Drivers for devices on that bus can use
92ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * the iommu-api after these ops are registered.
93ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * This special function is needed because IOMMUs are usually devices on
94ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * the bus itself, so the iommu drivers are not initialized when the bus
95ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * is set up. With this function the iommu-driver can set the iommu-ops
96ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel * afterwards.
97ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel */
98ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedelint bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops)
99fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
100ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel	if (bus->iommu_ops != NULL)
101ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel		return -EBUSY;
102fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
103ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel	bus->iommu_ops = ops;
104ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel
105ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel	/* Do IOMMU specific setup for this bus-type */
106ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel	iommu_bus_init(bus, ops);
107fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
108ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg Roedel	return 0;
109fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
110ff21776d12ff7993a6b236b8273ef62777d25dfbJoerg RoedelEXPORT_SYMBOL_GPL(bus_set_iommu);
111fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
112a1b60c1cd913c5ccfb38c717ba0bd22622425fa7Joerg Roedelbool iommu_present(struct bus_type *bus)
113fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
11494441c3bd99287b9d84f148a08cc9a44675ec749Joerg Roedel	return bus->iommu_ops != NULL;
115fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
116a1b60c1cd913c5ccfb38c717ba0bd22622425fa7Joerg RoedelEXPORT_SYMBOL_GPL(iommu_present);
117fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
1184f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen/**
1194f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen * iommu_set_fault_handler() - set a fault handler for an iommu domain
1204f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen * @domain: iommu domain
1214f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen * @handler: fault handler
1220ed6d2d27bcc2ace454a8c55446e1bc3efd2d529Ohad Ben-Cohen *
1230ed6d2d27bcc2ace454a8c55446e1bc3efd2d529Ohad Ben-Cohen * This function should be used by IOMMU users which want to be notified
1240ed6d2d27bcc2ace454a8c55446e1bc3efd2d529Ohad Ben-Cohen * whenever an IOMMU fault happens.
1250ed6d2d27bcc2ace454a8c55446e1bc3efd2d529Ohad Ben-Cohen *
1260ed6d2d27bcc2ace454a8c55446e1bc3efd2d529Ohad Ben-Cohen * The fault handler itself should return 0 on success, and an appropriate
1270ed6d2d27bcc2ace454a8c55446e1bc3efd2d529Ohad Ben-Cohen * error code otherwise.
1284f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen */
1294f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohenvoid iommu_set_fault_handler(struct iommu_domain *domain,
1304f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen					iommu_fault_handler_t handler)
1314f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen{
1324f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen	BUG_ON(!domain);
1334f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen
1344f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen	domain->handler = handler;
1354f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen}
13630bd918c7132adddd370c79fd5619bf108efd702Ohad Ben-CohenEXPORT_SYMBOL_GPL(iommu_set_fault_handler);
1374f3f8d9db359bbc780d482849f2a9c8b12f910b6Ohad Ben-Cohen
138905d66c1e5dc8149e111f04a32bb193f25da1d53Joerg Roedelstruct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
139fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
140fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	struct iommu_domain *domain;
141fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	int ret;
142fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
14394441c3bd99287b9d84f148a08cc9a44675ec749Joerg Roedel	if (bus == NULL || bus->iommu_ops == NULL)
144905d66c1e5dc8149e111f04a32bb193f25da1d53Joerg Roedel		return NULL;
145905d66c1e5dc8149e111f04a32bb193f25da1d53Joerg Roedel
1468bd6960c6ae65d7f92bfb708154ee813417d7b26KyongHo Cho	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
147fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	if (!domain)
148fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel		return NULL;
149fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
15094441c3bd99287b9d84f148a08cc9a44675ec749Joerg Roedel	domain->ops = bus->iommu_ops;
151905d66c1e5dc8149e111f04a32bb193f25da1d53Joerg Roedel
15294441c3bd99287b9d84f148a08cc9a44675ec749Joerg Roedel	ret = domain->ops->domain_init(domain);
153fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	if (ret)
154fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel		goto out_free;
155fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
156fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	return domain;
157fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
158fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedelout_free:
159fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	kfree(domain);
160fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
161fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	return NULL;
162fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
163fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg RoedelEXPORT_SYMBOL_GPL(iommu_domain_alloc);
164fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
165fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedelvoid iommu_domain_free(struct iommu_domain *domain)
166fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
167e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (likely(domain->ops->domain_destroy != NULL))
168e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		domain->ops->domain_destroy(domain);
169e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel
170fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel	kfree(domain);
171fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
172fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg RoedelEXPORT_SYMBOL_GPL(iommu_domain_free);
173fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
174fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedelint iommu_attach_device(struct iommu_domain *domain, struct device *dev)
175fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
176e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (unlikely(domain->ops->attach_dev == NULL))
177e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		return -ENODEV;
178e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel
179e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	return domain->ops->attach_dev(domain, dev);
180fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
181fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg RoedelEXPORT_SYMBOL_GPL(iommu_attach_device);
182fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
183fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedelvoid iommu_detach_device(struct iommu_domain *domain, struct device *dev)
184fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
185e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (unlikely(domain->ops->detach_dev == NULL))
186e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		return;
187e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel
188e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	domain->ops->detach_dev(domain, dev);
189fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
190fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg RoedelEXPORT_SYMBOL_GPL(iommu_detach_device);
191fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel
192fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedelphys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
193fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel			       unsigned long iova)
194fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel{
195e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (unlikely(domain->ops->iova_to_phys == NULL))
196e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		return 0;
197e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel
198e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	return domain->ops->iova_to_phys(domain, iova);
199fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg Roedel}
200fc2100eb4d0960b56c2c705a97941c08fb1c0fd4Joerg RoedelEXPORT_SYMBOL_GPL(iommu_iova_to_phys);
201dbb9fd8630e95b6155aff658a2b5f80e95ca2bc6Sheng Yang
202dbb9fd8630e95b6155aff658a2b5f80e95ca2bc6Sheng Yangint iommu_domain_has_cap(struct iommu_domain *domain,
203dbb9fd8630e95b6155aff658a2b5f80e95ca2bc6Sheng Yang			 unsigned long cap)
204dbb9fd8630e95b6155aff658a2b5f80e95ca2bc6Sheng Yang{
205e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (unlikely(domain->ops->domain_has_cap == NULL))
206e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		return 0;
207e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel
208e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	return domain->ops->domain_has_cap(domain, cap);
209dbb9fd8630e95b6155aff658a2b5f80e95ca2bc6Sheng Yang}
210dbb9fd8630e95b6155aff658a2b5f80e95ca2bc6Sheng YangEXPORT_SYMBOL_GPL(iommu_domain_has_cap);
211cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
212cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedelint iommu_map(struct iommu_domain *domain, unsigned long iova,
2137d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	      phys_addr_t paddr, size_t size, int prot)
214cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel{
2157d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	unsigned long orig_iova = iova;
2167d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	unsigned int min_pagesz;
2177d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	size_t orig_size = size;
2187d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	int ret = 0;
219cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
220e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (unlikely(domain->ops->map == NULL))
221e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		return -ENODEV;
222cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
2237d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	/* find out the minimum page size supported */
2247d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
2257d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2267d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	/*
2277d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * both the virtual address and the physical one, as well as
2287d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * the size of the mapping, must be aligned (at least) to the
2297d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * size of the smallest page supported by the hardware
2307d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 */
2317d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
2327d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
2337d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			"0x%x\n", iova, (unsigned long)paddr,
2347d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			(unsigned long)size, min_pagesz);
2357d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		return -EINVAL;
2367d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	}
2377d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2387d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
2397d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen				(unsigned long)paddr, (unsigned long)size);
2407d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2417d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	while (size) {
2427d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		unsigned long pgsize, addr_merge = iova | paddr;
2437d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		unsigned int pgsize_idx;
2447d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2457d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		/* Max page size that still fits into 'size' */
2467d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pgsize_idx = __fls(size);
2477d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2487d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		/* need to consider alignment requirements ? */
2497d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		if (likely(addr_merge)) {
2507d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			/* Max page size allowed by both iova and paddr */
2517d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			unsigned int align_pgsize_idx = __ffs(addr_merge);
2527d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2537d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			pgsize_idx = min(pgsize_idx, align_pgsize_idx);
2547d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		}
2557d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2567d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		/* build a mask of acceptable page sizes */
2577d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pgsize = (1UL << (pgsize_idx + 1)) - 1;
2587d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2597d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		/* throw away page sizes not supported by the hardware */
2607d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pgsize &= domain->ops->pgsize_bitmap;
261cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
2627d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		/* make sure we're still sane */
2637d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		BUG_ON(!pgsize);
264cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
2657d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		/* pick the biggest page */
2667d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pgsize_idx = __fls(pgsize);
2677d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pgsize = 1UL << pgsize_idx;
2687d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2697d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pr_debug("mapping: iova 0x%lx pa 0x%lx pgsize %lu\n", iova,
2707d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen					(unsigned long)paddr, pgsize);
2717d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2727d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
2737d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		if (ret)
2747d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			break;
2757d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2767d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		iova += pgsize;
2777d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		paddr += pgsize;
2787d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		size -= pgsize;
2797d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	}
2807d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2817d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	/* unroll mapping in case something went wrong */
2827d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	if (ret)
2837d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		iommu_unmap(domain, orig_iova, orig_size - size);
2847d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
2857d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	return ret;
286cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel}
287cefc53c7f494240d4813c80154c7617452d1904dJoerg RoedelEXPORT_SYMBOL_GPL(iommu_map);
288cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
2897d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohensize_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
290cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel{
2917d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	size_t unmapped_page, unmapped = 0;
2927d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	unsigned int min_pagesz;
293cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel
294e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel	if (unlikely(domain->ops->unmap == NULL))
295e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel		return -ENODEV;
296e5aa7f00776f2d73f410ede5c1f68246cdc83de1Joerg Roedel
2977d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	/* find out the minimum page size supported */
2987d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
2997d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3007d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	/*
3017d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * The virtual address, as well as the size of the mapping, must be
3027d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * aligned (at least) to the size of the smallest page supported
3037d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * by the hardware
3047d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 */
3057d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	if (!IS_ALIGNED(iova | size, min_pagesz)) {
3067d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
3077d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen					iova, (unsigned long)size, min_pagesz);
3087d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		return -EINVAL;
3097d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	}
3107d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3117d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
3127d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen							(unsigned long)size);
3137d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3147d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	/*
3157d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * Keep iterating until we either unmap 'size' bytes (or more)
3167d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 * or we hit an area that isn't mapped.
3177d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	 */
3187d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	while (unmapped < size) {
3197d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		size_t left = size - unmapped;
3207d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3217d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		unmapped_page = domain->ops->unmap(domain, iova, left);
3227d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		if (!unmapped_page)
3237d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen			break;
3247d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3257d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		pr_debug("unmapped: iova 0x%lx size %lx\n", iova,
3267d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen					(unsigned long)unmapped_page);
3277d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3287d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		iova += unmapped_page;
3297d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen		unmapped += unmapped_page;
3307d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	}
3317d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen
3327d3002cc8c160dbda0e6ab9cd66dc6eb401b8b70Ohad Ben-Cohen	return unmapped;
333cefc53c7f494240d4813c80154c7617452d1904dJoerg Roedel}
334cefc53c7f494240d4813c80154c7617452d1904dJoerg RoedelEXPORT_SYMBOL_GPL(iommu_unmap);
3351460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
3361460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamsonint iommu_device_group(struct device *dev, unsigned int *groupid)
3371460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson{
3381460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group)
3391460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson		return dev->bus->iommu_ops->device_group(dev, groupid);
3401460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson
3411460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson	return -ENODEV;
3421460432cb513f0c16136ed132c20ecfbf8ccf942Alex Williamson}
3431460432cb513f0c16136ed132c20ecfbf8ccf942Alex WilliamsonEXPORT_SYMBOL_GPL(iommu_device_group);
344