xref: /freebsd/sys/amd64/vmm/io/iommu.c (revision 51f45d01467047fed6e9292d272ac27f7f9c1d23)
1366f6083SPeter Grehan /*-
2366f6083SPeter Grehan  * Copyright (c) 2011 NetApp, Inc.
3366f6083SPeter Grehan  * All rights reserved.
4366f6083SPeter Grehan  *
5366f6083SPeter Grehan  * Redistribution and use in source and binary forms, with or without
6366f6083SPeter Grehan  * modification, are permitted provided that the following conditions
7366f6083SPeter Grehan  * are met:
8366f6083SPeter Grehan  * 1. Redistributions of source code must retain the above copyright
9366f6083SPeter Grehan  *    notice, this list of conditions and the following disclaimer.
10366f6083SPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
11366f6083SPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
12366f6083SPeter Grehan  *    documentation and/or other materials provided with the distribution.
13366f6083SPeter Grehan  *
14366f6083SPeter Grehan  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15366f6083SPeter Grehan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16366f6083SPeter Grehan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17366f6083SPeter Grehan  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18366f6083SPeter Grehan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19366f6083SPeter Grehan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20366f6083SPeter Grehan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21366f6083SPeter Grehan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22366f6083SPeter Grehan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23366f6083SPeter Grehan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24366f6083SPeter Grehan  * SUCH DAMAGE.
25366f6083SPeter Grehan  *
26366f6083SPeter Grehan  * $FreeBSD$
27366f6083SPeter Grehan  */
28366f6083SPeter Grehan 
29366f6083SPeter Grehan #include <sys/cdefs.h>
30366f6083SPeter Grehan __FBSDID("$FreeBSD$");
31366f6083SPeter Grehan 
32366f6083SPeter Grehan #include <sys/param.h>
33366f6083SPeter Grehan #include <sys/types.h>
34366f6083SPeter Grehan #include <sys/systm.h>
35366f6083SPeter Grehan #include <sys/bus.h>
36*51f45d01SNeel Natu #include <sys/sysctl.h>
37366f6083SPeter Grehan 
38366f6083SPeter Grehan #include <dev/pci/pcivar.h>
39366f6083SPeter Grehan #include <dev/pci/pcireg.h>
40366f6083SPeter Grehan 
41366f6083SPeter Grehan #include <machine/md_var.h>
42366f6083SPeter Grehan 
43366f6083SPeter Grehan #include "vmm_util.h"
447ce04d0aSNeel Natu #include "vmm_mem.h"
45366f6083SPeter Grehan #include "iommu.h"
46366f6083SPeter Grehan 
47*51f45d01SNeel Natu SYSCTL_DECL(_hw_vmm);
48*51f45d01SNeel Natu SYSCTL_NODE(_hw_vmm, OID_AUTO, iommu, CTLFLAG_RW, 0, "bhyve iommu parameters");
49*51f45d01SNeel Natu 
50*51f45d01SNeel Natu static int iommu_avail;
51*51f45d01SNeel Natu SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, initialized, CTLFLAG_RD, &iommu_avail,
52*51f45d01SNeel Natu     0, "bhyve iommu initialized?");
53*51f45d01SNeel Natu 
54366f6083SPeter Grehan static struct iommu_ops *ops;
55366f6083SPeter Grehan static void *host_domain;
56366f6083SPeter Grehan 
57366f6083SPeter Grehan static __inline int
58366f6083SPeter Grehan IOMMU_INIT(void)
59366f6083SPeter Grehan {
60366f6083SPeter Grehan 	if (ops != NULL)
61366f6083SPeter Grehan 		return ((*ops->init)());
62366f6083SPeter Grehan 	else
63366f6083SPeter Grehan 		return (ENXIO);
64366f6083SPeter Grehan }
65366f6083SPeter Grehan 
66366f6083SPeter Grehan static __inline void
67366f6083SPeter Grehan IOMMU_CLEANUP(void)
68366f6083SPeter Grehan {
69366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
70366f6083SPeter Grehan 		(*ops->cleanup)();
71366f6083SPeter Grehan }
72366f6083SPeter Grehan 
73366f6083SPeter Grehan static __inline void *
74366f6083SPeter Grehan IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr)
75366f6083SPeter Grehan {
76366f6083SPeter Grehan 
77366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
78366f6083SPeter Grehan 		return ((*ops->create_domain)(maxaddr));
79366f6083SPeter Grehan 	else
80366f6083SPeter Grehan 		return (NULL);
81366f6083SPeter Grehan }
82366f6083SPeter Grehan 
83366f6083SPeter Grehan static __inline void
84366f6083SPeter Grehan IOMMU_DESTROY_DOMAIN(void *dom)
85366f6083SPeter Grehan {
86366f6083SPeter Grehan 
87366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
88366f6083SPeter Grehan 		(*ops->destroy_domain)(dom);
89366f6083SPeter Grehan }
90366f6083SPeter Grehan 
91366f6083SPeter Grehan static __inline uint64_t
92366f6083SPeter Grehan IOMMU_CREATE_MAPPING(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
93366f6083SPeter Grehan {
94366f6083SPeter Grehan 
95366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
96366f6083SPeter Grehan 		return ((*ops->create_mapping)(domain, gpa, hpa, len));
97366f6083SPeter Grehan 	else
98366f6083SPeter Grehan 		return (len);		/* XXX */
99366f6083SPeter Grehan }
100366f6083SPeter Grehan 
1017ce04d0aSNeel Natu static __inline uint64_t
1027ce04d0aSNeel Natu IOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len)
1037ce04d0aSNeel Natu {
1047ce04d0aSNeel Natu 
1057ce04d0aSNeel Natu 	if (ops != NULL && iommu_avail)
1067ce04d0aSNeel Natu 		return ((*ops->remove_mapping)(domain, gpa, len));
1077ce04d0aSNeel Natu 	else
1087ce04d0aSNeel Natu 		return (len);		/* XXX */
1097ce04d0aSNeel Natu }
1107ce04d0aSNeel Natu 
111366f6083SPeter Grehan static __inline void
112366f6083SPeter Grehan IOMMU_ADD_DEVICE(void *domain, int bus, int slot, int func)
113366f6083SPeter Grehan {
114366f6083SPeter Grehan 
115366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
116366f6083SPeter Grehan 		(*ops->add_device)(domain, bus, slot, func);
117366f6083SPeter Grehan }
118366f6083SPeter Grehan 
119366f6083SPeter Grehan static __inline void
120366f6083SPeter Grehan IOMMU_REMOVE_DEVICE(void *domain, int bus, int slot, int func)
121366f6083SPeter Grehan {
122366f6083SPeter Grehan 
123366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
124366f6083SPeter Grehan 		(*ops->remove_device)(domain, bus, slot, func);
125366f6083SPeter Grehan }
126366f6083SPeter Grehan 
127366f6083SPeter Grehan static __inline void
1287ce04d0aSNeel Natu IOMMU_INVALIDATE_TLB(void *domain)
1297ce04d0aSNeel Natu {
1307ce04d0aSNeel Natu 
1317ce04d0aSNeel Natu 	if (ops != NULL && iommu_avail)
1327ce04d0aSNeel Natu 		(*ops->invalidate_tlb)(domain);
1337ce04d0aSNeel Natu }
1347ce04d0aSNeel Natu 
1357ce04d0aSNeel Natu static __inline void
136366f6083SPeter Grehan IOMMU_ENABLE(void)
137366f6083SPeter Grehan {
138366f6083SPeter Grehan 
139366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
140366f6083SPeter Grehan 		(*ops->enable)();
141366f6083SPeter Grehan }
142366f6083SPeter Grehan 
143366f6083SPeter Grehan static __inline void
144366f6083SPeter Grehan IOMMU_DISABLE(void)
145366f6083SPeter Grehan {
146366f6083SPeter Grehan 
147366f6083SPeter Grehan 	if (ops != NULL && iommu_avail)
148366f6083SPeter Grehan 		(*ops->disable)();
149366f6083SPeter Grehan }
150366f6083SPeter Grehan 
151366f6083SPeter Grehan void
152366f6083SPeter Grehan iommu_init(void)
153366f6083SPeter Grehan {
154366f6083SPeter Grehan 	int error, bus, slot, func;
155366f6083SPeter Grehan 	vm_paddr_t maxaddr;
156366f6083SPeter Grehan 	const char *name;
157366f6083SPeter Grehan 	device_t dev;
158366f6083SPeter Grehan 
159366f6083SPeter Grehan 	if (vmm_is_intel())
160366f6083SPeter Grehan 		ops = &iommu_ops_intel;
161366f6083SPeter Grehan 	else if (vmm_is_amd())
162366f6083SPeter Grehan 		ops = &iommu_ops_amd;
163366f6083SPeter Grehan 	else
164366f6083SPeter Grehan 		ops = NULL;
165366f6083SPeter Grehan 
166366f6083SPeter Grehan 	error = IOMMU_INIT();
167366f6083SPeter Grehan 	if (error)
168366f6083SPeter Grehan 		return;
169366f6083SPeter Grehan 
170*51f45d01SNeel Natu 	iommu_avail = 1;
171366f6083SPeter Grehan 
172366f6083SPeter Grehan 	/*
173366f6083SPeter Grehan 	 * Create a domain for the devices owned by the host
174366f6083SPeter Grehan 	 */
1757ce04d0aSNeel Natu 	maxaddr = vmm_mem_maxaddr();
176366f6083SPeter Grehan 	host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
177366f6083SPeter Grehan 	if (host_domain == NULL)
178366f6083SPeter Grehan 		panic("iommu_init: unable to create a host domain");
179366f6083SPeter Grehan 
180366f6083SPeter Grehan 	/*
1817ce04d0aSNeel Natu 	 * Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to
182366f6083SPeter Grehan 	 * the host
183366f6083SPeter Grehan 	 */
184366f6083SPeter Grehan 	iommu_create_mapping(host_domain, 0, 0, maxaddr);
185366f6083SPeter Grehan 
186366f6083SPeter Grehan 	for (bus = 0; bus <= PCI_BUSMAX; bus++) {
187366f6083SPeter Grehan 		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
188366f6083SPeter Grehan 			for (func = 0; func <= PCI_FUNCMAX; func++) {
189366f6083SPeter Grehan 				dev = pci_find_dbsf(0, bus, slot, func);
190366f6083SPeter Grehan 				if (dev == NULL)
191366f6083SPeter Grehan 					continue;
192366f6083SPeter Grehan 
193366f6083SPeter Grehan 				/* skip passthrough devices */
194366f6083SPeter Grehan 				name = device_get_name(dev);
195366f6083SPeter Grehan 				if (name != NULL && strcmp(name, "ppt") == 0)
196366f6083SPeter Grehan 					continue;
197366f6083SPeter Grehan 
198366f6083SPeter Grehan 				/* everything else belongs to the host domain */
199366f6083SPeter Grehan 				iommu_add_device(host_domain, bus, slot, func);
200366f6083SPeter Grehan 			}
201366f6083SPeter Grehan 		}
202366f6083SPeter Grehan 	}
203366f6083SPeter Grehan 	IOMMU_ENABLE();
204366f6083SPeter Grehan 
205366f6083SPeter Grehan }
206366f6083SPeter Grehan 
207366f6083SPeter Grehan void
208366f6083SPeter Grehan iommu_cleanup(void)
209366f6083SPeter Grehan {
210366f6083SPeter Grehan 	IOMMU_DISABLE();
211366f6083SPeter Grehan 	IOMMU_DESTROY_DOMAIN(host_domain);
212366f6083SPeter Grehan 	IOMMU_CLEANUP();
213366f6083SPeter Grehan }
214366f6083SPeter Grehan 
215366f6083SPeter Grehan void *
216366f6083SPeter Grehan iommu_create_domain(vm_paddr_t maxaddr)
217366f6083SPeter Grehan {
218366f6083SPeter Grehan 
219366f6083SPeter Grehan 	return (IOMMU_CREATE_DOMAIN(maxaddr));
220366f6083SPeter Grehan }
221366f6083SPeter Grehan 
222366f6083SPeter Grehan void
223366f6083SPeter Grehan iommu_destroy_domain(void *dom)
224366f6083SPeter Grehan {
225366f6083SPeter Grehan 
226366f6083SPeter Grehan 	IOMMU_DESTROY_DOMAIN(dom);
227366f6083SPeter Grehan }
228366f6083SPeter Grehan 
229366f6083SPeter Grehan void
230366f6083SPeter Grehan iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len)
231366f6083SPeter Grehan {
232366f6083SPeter Grehan 	uint64_t mapped, remaining;
233366f6083SPeter Grehan 
234366f6083SPeter Grehan 	remaining = len;
235366f6083SPeter Grehan 
236366f6083SPeter Grehan 	while (remaining > 0) {
237366f6083SPeter Grehan 		mapped = IOMMU_CREATE_MAPPING(dom, gpa, hpa, remaining);
238366f6083SPeter Grehan 		gpa += mapped;
239366f6083SPeter Grehan 		hpa += mapped;
240366f6083SPeter Grehan 		remaining -= mapped;
241366f6083SPeter Grehan 	}
242366f6083SPeter Grehan }
243366f6083SPeter Grehan 
244366f6083SPeter Grehan void
2457ce04d0aSNeel Natu iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len)
2467ce04d0aSNeel Natu {
2477ce04d0aSNeel Natu 	uint64_t unmapped, remaining;
2487ce04d0aSNeel Natu 
2497ce04d0aSNeel Natu 	remaining = len;
2507ce04d0aSNeel Natu 
2517ce04d0aSNeel Natu 	while (remaining > 0) {
2527ce04d0aSNeel Natu 		unmapped = IOMMU_REMOVE_MAPPING(dom, gpa, remaining);
2537ce04d0aSNeel Natu 		gpa += unmapped;
2547ce04d0aSNeel Natu 		remaining -= unmapped;
2557ce04d0aSNeel Natu 	}
2567ce04d0aSNeel Natu }
2577ce04d0aSNeel Natu 
2587ce04d0aSNeel Natu void *
2597ce04d0aSNeel Natu iommu_host_domain(void)
2607ce04d0aSNeel Natu {
2617ce04d0aSNeel Natu 
2627ce04d0aSNeel Natu 	return (host_domain);
2637ce04d0aSNeel Natu }
2647ce04d0aSNeel Natu 
2657ce04d0aSNeel Natu void
266366f6083SPeter Grehan iommu_add_device(void *dom, int bus, int slot, int func)
267366f6083SPeter Grehan {
268366f6083SPeter Grehan 
269366f6083SPeter Grehan 	IOMMU_ADD_DEVICE(dom, bus, slot, func);
270366f6083SPeter Grehan }
271366f6083SPeter Grehan 
272366f6083SPeter Grehan void
273366f6083SPeter Grehan iommu_remove_device(void *dom, int bus, int slot, int func)
274366f6083SPeter Grehan {
275366f6083SPeter Grehan 
276366f6083SPeter Grehan 	IOMMU_REMOVE_DEVICE(dom, bus, slot, func);
277366f6083SPeter Grehan }
2787ce04d0aSNeel Natu 
2797ce04d0aSNeel Natu void
2807ce04d0aSNeel Natu iommu_invalidate_tlb(void *domain)
2817ce04d0aSNeel Natu {
2827ce04d0aSNeel Natu 
2837ce04d0aSNeel Natu 	IOMMU_INVALIDATE_TLB(domain);
2847ce04d0aSNeel Natu }
285