]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/amd64/vmm/io/iommu.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
37 #include <dev/pci/pcivar.h>
38 #include <dev/pci/pcireg.h>
39
40 #include <machine/md_var.h>
41
42 #include "vmm_util.h"
43 #include "vmm_mem.h"
44 #include "iommu.h"
45
46 static boolean_t iommu_avail;
47 static struct iommu_ops *ops;
48 static void *host_domain;
49
50 static __inline int
51 IOMMU_INIT(void)
52 {
53         if (ops != NULL)
54                 return ((*ops->init)());
55         else
56                 return (ENXIO);
57 }
58
59 static __inline void
60 IOMMU_CLEANUP(void)
61 {
62         if (ops != NULL && iommu_avail)
63                 (*ops->cleanup)();
64 }
65
66 static __inline void *
67 IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr)
68 {
69
70         if (ops != NULL && iommu_avail)
71                 return ((*ops->create_domain)(maxaddr));
72         else
73                 return (NULL);
74 }
75
76 static __inline void
77 IOMMU_DESTROY_DOMAIN(void *dom)
78 {
79
80         if (ops != NULL && iommu_avail)
81                 (*ops->destroy_domain)(dom);
82 }
83
84 static __inline uint64_t
85 IOMMU_CREATE_MAPPING(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
86 {
87
88         if (ops != NULL && iommu_avail)
89                 return ((*ops->create_mapping)(domain, gpa, hpa, len));
90         else
91                 return (len);           /* XXX */
92 }
93
94 static __inline uint64_t
95 IOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len)
96 {
97
98         if (ops != NULL && iommu_avail)
99                 return ((*ops->remove_mapping)(domain, gpa, len));
100         else
101                 return (len);           /* XXX */
102 }
103
104 static __inline void
105 IOMMU_ADD_DEVICE(void *domain, int bus, int slot, int func)
106 {
107
108         if (ops != NULL && iommu_avail)
109                 (*ops->add_device)(domain, bus, slot, func);
110 }
111
112 static __inline void
113 IOMMU_REMOVE_DEVICE(void *domain, int bus, int slot, int func)
114 {
115
116         if (ops != NULL && iommu_avail)
117                 (*ops->remove_device)(domain, bus, slot, func);
118 }
119
120 static __inline void
121 IOMMU_INVALIDATE_TLB(void *domain)
122 {
123
124         if (ops != NULL && iommu_avail)
125                 (*ops->invalidate_tlb)(domain);
126 }
127
128 static __inline void
129 IOMMU_ENABLE(void)
130 {
131
132         if (ops != NULL && iommu_avail)
133                 (*ops->enable)();
134 }
135
136 static __inline void
137 IOMMU_DISABLE(void)
138 {
139
140         if (ops != NULL && iommu_avail)
141                 (*ops->disable)();
142 }
143
144 void
145 iommu_init(void)
146 {
147         int error, bus, slot, func;
148         vm_paddr_t maxaddr;
149         const char *name;
150         device_t dev;
151
152         if (vmm_is_intel())
153                 ops = &iommu_ops_intel;
154         else if (vmm_is_amd())
155                 ops = &iommu_ops_amd;
156         else
157                 ops = NULL;
158
159         error = IOMMU_INIT();
160         if (error)
161                 return;
162
163         iommu_avail = TRUE;
164
165         /*
166          * Create a domain for the devices owned by the host
167          */
168         maxaddr = vmm_mem_maxaddr();
169         host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
170         if (host_domain == NULL)
171                 panic("iommu_init: unable to create a host domain");
172
173         /*
174          * Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to
175          * the host
176          */
177         iommu_create_mapping(host_domain, 0, 0, maxaddr);
178
179         for (bus = 0; bus <= PCI_BUSMAX; bus++) {
180                 for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
181                         for (func = 0; func <= PCI_FUNCMAX; func++) {
182                                 dev = pci_find_dbsf(0, bus, slot, func);
183                                 if (dev == NULL)
184                                         continue;
185
186                                 /* skip passthrough devices */
187                                 name = device_get_name(dev);
188                                 if (name != NULL && strcmp(name, "ppt") == 0)
189                                         continue;
190
191                                 /* everything else belongs to the host domain */
192                                 iommu_add_device(host_domain, bus, slot, func);
193                         }
194                 }
195         }
196         IOMMU_ENABLE();
197
198 }
199
200 void
201 iommu_cleanup(void)
202 {
203         IOMMU_DISABLE();
204         IOMMU_DESTROY_DOMAIN(host_domain);
205         IOMMU_CLEANUP();
206 }
207
208 void *
209 iommu_create_domain(vm_paddr_t maxaddr)
210 {
211
212         return (IOMMU_CREATE_DOMAIN(maxaddr));
213 }
214
215 void
216 iommu_destroy_domain(void *dom)
217 {
218
219         IOMMU_DESTROY_DOMAIN(dom);
220 }
221
222 void
223 iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len)
224 {
225         uint64_t mapped, remaining;
226
227         remaining = len;
228
229         while (remaining > 0) {
230                 mapped = IOMMU_CREATE_MAPPING(dom, gpa, hpa, remaining);
231                 gpa += mapped;
232                 hpa += mapped;
233                 remaining -= mapped;
234         }
235 }
236
237 void
238 iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len)
239 {
240         uint64_t unmapped, remaining;
241
242         remaining = len;
243
244         while (remaining > 0) {
245                 unmapped = IOMMU_REMOVE_MAPPING(dom, gpa, remaining);
246                 gpa += unmapped;
247                 remaining -= unmapped;
248         }
249 }
250
251 void *
252 iommu_host_domain(void)
253 {
254
255         return (host_domain);
256 }
257
258 void
259 iommu_add_device(void *dom, int bus, int slot, int func)
260 {
261
262         IOMMU_ADD_DEVICE(dom, bus, slot, func);
263 }
264
265 void
266 iommu_remove_device(void *dom, int bus, int slot, int func)
267 {
268
269         IOMMU_REMOVE_DEVICE(dom, bus, slot, func);
270 }
271
272 void
273 iommu_invalidate_tlb(void *domain)
274 {
275
276         IOMMU_INVALIDATE_TLB(domain);
277 }