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
IOMMU_INIT(void)58 IOMMU_INIT(void)
59 {
60 if (ops != NULL)
61 return ((*ops->init)());
62 else
63 return (ENXIO);
64 }
65
66 static __inline void
IOMMU_CLEANUP(void)67 IOMMU_CLEANUP(void)
68 {
69 if (ops != NULL && iommu_avail)
70 (*ops->cleanup)();
71 }
72
73 static __inline void *
IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr)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
IOMMU_DESTROY_DOMAIN(void * dom)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
IOMMU_CREATE_MAPPING(void * domain,vm_paddr_t gpa,vm_paddr_t hpa,uint64_t len)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
IOMMU_REMOVE_MAPPING(void * domain,vm_paddr_t gpa,uint64_t len)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
IOMMU_ADD_DEVICE(void * domain,uint16_t rid)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
IOMMU_REMOVE_DEVICE(void * domain,uint16_t rid)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
IOMMU_INVALIDATE_TLB(void * domain)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
IOMMU_ENABLE(void)136 IOMMU_ENABLE(void)
137 {
138
139 if (ops != NULL && iommu_avail)
140 (*ops->enable)();
141 }
142
143 static __inline void
IOMMU_DISABLE(void)144 IOMMU_DISABLE(void)
145 {
146
147 if (ops != NULL && iommu_avail)
148 (*ops->disable)();
149 }
150
151 void
iommu_init(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
iommu_cleanup(void)209 iommu_cleanup(void)
210 {
211 IOMMU_DISABLE();
212 IOMMU_DESTROY_DOMAIN(host_domain);
213 IOMMU_CLEANUP();
214 }
215
216 void *
iommu_create_domain(vm_paddr_t maxaddr)217 iommu_create_domain(vm_paddr_t maxaddr)
218 {
219
220 return (IOMMU_CREATE_DOMAIN(maxaddr));
221 }
222
223 void
iommu_destroy_domain(void * dom)224 iommu_destroy_domain(void *dom)
225 {
226
227 IOMMU_DESTROY_DOMAIN(dom);
228 }
229
230 void
iommu_create_mapping(void * dom,vm_paddr_t gpa,vm_paddr_t hpa,size_t len)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
iommu_remove_mapping(void * dom,vm_paddr_t gpa,size_t len)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 *
iommu_host_domain(void)260 iommu_host_domain(void)
261 {
262
263 return (host_domain);
264 }
265
266 void
iommu_add_device(void * dom,uint16_t rid)267 iommu_add_device(void *dom, uint16_t rid)
268 {
269
270 IOMMU_ADD_DEVICE(dom, rid);
271 }
272
273 void
iommu_remove_device(void * dom,uint16_t rid)274 iommu_remove_device(void *dom, uint16_t rid)
275 {
276
277 IOMMU_REMOVE_DEVICE(dom, rid);
278 }
279
280 void
iommu_invalidate_tlb(void * domain)281 iommu_invalidate_tlb(void *domain)
282 {
283
284 IOMMU_INVALIDATE_TLB(domain);
285 }
286