Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Virtual I/O topology
  4 *
  5 * The Virtual I/O Translation Table (VIOT) describes the topology of
  6 * para-virtual IOMMUs and the endpoints they manage. The OS uses it to
  7 * initialize devices in the right order, preventing endpoints from issuing DMA
  8 * before their IOMMU is ready.
  9 *
 10 * When binding a driver to a device, before calling the device driver's probe()
 11 * method, the driver infrastructure calls dma_configure(). At that point the
 12 * VIOT driver looks for an IOMMU associated to the device in the VIOT table.
 13 * If an IOMMU exists and has been initialized, the VIOT driver initializes the
 14 * device's IOMMU fwspec, allowing the DMA infrastructure to invoke the IOMMU
 15 * ops when the device driver configures DMA mappings. If an IOMMU exists and
 16 * hasn't yet been initialized, VIOT returns -EPROBE_DEFER to postpone probing
 17 * the device until the IOMMU is available.
 18 */
 19#define pr_fmt(fmt) "ACPI: VIOT: " fmt
 20
 21#include <linux/acpi_viot.h>
 22#include <linux/dma-iommu.h>
 23#include <linux/fwnode.h>
 24#include <linux/iommu.h>
 25#include <linux/list.h>
 26#include <linux/pci.h>
 27#include <linux/platform_device.h>
 28
 29struct viot_iommu {
 30	/* Node offset within the table */
 31	unsigned int			offset;
 32	struct fwnode_handle		*fwnode;
 33	struct list_head		list;
 34};
 35
 36struct viot_endpoint {
 37	union {
 38		/* PCI range */
 39		struct {
 40			u16		segment_start;
 41			u16		segment_end;
 42			u16		bdf_start;
 43			u16		bdf_end;
 44		};
 45		/* MMIO */
 46		u64			address;
 47	};
 48	u32				endpoint_id;
 49	struct viot_iommu		*viommu;
 50	struct list_head		list;
 51};
 52
 53static struct acpi_table_viot *viot;
 54static LIST_HEAD(viot_iommus);
 55static LIST_HEAD(viot_pci_ranges);
 56static LIST_HEAD(viot_mmio_endpoints);
 57
 58static int __init viot_check_bounds(const struct acpi_viot_header *hdr)
 59{
 60	struct acpi_viot_header *start, *end, *hdr_end;
 61
 62	start = ACPI_ADD_PTR(struct acpi_viot_header, viot,
 63			     max_t(size_t, sizeof(*viot), viot->node_offset));
 64	end = ACPI_ADD_PTR(struct acpi_viot_header, viot, viot->header.length);
 65	hdr_end = ACPI_ADD_PTR(struct acpi_viot_header, hdr, sizeof(*hdr));
 66
 67	if (hdr < start || hdr_end > end) {
 68		pr_err(FW_BUG "Node pointer overflows\n");
 69		return -EOVERFLOW;
 70	}
 71	if (hdr->length < sizeof(*hdr)) {
 72		pr_err(FW_BUG "Empty node\n");
 73		return -EINVAL;
 74	}
 75	return 0;
 76}
 77
 78static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu,
 79					    u16 segment, u16 bdf)
 80{
 81	struct pci_dev *pdev;
 82	struct fwnode_handle *fwnode;
 83
 84	pdev = pci_get_domain_bus_and_slot(segment, PCI_BUS_NUM(bdf),
 85					   bdf & 0xff);
 86	if (!pdev) {
 87		pr_err("Could not find PCI IOMMU\n");
 88		return -ENODEV;
 89	}
 90
 91	fwnode = pdev->dev.fwnode;
 92	if (!fwnode) {
 93		/*
 94		 * PCI devices aren't necessarily described by ACPI. Create a
 95		 * fwnode so the IOMMU subsystem can identify this device.
 96		 */
 97		fwnode = acpi_alloc_fwnode_static();
 98		if (!fwnode) {
 99			pci_dev_put(pdev);
100			return -ENOMEM;
101		}
102		set_primary_fwnode(&pdev->dev, fwnode);
103	}
104	viommu->fwnode = pdev->dev.fwnode;
105	pci_dev_put(pdev);
106	return 0;
107}
108
109static int __init viot_get_mmio_iommu_fwnode(struct viot_iommu *viommu,
110					     u64 address)
111{
112	struct acpi_device *adev;
113	struct resource res = {
114		.start	= address,
115		.end	= address,
116		.flags	= IORESOURCE_MEM,
117	};
118
119	adev = acpi_resource_consumer(&res);
120	if (!adev) {
121		pr_err("Could not find MMIO IOMMU\n");
122		return -EINVAL;
123	}
124	viommu->fwnode = &adev->fwnode;
125	return 0;
126}
127
128static struct viot_iommu * __init viot_get_iommu(unsigned int offset)
129{
130	int ret;
131	struct viot_iommu *viommu;
132	struct acpi_viot_header *hdr = ACPI_ADD_PTR(struct acpi_viot_header,
133						    viot, offset);
134	union {
135		struct acpi_viot_virtio_iommu_pci pci;
136		struct acpi_viot_virtio_iommu_mmio mmio;
137	} *node = (void *)hdr;
138
139	list_for_each_entry(viommu, &viot_iommus, list)
140		if (viommu->offset == offset)
141			return viommu;
142
143	if (viot_check_bounds(hdr))
144		return NULL;
145
146	viommu = kzalloc(sizeof(*viommu), GFP_KERNEL);
147	if (!viommu)
148		return NULL;
149
150	viommu->offset = offset;
151	switch (hdr->type) {
152	case ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI:
153		if (hdr->length < sizeof(node->pci))
154			goto err_free;
155
156		ret = viot_get_pci_iommu_fwnode(viommu, node->pci.segment,
157						node->pci.bdf);
158		break;
159	case ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO:
160		if (hdr->length < sizeof(node->mmio))
161			goto err_free;
162
163		ret = viot_get_mmio_iommu_fwnode(viommu,
164						 node->mmio.base_address);
165		break;
166	default:
167		ret = -EINVAL;
168	}
169	if (ret)
170		goto err_free;
171
172	list_add(&viommu->list, &viot_iommus);
173	return viommu;
174
175err_free:
176	kfree(viommu);
177	return NULL;
178}
179
180static int __init viot_parse_node(const struct acpi_viot_header *hdr)
181{
182	int ret = -EINVAL;
183	struct list_head *list;
184	struct viot_endpoint *ep;
185	union {
186		struct acpi_viot_mmio mmio;
187		struct acpi_viot_pci_range pci;
188	} *node = (void *)hdr;
189
190	if (viot_check_bounds(hdr))
191		return -EINVAL;
192
193	if (hdr->type == ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI ||
194	    hdr->type == ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO)
195		return 0;
196
197	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
198	if (!ep)
199		return -ENOMEM;
200
201	switch (hdr->type) {
202	case ACPI_VIOT_NODE_PCI_RANGE:
203		if (hdr->length < sizeof(node->pci)) {
204			pr_err(FW_BUG "Invalid PCI node size\n");
205			goto err_free;
206		}
207
208		ep->segment_start = node->pci.segment_start;
209		ep->segment_end = node->pci.segment_end;
210		ep->bdf_start = node->pci.bdf_start;
211		ep->bdf_end = node->pci.bdf_end;
212		ep->endpoint_id = node->pci.endpoint_start;
213		ep->viommu = viot_get_iommu(node->pci.output_node);
214		list = &viot_pci_ranges;
215		break;
216	case ACPI_VIOT_NODE_MMIO:
217		if (hdr->length < sizeof(node->mmio)) {
218			pr_err(FW_BUG "Invalid MMIO node size\n");
219			goto err_free;
220		}
221
222		ep->address = node->mmio.base_address;
223		ep->endpoint_id = node->mmio.endpoint;
224		ep->viommu = viot_get_iommu(node->mmio.output_node);
225		list = &viot_mmio_endpoints;
226		break;
227	default:
228		pr_warn("Unsupported node %x\n", hdr->type);
229		ret = 0;
230		goto err_free;
231	}
232
233	if (!ep->viommu) {
234		pr_warn("No IOMMU node found\n");
235		/*
236		 * A future version of the table may use the node for other
237		 * purposes. Keep parsing.
238		 */
239		ret = 0;
240		goto err_free;
241	}
242
243	list_add(&ep->list, list);
244	return 0;
245
246err_free:
247	kfree(ep);
248	return ret;
249}
250
251/**
252 * acpi_viot_init - Parse the VIOT table
253 *
254 * Parse the VIOT table, prepare the list of endpoints to be used during DMA
255 * setup of devices.
256 */
257void __init acpi_viot_init(void)
258{
259	int i;
260	acpi_status status;
261	struct acpi_table_header *hdr;
262	struct acpi_viot_header *node;
263
264	status = acpi_get_table(ACPI_SIG_VIOT, 0, &hdr);
265	if (ACPI_FAILURE(status)) {
266		if (status != AE_NOT_FOUND) {
267			const char *msg = acpi_format_exception(status);
268
269			pr_err("Failed to get table, %s\n", msg);
270		}
271		return;
272	}
273
274	viot = (void *)hdr;
275
276	node = ACPI_ADD_PTR(struct acpi_viot_header, viot, viot->node_offset);
277	for (i = 0; i < viot->node_count; i++) {
278		if (viot_parse_node(node))
279			return;
280
281		node = ACPI_ADD_PTR(struct acpi_viot_header, node,
282				    node->length);
283	}
284
285	acpi_put_table(hdr);
286}
287
288static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
289			       u32 epid)
290{
291	const struct iommu_ops *ops;
292
293	if (!viommu)
294		return -ENODEV;
295
296	/* We're not translating ourself */
297	if (viommu->fwnode == dev->fwnode)
298		return -EINVAL;
299
300	ops = iommu_ops_from_fwnode(viommu->fwnode);
301	if (!ops)
302		return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
303			-EPROBE_DEFER : -ENODEV;
304
305	return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
306}
307
308static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
309{
310	u32 epid;
311	struct viot_endpoint *ep;
312	u32 domain_nr = pci_domain_nr(pdev->bus);
313
314	list_for_each_entry(ep, &viot_pci_ranges, list) {
315		if (domain_nr >= ep->segment_start &&
316		    domain_nr <= ep->segment_end &&
317		    dev_id >= ep->bdf_start &&
318		    dev_id <= ep->bdf_end) {
319			epid = ((domain_nr - ep->segment_start) << 16) +
320				dev_id - ep->bdf_start + ep->endpoint_id;
321
322			/*
323			 * If we found a PCI range managed by the viommu, we're
324			 * the one that has to request ACS.
325			 */
326			pci_request_acs();
327
328			return viot_dev_iommu_init(&pdev->dev, ep->viommu,
329						   epid);
330		}
331	}
332	return -ENODEV;
333}
334
335static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
336{
337	struct resource *mem;
338	struct viot_endpoint *ep;
339
340	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
341	if (!mem)
342		return -ENODEV;
343
344	list_for_each_entry(ep, &viot_mmio_endpoints, list) {
345		if (ep->address == mem->start)
346			return viot_dev_iommu_init(&pdev->dev, ep->viommu,
347						   ep->endpoint_id);
348	}
349	return -ENODEV;
350}
351
352/**
353 * viot_iommu_configure - Setup IOMMU ops for an endpoint described by VIOT
354 * @dev: the endpoint
355 *
356 * Return: 0 on success, <0 on failure
357 */
358int viot_iommu_configure(struct device *dev)
359{
360	if (dev_is_pci(dev))
361		return pci_for_each_dma_alias(to_pci_dev(dev),
362					      viot_pci_dev_iommu_init, NULL);
363	else if (dev_is_platform(dev))
364		return viot_mmio_dev_iommu_init(to_platform_device(dev));
365	return -ENODEV;
366}