xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/iommu.c (revision 32640292)
1eb9a1df2SHans Rosenfeld /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3eb9a1df2SHans Rosenfeld  *
4eb9a1df2SHans Rosenfeld  * Copyright (c) 2011 NetApp, Inc.
5eb9a1df2SHans Rosenfeld  * All rights reserved.
6eb9a1df2SHans Rosenfeld  *
7eb9a1df2SHans Rosenfeld  * Redistribution and use in source and binary forms, with or without
8eb9a1df2SHans Rosenfeld  * modification, are permitted provided that the following conditions
9eb9a1df2SHans Rosenfeld  * are met:
10eb9a1df2SHans Rosenfeld  * 1. Redistributions of source code must retain the above copyright
11eb9a1df2SHans Rosenfeld  *    notice, this list of conditions and the following disclaimer.
12eb9a1df2SHans Rosenfeld  * 2. Redistributions in binary form must reproduce the above copyright
13eb9a1df2SHans Rosenfeld  *    notice, this list of conditions and the following disclaimer in the
14eb9a1df2SHans Rosenfeld  *    documentation and/or other materials provided with the distribution.
15eb9a1df2SHans Rosenfeld  *
16eb9a1df2SHans Rosenfeld  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17eb9a1df2SHans Rosenfeld  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18eb9a1df2SHans Rosenfeld  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19eb9a1df2SHans Rosenfeld  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20eb9a1df2SHans Rosenfeld  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21eb9a1df2SHans Rosenfeld  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22eb9a1df2SHans Rosenfeld  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23eb9a1df2SHans Rosenfeld  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24eb9a1df2SHans Rosenfeld  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25eb9a1df2SHans Rosenfeld  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26eb9a1df2SHans Rosenfeld  * SUCH DAMAGE.
27eb9a1df2SHans Rosenfeld  */
28e760f150SPatrick Mooney /*
29e760f150SPatrick Mooney  * This file and its contents are supplied under the terms of the
30e760f150SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
31e760f150SPatrick Mooney  * You may only use this file in accordance with the terms of version
32e760f150SPatrick Mooney  * 1.0 of the CDDL.
33e760f150SPatrick Mooney  *
34e760f150SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
35e760f150SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
36e760f150SPatrick Mooney  * http://www.illumos.org/license/CDDL.
37e760f150SPatrick Mooney  *
38e760f150SPatrick Mooney  * Copyright 2022 Oxide Computer Company
39e760f150SPatrick Mooney  */
40eb9a1df2SHans Rosenfeld 
41eb9a1df2SHans Rosenfeld #include <sys/cdefs.h>
42eb9a1df2SHans Rosenfeld 
43eb9a1df2SHans Rosenfeld #include <sys/param.h>
44eb9a1df2SHans Rosenfeld #include <sys/bus.h>
45eb9a1df2SHans Rosenfeld #include <sys/eventhandler.h>
46eb9a1df2SHans Rosenfeld #include <sys/sysctl.h>
47eb9a1df2SHans Rosenfeld #include <sys/systm.h>
48eb9a1df2SHans Rosenfeld 
49eb9a1df2SHans Rosenfeld #include <dev/pci/pcivar.h>
50eb9a1df2SHans Rosenfeld #include <dev/pci/pcireg.h>
51eb9a1df2SHans Rosenfeld 
52eb9a1df2SHans Rosenfeld #include <machine/cpu.h>
53eb9a1df2SHans Rosenfeld #include <machine/md_var.h>
54eb9a1df2SHans Rosenfeld 
55eb9a1df2SHans Rosenfeld #include <sys/ddi.h>
56eb9a1df2SHans Rosenfeld #include <sys/sunddi.h>
57eb9a1df2SHans Rosenfeld #include <sys/pci.h>
58eb9a1df2SHans Rosenfeld 
59eb9a1df2SHans Rosenfeld #include "vmm_util.h"
60eb9a1df2SHans Rosenfeld #include "iommu.h"
61eb9a1df2SHans Rosenfeld 
62eb9a1df2SHans Rosenfeld 
63e760f150SPatrick Mooney static kmutex_t iommu_lock;
64eb9a1df2SHans Rosenfeld 
65e760f150SPatrick Mooney static uint_t iommu_refcnt;
66e760f150SPatrick Mooney ddi_modhandle_t iommu_modhdl;
67b0de25cbSAndy Fiddaman static const struct iommu_ops *ops;
68eb9a1df2SHans Rosenfeld static void *host_domain;
69eb9a1df2SHans Rosenfeld 
70eb9a1df2SHans Rosenfeld static int
iommu_find_device(dev_info_t * dip,void * arg)71eb9a1df2SHans Rosenfeld iommu_find_device(dev_info_t *dip, void *arg)
72eb9a1df2SHans Rosenfeld {
733cdfcc97SToomas Soome 	boolean_t add = (boolean_t)(uintptr_t)arg;
74eb9a1df2SHans Rosenfeld 
75eb9a1df2SHans Rosenfeld 	if (pcie_is_pci_device(dip)) {
76eb9a1df2SHans Rosenfeld 		if (add)
77eb9a1df2SHans Rosenfeld 			iommu_add_device(host_domain, pci_get_rid(dip));
78eb9a1df2SHans Rosenfeld 		else
79eb9a1df2SHans Rosenfeld 			iommu_remove_device(host_domain, pci_get_rid(dip));
80eb9a1df2SHans Rosenfeld 	}
81eb9a1df2SHans Rosenfeld 
82eb9a1df2SHans Rosenfeld 	return (DDI_WALK_CONTINUE);
83eb9a1df2SHans Rosenfeld }
840153d828SPatrick Mooney 
850153d828SPatrick Mooney static vm_paddr_t
vmm_mem_maxaddr(void)860153d828SPatrick Mooney vmm_mem_maxaddr(void)
870153d828SPatrick Mooney {
880153d828SPatrick Mooney 	return (ptoa(physmax + 1));
890153d828SPatrick Mooney }
90eb9a1df2SHans Rosenfeld 
91e760f150SPatrick Mooney static int
iommu_init(void)92eb9a1df2SHans Rosenfeld iommu_init(void)
93eb9a1df2SHans Rosenfeld {
94e760f150SPatrick Mooney 	const char *mod_name;
95e760f150SPatrick Mooney 	int error = 0;
96eb9a1df2SHans Rosenfeld 
97e760f150SPatrick Mooney 	ASSERT(MUTEX_HELD(&iommu_lock));
98eb9a1df2SHans Rosenfeld 
99e760f150SPatrick Mooney 	if (vmm_is_intel()) {
100e760f150SPatrick Mooney 		mod_name = "misc/vmm_vtd";
101e760f150SPatrick Mooney 	} else if (vmm_is_svm()) {
102e760f150SPatrick Mooney 		/* Use the expected name for if/when this is ported */
103e760f150SPatrick Mooney 		mod_name = "misc/vmm_amdvi";
104e760f150SPatrick Mooney 	} else {
105e760f150SPatrick Mooney 		return (ENXIO);
106e760f150SPatrick Mooney 	}
107eb9a1df2SHans Rosenfeld 
108e760f150SPatrick Mooney 	/* Load the backend driver */
109e760f150SPatrick Mooney 	iommu_modhdl = ddi_modopen(mod_name, KRTLD_MODE_FIRST, &error);
110e760f150SPatrick Mooney 	if (iommu_modhdl == NULL) {
111e760f150SPatrick Mooney 		return (error);
112e760f150SPatrick Mooney 	}
113eb9a1df2SHans Rosenfeld 
114e760f150SPatrick Mooney 	/* Locate the iommu_ops struct */
115e760f150SPatrick Mooney 	ops = ddi_modsym(iommu_modhdl, IOMMU_OPS_SYM_NAME, &error);
116e760f150SPatrick Mooney 	if (ops == NULL) {
117e760f150SPatrick Mooney 		goto bail;
118e760f150SPatrick Mooney 	}
119eb9a1df2SHans Rosenfeld 
120e760f150SPatrick Mooney 	/* Initialize the backend */
121e760f150SPatrick Mooney 	error = ops->init();
122e760f150SPatrick Mooney 	if (error != 0) {
123e760f150SPatrick Mooney 		goto bail;
124e760f150SPatrick Mooney 	}
125e760f150SPatrick Mooney 
126e760f150SPatrick Mooney 	/* Create a domain for the devices owned by the host */
127e760f150SPatrick Mooney 	const vm_paddr_t maxaddr = vmm_mem_maxaddr();
128e760f150SPatrick Mooney 	host_domain = ops->create_domain(maxaddr);
129eb9a1df2SHans Rosenfeld 	if (host_domain == NULL) {
130e760f150SPatrick Mooney 		goto bail;
131eb9a1df2SHans Rosenfeld 	}
132eb9a1df2SHans Rosenfeld 
133e760f150SPatrick Mooney 	/* ... and populate it with 1:1 mappings for all of physical mem */
134eb9a1df2SHans Rosenfeld 	iommu_create_mapping(host_domain, 0, 0, maxaddr);
135eb9a1df2SHans Rosenfeld 
136eb9a1df2SHans Rosenfeld 	ddi_walk_devs(ddi_root_node(), iommu_find_device, (void *)B_TRUE);
137e760f150SPatrick Mooney 	ops->enable();
138e760f150SPatrick Mooney 
139e760f150SPatrick Mooney 	return (0);
140eb9a1df2SHans Rosenfeld 
141e760f150SPatrick Mooney bail:
142e760f150SPatrick Mooney 	if (ops != NULL) {
143e760f150SPatrick Mooney 		ops->cleanup();
144e760f150SPatrick Mooney 		ops = NULL;
145e760f150SPatrick Mooney 	}
146e760f150SPatrick Mooney 	if (iommu_modhdl != NULL) {
147e760f150SPatrick Mooney 		(void) ddi_modclose(iommu_modhdl);
148e760f150SPatrick Mooney 		iommu_modhdl = NULL;
149e760f150SPatrick Mooney 	}
150e760f150SPatrick Mooney 	return (error);
151eb9a1df2SHans Rosenfeld }
152eb9a1df2SHans Rosenfeld 
153e760f150SPatrick Mooney static void
iommu_cleanup(void)154eb9a1df2SHans Rosenfeld iommu_cleanup(void)
155eb9a1df2SHans Rosenfeld {
156e760f150SPatrick Mooney 	ASSERT(MUTEX_HELD(&iommu_lock));
157e760f150SPatrick Mooney 	ASSERT3P(ops, !=, NULL);
158e760f150SPatrick Mooney 	ASSERT0(iommu_refcnt);
159e760f150SPatrick Mooney 
160e760f150SPatrick Mooney 	ops->disable();
161eb9a1df2SHans Rosenfeld 	ddi_walk_devs(ddi_root_node(), iommu_find_device, (void *)B_FALSE);
162e760f150SPatrick Mooney 
163e760f150SPatrick Mooney 	ops->destroy_domain(host_domain);
164e760f150SPatrick Mooney 	host_domain = NULL;
165e760f150SPatrick Mooney 
166e760f150SPatrick Mooney 	ops->cleanup();
167eb9a1df2SHans Rosenfeld 	ops = NULL;
168e760f150SPatrick Mooney 
169e760f150SPatrick Mooney 	(void) ddi_modclose(iommu_modhdl);
170e760f150SPatrick Mooney 	iommu_modhdl = NULL;
171e760f150SPatrick Mooney }
172e760f150SPatrick Mooney 
173e760f150SPatrick Mooney static bool
iommu_ref(void)174e760f150SPatrick Mooney iommu_ref(void)
175e760f150SPatrick Mooney {
176e760f150SPatrick Mooney 	mutex_enter(&iommu_lock);
177e760f150SPatrick Mooney 	if (ops == NULL) {
178e760f150SPatrick Mooney 		int err = iommu_init();
179e760f150SPatrick Mooney 
180e760f150SPatrick Mooney 		if (err != 0) {
181e760f150SPatrick Mooney 			VERIFY3P(ops, ==, NULL);
182e760f150SPatrick Mooney 			mutex_exit(&iommu_lock);
183e760f150SPatrick Mooney 			return (false);
184e760f150SPatrick Mooney 		}
185e760f150SPatrick Mooney 		VERIFY3P(ops, !=, NULL);
186e760f150SPatrick Mooney 	}
187e760f150SPatrick Mooney 	iommu_refcnt++;
188e760f150SPatrick Mooney 	VERIFY3U(iommu_refcnt, <, UINT_MAX);
189e760f150SPatrick Mooney 	mutex_exit(&iommu_lock);
190e760f150SPatrick Mooney 
191e760f150SPatrick Mooney 	return (true);
192e760f150SPatrick Mooney }
193e760f150SPatrick Mooney 
194e760f150SPatrick Mooney static void
iommu_unref(void)195e760f150SPatrick Mooney iommu_unref(void)
196e760f150SPatrick Mooney {
197e760f150SPatrick Mooney 	mutex_enter(&iommu_lock);
198e760f150SPatrick Mooney 	VERIFY3U(iommu_refcnt, >, 0);
199e760f150SPatrick Mooney 	iommu_refcnt--;
200e760f150SPatrick Mooney 	if (iommu_refcnt == 0) {
201e760f150SPatrick Mooney 		iommu_cleanup();
202e760f150SPatrick Mooney 		VERIFY3P(ops, ==, NULL);
203e760f150SPatrick Mooney 	}
204e760f150SPatrick Mooney 	mutex_exit(&iommu_lock);
205eb9a1df2SHans Rosenfeld }
206eb9a1df2SHans Rosenfeld 
207eb9a1df2SHans Rosenfeld void *
iommu_create_domain(vm_paddr_t maxaddr)208eb9a1df2SHans Rosenfeld iommu_create_domain(vm_paddr_t maxaddr)
209eb9a1df2SHans Rosenfeld {
210e760f150SPatrick Mooney 	if (iommu_ref()) {
211e760f150SPatrick Mooney 		return (ops->create_domain(maxaddr));
212e760f150SPatrick Mooney 	} else {
213e760f150SPatrick Mooney 		return (NULL);
214eb9a1df2SHans Rosenfeld 	}
215eb9a1df2SHans Rosenfeld }
216eb9a1df2SHans Rosenfeld 
217eb9a1df2SHans Rosenfeld void
iommu_destroy_domain(void * domain)218e760f150SPatrick Mooney iommu_destroy_domain(void *domain)
219eb9a1df2SHans Rosenfeld {
220e760f150SPatrick Mooney 	ASSERT3P(domain, !=, NULL);
221eb9a1df2SHans Rosenfeld 
222e760f150SPatrick Mooney 	ops->destroy_domain(domain);
223e760f150SPatrick Mooney 	iommu_unref();
224eb9a1df2SHans Rosenfeld }
225eb9a1df2SHans Rosenfeld 
226eb9a1df2SHans Rosenfeld void
iommu_create_mapping(void * domain,vm_paddr_t gpa,vm_paddr_t hpa,size_t len)227e760f150SPatrick Mooney iommu_create_mapping(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, size_t len)
228eb9a1df2SHans Rosenfeld {
229e760f150SPatrick Mooney 	uint64_t remaining = len;
230eb9a1df2SHans Rosenfeld 
231e760f150SPatrick Mooney 	ASSERT3P(domain, !=, NULL);
232eb9a1df2SHans Rosenfeld 
233eb9a1df2SHans Rosenfeld 	while (remaining > 0) {
234e760f150SPatrick Mooney 		uint64_t mapped;
235e760f150SPatrick Mooney 
236e760f150SPatrick Mooney 		mapped = ops->create_mapping(domain, gpa, hpa, remaining);
237eb9a1df2SHans Rosenfeld 		gpa += mapped;
238eb9a1df2SHans Rosenfeld 		hpa += mapped;
239eb9a1df2SHans Rosenfeld 		remaining -= mapped;
240eb9a1df2SHans Rosenfeld 	}
241eb9a1df2SHans Rosenfeld }
242eb9a1df2SHans Rosenfeld 
243eb9a1df2SHans Rosenfeld void
iommu_remove_mapping(void * domain,vm_paddr_t gpa,size_t len)244e760f150SPatrick Mooney iommu_remove_mapping(void *domain, vm_paddr_t gpa, size_t len)
245eb9a1df2SHans Rosenfeld {
246e760f150SPatrick Mooney 	uint64_t remaining = len;
247eb9a1df2SHans Rosenfeld 
248e760f150SPatrick Mooney 	ASSERT3P(domain, !=, NULL);
249eb9a1df2SHans Rosenfeld 
250eb9a1df2SHans Rosenfeld 	while (remaining > 0) {
251e760f150SPatrick Mooney 		uint64_t unmapped;
252e760f150SPatrick Mooney 
253e760f150SPatrick Mooney 		unmapped = ops->remove_mapping(domain, gpa, remaining);
254eb9a1df2SHans Rosenfeld 		gpa += unmapped;
255eb9a1df2SHans Rosenfeld 		remaining -= unmapped;
256eb9a1df2SHans Rosenfeld 	}
257eb9a1df2SHans Rosenfeld }
258eb9a1df2SHans Rosenfeld 
259eb9a1df2SHans Rosenfeld void *
iommu_host_domain(void)260eb9a1df2SHans Rosenfeld iommu_host_domain(void)
261eb9a1df2SHans Rosenfeld {
262eb9a1df2SHans Rosenfeld 	return (host_domain);
263eb9a1df2SHans Rosenfeld }
264eb9a1df2SHans Rosenfeld 
265eb9a1df2SHans Rosenfeld void
iommu_add_device(void * domain,uint16_t rid)266e760f150SPatrick Mooney iommu_add_device(void *domain, uint16_t rid)
267eb9a1df2SHans Rosenfeld {
268e760f150SPatrick Mooney 	ASSERT3P(domain, !=, NULL);
269eb9a1df2SHans Rosenfeld 
270e760f150SPatrick Mooney 	ops->add_device(domain, rid);
271eb9a1df2SHans Rosenfeld }
272eb9a1df2SHans Rosenfeld 
273eb9a1df2SHans Rosenfeld void
iommu_remove_device(void * domain,uint16_t rid)274e760f150SPatrick Mooney iommu_remove_device(void *domain, uint16_t rid)
275eb9a1df2SHans Rosenfeld {
276e760f150SPatrick Mooney 	ASSERT3P(domain, !=, NULL);
277eb9a1df2SHans Rosenfeld 
278e760f150SPatrick Mooney 	ops->remove_device(domain, rid);
279eb9a1df2SHans Rosenfeld }
280eb9a1df2SHans Rosenfeld 
281eb9a1df2SHans Rosenfeld void
iommu_invalidate_tlb(void * domain)282eb9a1df2SHans Rosenfeld iommu_invalidate_tlb(void *domain)
283eb9a1df2SHans Rosenfeld {
284e760f150SPatrick Mooney 	ASSERT3P(domain, !=, NULL);
285eb9a1df2SHans Rosenfeld 
286e760f150SPatrick Mooney 	ops->invalidate_tlb(domain);
287eb9a1df2SHans Rosenfeld }
288