1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD$
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD$");
31221828Sgrehan
32221828Sgrehan#include <sys/param.h>
33221828Sgrehan#include <sys/types.h>
34221828Sgrehan#include <sys/systm.h>
35221828Sgrehan#include <sys/bus.h>
36221828Sgrehan
37221828Sgrehan#include <dev/pci/pcivar.h>
38221828Sgrehan#include <dev/pci/pcireg.h>
39221828Sgrehan
40221828Sgrehan#include <machine/md_var.h>
41221828Sgrehan
42221828Sgrehan#include "vmm_util.h"
43241362Sneel#include "vmm_mem.h"
44221828Sgrehan#include "iommu.h"
45221828Sgrehan
46221828Sgrehanstatic boolean_t iommu_avail;
47221828Sgrehanstatic struct iommu_ops *ops;
48221828Sgrehanstatic void *host_domain;
49221828Sgrehan
50221828Sgrehanstatic __inline int
51221828SgrehanIOMMU_INIT(void)
52221828Sgrehan{
53221828Sgrehan	if (ops != NULL)
54221828Sgrehan		return ((*ops->init)());
55221828Sgrehan	else
56221828Sgrehan		return (ENXIO);
57221828Sgrehan}
58221828Sgrehan
59221828Sgrehanstatic __inline void
60221828SgrehanIOMMU_CLEANUP(void)
61221828Sgrehan{
62221828Sgrehan	if (ops != NULL && iommu_avail)
63221828Sgrehan		(*ops->cleanup)();
64221828Sgrehan}
65221828Sgrehan
66221828Sgrehanstatic __inline void *
67221828SgrehanIOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr)
68221828Sgrehan{
69221828Sgrehan
70221828Sgrehan	if (ops != NULL && iommu_avail)
71221828Sgrehan		return ((*ops->create_domain)(maxaddr));
72221828Sgrehan	else
73221828Sgrehan		return (NULL);
74221828Sgrehan}
75221828Sgrehan
76221828Sgrehanstatic __inline void
77221828SgrehanIOMMU_DESTROY_DOMAIN(void *dom)
78221828Sgrehan{
79221828Sgrehan
80221828Sgrehan	if (ops != NULL && iommu_avail)
81221828Sgrehan		(*ops->destroy_domain)(dom);
82221828Sgrehan}
83221828Sgrehan
84221828Sgrehanstatic __inline uint64_t
85221828SgrehanIOMMU_CREATE_MAPPING(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
86221828Sgrehan{
87221828Sgrehan
88221828Sgrehan	if (ops != NULL && iommu_avail)
89221828Sgrehan		return ((*ops->create_mapping)(domain, gpa, hpa, len));
90221828Sgrehan	else
91221828Sgrehan		return (len);		/* XXX */
92221828Sgrehan}
93221828Sgrehan
94241362Sneelstatic __inline uint64_t
95241362SneelIOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len)
96241362Sneel{
97241362Sneel
98241362Sneel	if (ops != NULL && iommu_avail)
99241362Sneel		return ((*ops->remove_mapping)(domain, gpa, len));
100241362Sneel	else
101241362Sneel		return (len);		/* XXX */
102241362Sneel}
103241362Sneel
104221828Sgrehanstatic __inline void
105221828SgrehanIOMMU_ADD_DEVICE(void *domain, int bus, int slot, int func)
106221828Sgrehan{
107221828Sgrehan
108221828Sgrehan	if (ops != NULL && iommu_avail)
109221828Sgrehan		(*ops->add_device)(domain, bus, slot, func);
110221828Sgrehan}
111221828Sgrehan
112221828Sgrehanstatic __inline void
113221828SgrehanIOMMU_REMOVE_DEVICE(void *domain, int bus, int slot, int func)
114221828Sgrehan{
115221828Sgrehan
116221828Sgrehan	if (ops != NULL && iommu_avail)
117221828Sgrehan		(*ops->remove_device)(domain, bus, slot, func);
118221828Sgrehan}
119221828Sgrehan
120221828Sgrehanstatic __inline void
121241362SneelIOMMU_INVALIDATE_TLB(void *domain)
122241362Sneel{
123241362Sneel
124241362Sneel	if (ops != NULL && iommu_avail)
125241362Sneel		(*ops->invalidate_tlb)(domain);
126241362Sneel}
127241362Sneel
128241362Sneelstatic __inline void
129221828SgrehanIOMMU_ENABLE(void)
130221828Sgrehan{
131221828Sgrehan
132221828Sgrehan	if (ops != NULL && iommu_avail)
133221828Sgrehan		(*ops->enable)();
134221828Sgrehan}
135221828Sgrehan
136221828Sgrehanstatic __inline void
137221828SgrehanIOMMU_DISABLE(void)
138221828Sgrehan{
139221828Sgrehan
140221828Sgrehan	if (ops != NULL && iommu_avail)
141221828Sgrehan		(*ops->disable)();
142221828Sgrehan}
143221828Sgrehan
144221828Sgrehanvoid
145221828Sgrehaniommu_init(void)
146221828Sgrehan{
147221828Sgrehan	int error, bus, slot, func;
148221828Sgrehan	vm_paddr_t maxaddr;
149221828Sgrehan	const char *name;
150221828Sgrehan	device_t dev;
151221828Sgrehan
152221828Sgrehan	if (vmm_is_intel())
153221828Sgrehan		ops = &iommu_ops_intel;
154221828Sgrehan	else if (vmm_is_amd())
155221828Sgrehan		ops = &iommu_ops_amd;
156221828Sgrehan	else
157221828Sgrehan		ops = NULL;
158221828Sgrehan
159221828Sgrehan	error = IOMMU_INIT();
160221828Sgrehan	if (error)
161221828Sgrehan		return;
162221828Sgrehan
163221828Sgrehan	iommu_avail = TRUE;
164221828Sgrehan
165221828Sgrehan	/*
166221828Sgrehan	 * Create a domain for the devices owned by the host
167221828Sgrehan	 */
168241362Sneel	maxaddr = vmm_mem_maxaddr();
169221828Sgrehan	host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
170221828Sgrehan	if (host_domain == NULL)
171221828Sgrehan		panic("iommu_init: unable to create a host domain");
172221828Sgrehan
173221828Sgrehan	/*
174241362Sneel	 * Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to
175221828Sgrehan	 * the host
176221828Sgrehan	 */
177221828Sgrehan	iommu_create_mapping(host_domain, 0, 0, maxaddr);
178221828Sgrehan
179221828Sgrehan	for (bus = 0; bus <= PCI_BUSMAX; bus++) {
180221828Sgrehan		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
181221828Sgrehan			for (func = 0; func <= PCI_FUNCMAX; func++) {
182221828Sgrehan				dev = pci_find_dbsf(0, bus, slot, func);
183221828Sgrehan				if (dev == NULL)
184221828Sgrehan					continue;
185221828Sgrehan
186221828Sgrehan				/* skip passthrough devices */
187221828Sgrehan				name = device_get_name(dev);
188221828Sgrehan				if (name != NULL && strcmp(name, "ppt") == 0)
189221828Sgrehan					continue;
190221828Sgrehan
191221828Sgrehan				/* everything else belongs to the host domain */
192221828Sgrehan				iommu_add_device(host_domain, bus, slot, func);
193221828Sgrehan			}
194221828Sgrehan		}
195221828Sgrehan	}
196221828Sgrehan	IOMMU_ENABLE();
197221828Sgrehan
198221828Sgrehan}
199221828Sgrehan
200221828Sgrehanvoid
201221828Sgrehaniommu_cleanup(void)
202221828Sgrehan{
203221828Sgrehan	IOMMU_DISABLE();
204221828Sgrehan	IOMMU_DESTROY_DOMAIN(host_domain);
205221828Sgrehan	IOMMU_CLEANUP();
206221828Sgrehan}
207221828Sgrehan
208221828Sgrehanvoid *
209221828Sgrehaniommu_create_domain(vm_paddr_t maxaddr)
210221828Sgrehan{
211221828Sgrehan
212221828Sgrehan	return (IOMMU_CREATE_DOMAIN(maxaddr));
213221828Sgrehan}
214221828Sgrehan
215221828Sgrehanvoid
216221828Sgrehaniommu_destroy_domain(void *dom)
217221828Sgrehan{
218221828Sgrehan
219221828Sgrehan	IOMMU_DESTROY_DOMAIN(dom);
220221828Sgrehan}
221221828Sgrehan
222221828Sgrehanvoid
223221828Sgrehaniommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len)
224221828Sgrehan{
225221828Sgrehan	uint64_t mapped, remaining;
226221828Sgrehan
227221828Sgrehan	remaining = len;
228221828Sgrehan
229221828Sgrehan	while (remaining > 0) {
230221828Sgrehan		mapped = IOMMU_CREATE_MAPPING(dom, gpa, hpa, remaining);
231221828Sgrehan		gpa += mapped;
232221828Sgrehan		hpa += mapped;
233221828Sgrehan		remaining -= mapped;
234221828Sgrehan	}
235221828Sgrehan}
236221828Sgrehan
237221828Sgrehanvoid
238241362Sneeliommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len)
239241362Sneel{
240241362Sneel	uint64_t unmapped, remaining;
241241362Sneel
242241362Sneel	remaining = len;
243241362Sneel
244241362Sneel	while (remaining > 0) {
245241362Sneel		unmapped = IOMMU_REMOVE_MAPPING(dom, gpa, remaining);
246241362Sneel		gpa += unmapped;
247241362Sneel		remaining -= unmapped;
248241362Sneel	}
249241362Sneel}
250241362Sneel
251241362Sneelvoid *
252241362Sneeliommu_host_domain(void)
253241362Sneel{
254241362Sneel
255241362Sneel	return (host_domain);
256241362Sneel}
257241362Sneel
258241362Sneelvoid
259221828Sgrehaniommu_add_device(void *dom, int bus, int slot, int func)
260221828Sgrehan{
261221828Sgrehan
262221828Sgrehan	IOMMU_ADD_DEVICE(dom, bus, slot, func);
263221828Sgrehan}
264221828Sgrehan
265221828Sgrehanvoid
266221828Sgrehaniommu_remove_device(void *dom, int bus, int slot, int func)
267221828Sgrehan{
268221828Sgrehan
269221828Sgrehan	IOMMU_REMOVE_DEVICE(dom, bus, slot, func);
270221828Sgrehan}
271241362Sneel
272241362Sneelvoid
273241362Sneeliommu_invalidate_tlb(void *domain)
274241362Sneel{
275241362Sneel
276241362Sneel	IOMMU_INVALIDATE_TLB(domain);
277241362Sneel}
278