xref: /freebsd/sys/amd64/vmm/io/iommu.c (revision 6ae1554a5d9b318f8ad53ccc39fa5a961403da73)
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/sysctl.h>
37 
38 #include <dev/pci/pcivar.h>
39 #include <dev/pci/pcireg.h>
40 
41 #include <machine/md_var.h>
42 
43 #include "vmm_util.h"
44 #include "vmm_mem.h"
45 #include "iommu.h"
46 
47 SYSCTL_DECL(_hw_vmm);
48 SYSCTL_NODE(_hw_vmm, OID_AUTO, iommu, CTLFLAG_RW, 0, "bhyve iommu parameters");
49 
50 static int iommu_avail;
51 SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, initialized, CTLFLAG_RD, &iommu_avail,
52     0, "bhyve iommu initialized?");
53 
54 static struct iommu_ops *ops;
55 static void *host_domain;
56 
57 static __inline int
58 IOMMU_INIT(void)
59 {
60 	if (ops != NULL)
61 		return ((*ops->init)());
62 	else
63 		return (ENXIO);
64 }
65 
66 static __inline void
67 IOMMU_CLEANUP(void)
68 {
69 	if (ops != NULL && iommu_avail)
70 		(*ops->cleanup)();
71 }
72 
73 static __inline void *
74 IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr)
75 {
76 
77 	if (ops != NULL && iommu_avail)
78 		return ((*ops->create_domain)(maxaddr));
79 	else
80 		return (NULL);
81 }
82 
83 static __inline void
84 IOMMU_DESTROY_DOMAIN(void *dom)
85 {
86 
87 	if (ops != NULL && iommu_avail)
88 		(*ops->destroy_domain)(dom);
89 }
90 
91 static __inline uint64_t
92 IOMMU_CREATE_MAPPING(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
93 {
94 
95 	if (ops != NULL && iommu_avail)
96 		return ((*ops->create_mapping)(domain, gpa, hpa, len));
97 	else
98 		return (len);		/* XXX */
99 }
100 
101 static __inline uint64_t
102 IOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len)
103 {
104 
105 	if (ops != NULL && iommu_avail)
106 		return ((*ops->remove_mapping)(domain, gpa, len));
107 	else
108 		return (len);		/* XXX */
109 }
110 
111 static __inline void
112 IOMMU_ADD_DEVICE(void *domain, uint16_t rid)
113 {
114 
115 	if (ops != NULL && iommu_avail)
116 		(*ops->add_device)(domain, rid);
117 }
118 
119 static __inline void
120 IOMMU_REMOVE_DEVICE(void *domain, uint16_t rid)
121 {
122 
123 	if (ops != NULL && iommu_avail)
124 		(*ops->remove_device)(domain, rid);
125 }
126 
127 static __inline void
128 IOMMU_INVALIDATE_TLB(void *domain)
129 {
130 
131 	if (ops != NULL && iommu_avail)
132 		(*ops->invalidate_tlb)(domain);
133 }
134 
135 static __inline void
136 IOMMU_ENABLE(void)
137 {
138 
139 	if (ops != NULL && iommu_avail)
140 		(*ops->enable)();
141 }
142 
143 static __inline void
144 IOMMU_DISABLE(void)
145 {
146 
147 	if (ops != NULL && iommu_avail)
148 		(*ops->disable)();
149 }
150 
151 void
152 iommu_init(void)
153 {
154 	int error, bus, slot, func;
155 	vm_paddr_t maxaddr;
156 	const char *name;
157 	device_t dev;
158 
159 	if (vmm_is_intel())
160 		ops = &iommu_ops_intel;
161 	else if (vmm_is_amd())
162 		ops = &iommu_ops_amd;
163 	else
164 		ops = NULL;
165 
166 	error = IOMMU_INIT();
167 	if (error)
168 		return;
169 
170 	iommu_avail = 1;
171 
172 	/*
173 	 * Create a domain for the devices owned by the host
174 	 */
175 	maxaddr = vmm_mem_maxaddr();
176 	host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
177 	if (host_domain == NULL)
178 		panic("iommu_init: unable to create a host domain");
179 
180 	/*
181 	 * Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to
182 	 * the host
183 	 */
184 	iommu_create_mapping(host_domain, 0, 0, maxaddr);
185 
186 	for (bus = 0; bus <= PCI_BUSMAX; bus++) {
187 		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
188 			for (func = 0; func <= PCI_FUNCMAX; func++) {
189 				dev = pci_find_dbsf(0, bus, slot, func);
190 				if (dev == NULL)
191 					continue;
192 
193 				/* skip passthrough devices */
194 				name = device_get_name(dev);
195 				if (name != NULL && strcmp(name, "ppt") == 0)
196 					continue;
197 
198 				/* everything else belongs to the host domain */
199 				iommu_add_device(host_domain,
200 				    pci_get_rid(dev));
201 			}
202 		}
203 	}
204 	IOMMU_ENABLE();
205 
206 }
207 
208 void
209 iommu_cleanup(void)
210 {
211 	IOMMU_DISABLE();
212 	IOMMU_DESTROY_DOMAIN(host_domain);
213 	IOMMU_CLEANUP();
214 }
215 
216 void *
217 iommu_create_domain(vm_paddr_t maxaddr)
218 {
219 
220 	return (IOMMU_CREATE_DOMAIN(maxaddr));
221 }
222 
223 void
224 iommu_destroy_domain(void *dom)
225 {
226 
227 	IOMMU_DESTROY_DOMAIN(dom);
228 }
229 
230 void
231 iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len)
232 {
233 	uint64_t mapped, remaining;
234 
235 	remaining = len;
236 
237 	while (remaining > 0) {
238 		mapped = IOMMU_CREATE_MAPPING(dom, gpa, hpa, remaining);
239 		gpa += mapped;
240 		hpa += mapped;
241 		remaining -= mapped;
242 	}
243 }
244 
245 void
246 iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len)
247 {
248 	uint64_t unmapped, remaining;
249 
250 	remaining = len;
251 
252 	while (remaining > 0) {
253 		unmapped = IOMMU_REMOVE_MAPPING(dom, gpa, remaining);
254 		gpa += unmapped;
255 		remaining -= unmapped;
256 	}
257 }
258 
259 void *
260 iommu_host_domain(void)
261 {
262 
263 	return (host_domain);
264 }
265 
266 void
267 iommu_add_device(void *dom, uint16_t rid)
268 {
269 
270 	IOMMU_ADD_DEVICE(dom, rid);
271 }
272 
273 void
274 iommu_remove_device(void *dom, uint16_t rid)
275 {
276 
277 	IOMMU_REMOVE_DEVICE(dom, rid);
278 }
279 
280 void
281 iommu_invalidate_tlb(void *domain)
282 {
283 
284 	IOMMU_INVALIDATE_TLB(domain);
285 }
286