iommu.c revision 279470
1215651Sweongyo/*-
2215651Sweongyo * Copyright (c) 2011 NetApp, Inc.
3215651Sweongyo * All rights reserved.
4215651Sweongyo *
5215651Sweongyo * Redistribution and use in source and binary forms, with or without
6220316Sthompsa * modification, are permitted provided that the following conditions
7215651Sweongyo * are met:
8215651Sweongyo * 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: stable/10/sys/amd64/vmm/io/iommu.c 279470 2015-03-01 04:22:06Z rstone $
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/iommu.c 279470 2015-03-01 04:22:06Z rstone $");
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
47SYSCTL_DECL(_hw_vmm);
48SYSCTL_NODE(_hw_vmm, OID_AUTO, iommu, CTLFLAG_RW, 0, "bhyve iommu parameters");
49
50static int iommu_avail;
51SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, initialized, CTLFLAG_RD, &iommu_avail,
52    0, "bhyve iommu initialized?");
53
54static struct iommu_ops *ops;
55static void *host_domain;
56
57static __inline int
58IOMMU_INIT(void)
59{
60	if (ops != NULL)
61		return ((*ops->init)());
62	else
63		return (ENXIO);
64}
65
66static __inline void
67IOMMU_CLEANUP(void)
68{
69	if (ops != NULL && iommu_avail)
70		(*ops->cleanup)();
71}
72
73static __inline void *
74IOMMU_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
83static __inline void
84IOMMU_DESTROY_DOMAIN(void *dom)
85{
86
87	if (ops != NULL && iommu_avail)
88		(*ops->destroy_domain)(dom);
89}
90
91static __inline uint64_t
92IOMMU_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
101static __inline uint64_t
102IOMMU_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
111static __inline void
112IOMMU_ADD_DEVICE(void *domain, uint16_t rid)
113{
114
115	if (ops != NULL && iommu_avail)
116		(*ops->add_device)(domain, rid);
117}
118
119static __inline void
120IOMMU_REMOVE_DEVICE(void *domain, uint16_t rid)
121{
122
123	if (ops != NULL && iommu_avail)
124		(*ops->remove_device)(domain, rid);
125}
126
127static __inline void
128IOMMU_INVALIDATE_TLB(void *domain)
129{
130
131	if (ops != NULL && iommu_avail)
132		(*ops->invalidate_tlb)(domain);
133}
134
135static __inline void
136IOMMU_ENABLE(void)
137{
138
139	if (ops != NULL && iommu_avail)
140		(*ops->enable)();
141}
142
143static __inline void
144IOMMU_DISABLE(void)
145{
146
147	if (ops != NULL && iommu_avail)
148		(*ops->disable)();
149}
150
151void
152iommu_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
208void
209iommu_cleanup(void)
210{
211	IOMMU_DISABLE();
212	IOMMU_DESTROY_DOMAIN(host_domain);
213	IOMMU_CLEANUP();
214}
215
216void *
217iommu_create_domain(vm_paddr_t maxaddr)
218{
219
220	return (IOMMU_CREATE_DOMAIN(maxaddr));
221}
222
223void
224iommu_destroy_domain(void *dom)
225{
226
227	IOMMU_DESTROY_DOMAIN(dom);
228}
229
230void
231iommu_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
245void
246iommu_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
259void *
260iommu_host_domain(void)
261{
262
263	return (host_domain);
264}
265
266void
267iommu_add_device(void *dom, uint16_t rid)
268{
269
270	IOMMU_ADD_DEVICE(dom, rid);
271}
272
273void
274iommu_remove_device(void *dom, uint16_t rid)
275{
276
277	IOMMU_REMOVE_DEVICE(dom, rid);
278}
279
280void
281iommu_invalidate_tlb(void *domain)
282{
283
284	IOMMU_INVALIDATE_TLB(domain);
285}
286