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
46static boolean_t iommu_avail;
47static struct iommu_ops *ops;
48static void *host_domain;
49
50static __inline int
51IOMMU_INIT(void)
52{
53	if (ops != NULL)
54		return ((*ops->init)());
55	else
56		return (ENXIO);
57}
58
59static __inline void
60IOMMU_CLEANUP(void)
61{
62	if (ops != NULL && iommu_avail)
63		(*ops->cleanup)();
64}
65
66static __inline void *
67IOMMU_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
76static __inline void
77IOMMU_DESTROY_DOMAIN(void *dom)
78{
79
80	if (ops != NULL && iommu_avail)
81		(*ops->destroy_domain)(dom);
82}
83
84static __inline uint64_t
85IOMMU_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
94static __inline uint64_t
95IOMMU_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
104static __inline void
105IOMMU_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
112static __inline void
113IOMMU_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
120static __inline void
121IOMMU_INVALIDATE_TLB(void *domain)
122{
123
124	if (ops != NULL && iommu_avail)
125		(*ops->invalidate_tlb)(domain);
126}
127
128static __inline void
129IOMMU_ENABLE(void)
130{
131
132	if (ops != NULL && iommu_avail)
133		(*ops->enable)();
134}
135
136static __inline void
137IOMMU_DISABLE(void)
138{
139
140	if (ops != NULL && iommu_avail)
141		(*ops->disable)();
142}
143
144void
145iommu_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
200void
201iommu_cleanup(void)
202{
203	IOMMU_DISABLE();
204	IOMMU_DESTROY_DOMAIN(host_domain);
205	IOMMU_CLEANUP();
206}
207
208void *
209iommu_create_domain(vm_paddr_t maxaddr)
210{
211
212	return (IOMMU_CREATE_DOMAIN(maxaddr));
213}
214
215void
216iommu_destroy_domain(void *dom)
217{
218
219	IOMMU_DESTROY_DOMAIN(dom);
220}
221
222void
223iommu_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
237void
238iommu_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
251void *
252iommu_host_domain(void)
253{
254
255	return (host_domain);
256}
257
258void
259iommu_add_device(void *dom, int bus, int slot, int func)
260{
261
262	IOMMU_ADD_DEVICE(dom, bus, slot, func);
263}
264
265void
266iommu_remove_device(void *dom, int bus, int slot, int func)
267{
268
269	IOMMU_REMOVE_DEVICE(dom, bus, slot, func);
270}
271
272void
273iommu_invalidate_tlb(void *domain)
274{
275
276	IOMMU_INVALIDATE_TLB(domain);
277}
278