1366f6083SPeter Grehan /*- 2366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3366f6083SPeter Grehan * All rights reserved. 4366f6083SPeter Grehan * 5366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 6366f6083SPeter Grehan * modification, are permitted provided that the following conditions 7366f6083SPeter Grehan * are met: 8366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 9366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 10366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 12366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 13366f6083SPeter Grehan * 14366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24366f6083SPeter Grehan * SUCH DAMAGE. 25366f6083SPeter Grehan * 26366f6083SPeter Grehan * $FreeBSD$ 27366f6083SPeter Grehan */ 28366f6083SPeter Grehan 29366f6083SPeter Grehan #include <sys/cdefs.h> 30366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/param.h> 33366f6083SPeter Grehan #include <sys/types.h> 34366f6083SPeter Grehan #include <sys/systm.h> 35366f6083SPeter Grehan #include <sys/bus.h> 36*51f45d01SNeel Natu #include <sys/sysctl.h> 37366f6083SPeter Grehan 38366f6083SPeter Grehan #include <dev/pci/pcivar.h> 39366f6083SPeter Grehan #include <dev/pci/pcireg.h> 40366f6083SPeter Grehan 41366f6083SPeter Grehan #include <machine/md_var.h> 42366f6083SPeter Grehan 43366f6083SPeter Grehan #include "vmm_util.h" 447ce04d0aSNeel Natu #include "vmm_mem.h" 45366f6083SPeter Grehan #include "iommu.h" 46366f6083SPeter Grehan 47*51f45d01SNeel Natu SYSCTL_DECL(_hw_vmm); 48*51f45d01SNeel Natu SYSCTL_NODE(_hw_vmm, OID_AUTO, iommu, CTLFLAG_RW, 0, "bhyve iommu parameters"); 49*51f45d01SNeel Natu 50*51f45d01SNeel Natu static int iommu_avail; 51*51f45d01SNeel Natu SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, initialized, CTLFLAG_RD, &iommu_avail, 52*51f45d01SNeel Natu 0, "bhyve iommu initialized?"); 53*51f45d01SNeel Natu 54366f6083SPeter Grehan static struct iommu_ops *ops; 55366f6083SPeter Grehan static void *host_domain; 56366f6083SPeter Grehan 57366f6083SPeter Grehan static __inline int 58366f6083SPeter Grehan IOMMU_INIT(void) 59366f6083SPeter Grehan { 60366f6083SPeter Grehan if (ops != NULL) 61366f6083SPeter Grehan return ((*ops->init)()); 62366f6083SPeter Grehan else 63366f6083SPeter Grehan return (ENXIO); 64366f6083SPeter Grehan } 65366f6083SPeter Grehan 66366f6083SPeter Grehan static __inline void 67366f6083SPeter Grehan IOMMU_CLEANUP(void) 68366f6083SPeter Grehan { 69366f6083SPeter Grehan if (ops != NULL && iommu_avail) 70366f6083SPeter Grehan (*ops->cleanup)(); 71366f6083SPeter Grehan } 72366f6083SPeter Grehan 73366f6083SPeter Grehan static __inline void * 74366f6083SPeter Grehan IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr) 75366f6083SPeter Grehan { 76366f6083SPeter Grehan 77366f6083SPeter Grehan if (ops != NULL && iommu_avail) 78366f6083SPeter Grehan return ((*ops->create_domain)(maxaddr)); 79366f6083SPeter Grehan else 80366f6083SPeter Grehan return (NULL); 81366f6083SPeter Grehan } 82366f6083SPeter Grehan 83366f6083SPeter Grehan static __inline void 84366f6083SPeter Grehan IOMMU_DESTROY_DOMAIN(void *dom) 85366f6083SPeter Grehan { 86366f6083SPeter Grehan 87366f6083SPeter Grehan if (ops != NULL && iommu_avail) 88366f6083SPeter Grehan (*ops->destroy_domain)(dom); 89366f6083SPeter Grehan } 90366f6083SPeter Grehan 91366f6083SPeter Grehan static __inline uint64_t 92366f6083SPeter Grehan IOMMU_CREATE_MAPPING(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len) 93366f6083SPeter Grehan { 94366f6083SPeter Grehan 95366f6083SPeter Grehan if (ops != NULL && iommu_avail) 96366f6083SPeter Grehan return ((*ops->create_mapping)(domain, gpa, hpa, len)); 97366f6083SPeter Grehan else 98366f6083SPeter Grehan return (len); /* XXX */ 99366f6083SPeter Grehan } 100366f6083SPeter Grehan 1017ce04d0aSNeel Natu static __inline uint64_t 1027ce04d0aSNeel Natu IOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len) 1037ce04d0aSNeel Natu { 1047ce04d0aSNeel Natu 1057ce04d0aSNeel Natu if (ops != NULL && iommu_avail) 1067ce04d0aSNeel Natu return ((*ops->remove_mapping)(domain, gpa, len)); 1077ce04d0aSNeel Natu else 1087ce04d0aSNeel Natu return (len); /* XXX */ 1097ce04d0aSNeel Natu } 1107ce04d0aSNeel Natu 111366f6083SPeter Grehan static __inline void 112366f6083SPeter Grehan IOMMU_ADD_DEVICE(void *domain, int bus, int slot, int func) 113366f6083SPeter Grehan { 114366f6083SPeter Grehan 115366f6083SPeter Grehan if (ops != NULL && iommu_avail) 116366f6083SPeter Grehan (*ops->add_device)(domain, bus, slot, func); 117366f6083SPeter Grehan } 118366f6083SPeter Grehan 119366f6083SPeter Grehan static __inline void 120366f6083SPeter Grehan IOMMU_REMOVE_DEVICE(void *domain, int bus, int slot, int func) 121366f6083SPeter Grehan { 122366f6083SPeter Grehan 123366f6083SPeter Grehan if (ops != NULL && iommu_avail) 124366f6083SPeter Grehan (*ops->remove_device)(domain, bus, slot, func); 125366f6083SPeter Grehan } 126366f6083SPeter Grehan 127366f6083SPeter Grehan static __inline void 1287ce04d0aSNeel Natu IOMMU_INVALIDATE_TLB(void *domain) 1297ce04d0aSNeel Natu { 1307ce04d0aSNeel Natu 1317ce04d0aSNeel Natu if (ops != NULL && iommu_avail) 1327ce04d0aSNeel Natu (*ops->invalidate_tlb)(domain); 1337ce04d0aSNeel Natu } 1347ce04d0aSNeel Natu 1357ce04d0aSNeel Natu static __inline void 136366f6083SPeter Grehan IOMMU_ENABLE(void) 137366f6083SPeter Grehan { 138366f6083SPeter Grehan 139366f6083SPeter Grehan if (ops != NULL && iommu_avail) 140366f6083SPeter Grehan (*ops->enable)(); 141366f6083SPeter Grehan } 142366f6083SPeter Grehan 143366f6083SPeter Grehan static __inline void 144366f6083SPeter Grehan IOMMU_DISABLE(void) 145366f6083SPeter Grehan { 146366f6083SPeter Grehan 147366f6083SPeter Grehan if (ops != NULL && iommu_avail) 148366f6083SPeter Grehan (*ops->disable)(); 149366f6083SPeter Grehan } 150366f6083SPeter Grehan 151366f6083SPeter Grehan void 152366f6083SPeter Grehan iommu_init(void) 153366f6083SPeter Grehan { 154366f6083SPeter Grehan int error, bus, slot, func; 155366f6083SPeter Grehan vm_paddr_t maxaddr; 156366f6083SPeter Grehan const char *name; 157366f6083SPeter Grehan device_t dev; 158366f6083SPeter Grehan 159366f6083SPeter Grehan if (vmm_is_intel()) 160366f6083SPeter Grehan ops = &iommu_ops_intel; 161366f6083SPeter Grehan else if (vmm_is_amd()) 162366f6083SPeter Grehan ops = &iommu_ops_amd; 163366f6083SPeter Grehan else 164366f6083SPeter Grehan ops = NULL; 165366f6083SPeter Grehan 166366f6083SPeter Grehan error = IOMMU_INIT(); 167366f6083SPeter Grehan if (error) 168366f6083SPeter Grehan return; 169366f6083SPeter Grehan 170*51f45d01SNeel Natu iommu_avail = 1; 171366f6083SPeter Grehan 172366f6083SPeter Grehan /* 173366f6083SPeter Grehan * Create a domain for the devices owned by the host 174366f6083SPeter Grehan */ 1757ce04d0aSNeel Natu maxaddr = vmm_mem_maxaddr(); 176366f6083SPeter Grehan host_domain = IOMMU_CREATE_DOMAIN(maxaddr); 177366f6083SPeter Grehan if (host_domain == NULL) 178366f6083SPeter Grehan panic("iommu_init: unable to create a host domain"); 179366f6083SPeter Grehan 180366f6083SPeter Grehan /* 1817ce04d0aSNeel Natu * Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to 182366f6083SPeter Grehan * the host 183366f6083SPeter Grehan */ 184366f6083SPeter Grehan iommu_create_mapping(host_domain, 0, 0, maxaddr); 185366f6083SPeter Grehan 186366f6083SPeter Grehan for (bus = 0; bus <= PCI_BUSMAX; bus++) { 187366f6083SPeter Grehan for (slot = 0; slot <= PCI_SLOTMAX; slot++) { 188366f6083SPeter Grehan for (func = 0; func <= PCI_FUNCMAX; func++) { 189366f6083SPeter Grehan dev = pci_find_dbsf(0, bus, slot, func); 190366f6083SPeter Grehan if (dev == NULL) 191366f6083SPeter Grehan continue; 192366f6083SPeter Grehan 193366f6083SPeter Grehan /* skip passthrough devices */ 194366f6083SPeter Grehan name = device_get_name(dev); 195366f6083SPeter Grehan if (name != NULL && strcmp(name, "ppt") == 0) 196366f6083SPeter Grehan continue; 197366f6083SPeter Grehan 198366f6083SPeter Grehan /* everything else belongs to the host domain */ 199366f6083SPeter Grehan iommu_add_device(host_domain, bus, slot, func); 200366f6083SPeter Grehan } 201366f6083SPeter Grehan } 202366f6083SPeter Grehan } 203366f6083SPeter Grehan IOMMU_ENABLE(); 204366f6083SPeter Grehan 205366f6083SPeter Grehan } 206366f6083SPeter Grehan 207366f6083SPeter Grehan void 208366f6083SPeter Grehan iommu_cleanup(void) 209366f6083SPeter Grehan { 210366f6083SPeter Grehan IOMMU_DISABLE(); 211366f6083SPeter Grehan IOMMU_DESTROY_DOMAIN(host_domain); 212366f6083SPeter Grehan IOMMU_CLEANUP(); 213366f6083SPeter Grehan } 214366f6083SPeter Grehan 215366f6083SPeter Grehan void * 216366f6083SPeter Grehan iommu_create_domain(vm_paddr_t maxaddr) 217366f6083SPeter Grehan { 218366f6083SPeter Grehan 219366f6083SPeter Grehan return (IOMMU_CREATE_DOMAIN(maxaddr)); 220366f6083SPeter Grehan } 221366f6083SPeter Grehan 222366f6083SPeter Grehan void 223366f6083SPeter Grehan iommu_destroy_domain(void *dom) 224366f6083SPeter Grehan { 225366f6083SPeter Grehan 226366f6083SPeter Grehan IOMMU_DESTROY_DOMAIN(dom); 227366f6083SPeter Grehan } 228366f6083SPeter Grehan 229366f6083SPeter Grehan void 230366f6083SPeter Grehan iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len) 231366f6083SPeter Grehan { 232366f6083SPeter Grehan uint64_t mapped, remaining; 233366f6083SPeter Grehan 234366f6083SPeter Grehan remaining = len; 235366f6083SPeter Grehan 236366f6083SPeter Grehan while (remaining > 0) { 237366f6083SPeter Grehan mapped = IOMMU_CREATE_MAPPING(dom, gpa, hpa, remaining); 238366f6083SPeter Grehan gpa += mapped; 239366f6083SPeter Grehan hpa += mapped; 240366f6083SPeter Grehan remaining -= mapped; 241366f6083SPeter Grehan } 242366f6083SPeter Grehan } 243366f6083SPeter Grehan 244366f6083SPeter Grehan void 2457ce04d0aSNeel Natu iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len) 2467ce04d0aSNeel Natu { 2477ce04d0aSNeel Natu uint64_t unmapped, remaining; 2487ce04d0aSNeel Natu 2497ce04d0aSNeel Natu remaining = len; 2507ce04d0aSNeel Natu 2517ce04d0aSNeel Natu while (remaining > 0) { 2527ce04d0aSNeel Natu unmapped = IOMMU_REMOVE_MAPPING(dom, gpa, remaining); 2537ce04d0aSNeel Natu gpa += unmapped; 2547ce04d0aSNeel Natu remaining -= unmapped; 2557ce04d0aSNeel Natu } 2567ce04d0aSNeel Natu } 2577ce04d0aSNeel Natu 2587ce04d0aSNeel Natu void * 2597ce04d0aSNeel Natu iommu_host_domain(void) 2607ce04d0aSNeel Natu { 2617ce04d0aSNeel Natu 2627ce04d0aSNeel Natu return (host_domain); 2637ce04d0aSNeel Natu } 2647ce04d0aSNeel Natu 2657ce04d0aSNeel Natu void 266366f6083SPeter Grehan iommu_add_device(void *dom, int bus, int slot, int func) 267366f6083SPeter Grehan { 268366f6083SPeter Grehan 269366f6083SPeter Grehan IOMMU_ADD_DEVICE(dom, bus, slot, func); 270366f6083SPeter Grehan } 271366f6083SPeter Grehan 272366f6083SPeter Grehan void 273366f6083SPeter Grehan iommu_remove_device(void *dom, int bus, int slot, int func) 274366f6083SPeter Grehan { 275366f6083SPeter Grehan 276366f6083SPeter Grehan IOMMU_REMOVE_DEVICE(dom, bus, slot, func); 277366f6083SPeter Grehan } 2787ce04d0aSNeel Natu 2797ce04d0aSNeel Natu void 2807ce04d0aSNeel Natu iommu_invalidate_tlb(void *domain) 2817ce04d0aSNeel Natu { 2827ce04d0aSNeel Natu 2837ce04d0aSNeel Natu IOMMU_INVALIDATE_TLB(domain); 2847ce04d0aSNeel Natu } 285