]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/amd64/vmm/io/iommu.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / amd64 / vmm / io / iommu.c
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 }