1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019-2020 Ruslan Bukin <br@bsdpad.com> 5 * 6 * This software was developed by SRI International and the University of 7 * Cambridge Computer Laboratory (Department of Computer Science and 8 * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the 9 * DARPA SSITH research programme. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include "opt_acpi.h" 34 35 #include <sys/cdefs.h> 36 #include <sys/types.h> 37 #include <sys/systm.h> 38 #include <sys/bus.h> 39 #include <sys/bitstring.h> 40 #include <sys/kernel.h> 41 #include <sys/rman.h> 42 #include <sys/tree.h> 43 #include <sys/taskqueue.h> 44 #include <sys/malloc.h> 45 #include <sys/module.h> 46 #include <vm/vm.h> 47 #include <vm/pmap.h> 48 #include <contrib/dev/acpica/include/acpi.h> 49 #include <dev/acpica/acpivar.h> 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 #include <dev/iommu/iommu.h> 53 54 #include <arm64/iommu/iommu.h> 55 56 #include "smmuvar.h" 57 58 #define MEMORY_RESOURCE_SIZE 0x20000 59 #define MAX_SMMU 8 60 61 struct smmu_acpi_devinfo { 62 struct resource_list di_rl; 63 }; 64 65 struct iort_table_data { 66 device_t parent; 67 device_t dev; 68 ACPI_IORT_SMMU_V3 *smmu[MAX_SMMU]; 69 int count; 70 }; 71 72 static void 73 iort_handler(ACPI_SUBTABLE_HEADER *entry, void *arg) 74 { 75 struct iort_table_data *iort_data; 76 ACPI_IORT_NODE *node; 77 int i; 78 79 iort_data = (struct iort_table_data *)arg; 80 i = iort_data->count; 81 82 switch(entry->Type) { 83 case ACPI_IORT_NODE_SMMU_V3: 84 if (i == MAX_SMMU) { 85 printf("SMMUv3 found, but no space available.\n"); 86 break; 87 } 88 89 if (iort_data->smmu[i] != NULL) { 90 if (bootverbose) 91 device_printf(iort_data->parent, 92 "smmu: Already have an SMMU table"); 93 break; 94 } 95 node = (ACPI_IORT_NODE *)entry; 96 iort_data->smmu[i] = (ACPI_IORT_SMMU_V3 *)node->NodeData; 97 iort_data->count++; 98 break; 99 default: 100 break; 101 } 102 } 103 104 static void 105 smmu_acpi_identify(driver_t *driver, device_t parent) 106 { 107 struct iort_table_data iort_data; 108 ACPI_TABLE_IORT *iort; 109 vm_paddr_t iort_pa; 110 uintptr_t priv; 111 device_t dev; 112 int i; 113 114 iort_pa = acpi_find_table(ACPI_SIG_IORT); 115 if (iort_pa == 0) 116 return; 117 118 iort = acpi_map_table(iort_pa, ACPI_SIG_IORT); 119 if (iort == NULL) { 120 device_printf(parent, "smmu: Unable to map the IORT\n"); 121 return; 122 } 123 124 iort_data.parent = parent; 125 for (i = 0; i < MAX_SMMU; i++) 126 iort_data.smmu[i] = NULL; 127 iort_data.count = 0; 128 129 acpi_walk_subtables(iort + 1, (char *)iort + iort->Header.Length, 130 iort_handler, &iort_data); 131 if (iort_data.count == 0) { 132 device_printf(parent, "No SMMU found.\n"); 133 goto out; 134 } 135 136 for (i = 0; i < iort_data.count; i++) { 137 dev = BUS_ADD_CHILD(parent, 138 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE, "smmu", -1); 139 if (dev == NULL) { 140 device_printf(parent, "add smmu child failed\n"); 141 goto out; 142 } 143 144 /* Add the IORT data */ 145 BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 0, 146 iort_data.smmu[i]->EventGsiv, 1); 147 BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 1, 148 iort_data.smmu[i]->PriGsiv, 1); 149 BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 2, 150 iort_data.smmu[i]->SyncGsiv, 1); 151 BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 3, 152 iort_data.smmu[i]->GerrGsiv, 1); 153 BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0, 154 iort_data.smmu[i]->BaseAddress, MEMORY_RESOURCE_SIZE); 155 156 priv = iort_data.smmu[i]->Flags; 157 priv <<= 32; 158 priv |= iort_data.smmu[i]->Model; 159 160 acpi_set_private(dev, (void *)priv); 161 } 162 163 iort_data.dev = dev; 164 165 out: 166 acpi_unmap_table(iort); 167 } 168 169 static int 170 smmu_acpi_probe(device_t dev) 171 { 172 173 switch((uintptr_t)acpi_get_private(dev) & 0xffffffff) { 174 case ACPI_IORT_SMMU_V3_GENERIC: 175 /* Generic SMMUv3 */ 176 break; 177 default: 178 return (ENXIO); 179 } 180 181 device_set_desc(dev, SMMU_DEVSTR); 182 183 return (BUS_PROBE_NOWILDCARD); 184 } 185 186 static int 187 smmu_acpi_attach(device_t dev) 188 { 189 struct smmu_softc *sc; 190 struct smmu_unit *unit; 191 struct iommu_unit *iommu; 192 uintptr_t priv; 193 int err; 194 int rid; 195 196 sc = device_get_softc(dev); 197 sc->dev = dev; 198 199 priv = (uintptr_t)acpi_get_private(dev); 200 if ((priv >> 32) & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) 201 sc->features |= SMMU_FEATURE_COHERENCY; 202 203 if (bootverbose) 204 device_printf(sc->dev, "%s: features %x\n", 205 __func__, sc->features); 206 207 rid = 0; 208 sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 209 RF_ACTIVE); 210 if (sc->res[0] == NULL) { 211 device_printf(dev, "Can't allocate memory resource.\n"); 212 err = ENXIO; 213 goto error; 214 } 215 216 /* 217 * Interrupt lines are "eventq", "priq", "cmdq-sync", "gerror". 218 */ 219 220 rid = 0; 221 sc->res[1] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 222 if (sc->res[1] == NULL) { 223 device_printf(dev, "Can't allocate eventq IRQ resource.\n"); 224 err = ENXIO; 225 goto error; 226 } 227 228 rid = 2; 229 sc->res[3] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 230 if (sc->res[3] == NULL) { 231 device_printf(dev, "Can't allocate cmdq-sync IRQ resource.\n"); 232 err = ENXIO; 233 goto error; 234 } 235 236 rid = 3; 237 sc->res[4] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 238 if (sc->res[4] == NULL) { 239 device_printf(dev, "Can't allocate gerror IRQ resource.\n"); 240 err = ENXIO; 241 goto error; 242 } 243 244 err = smmu_attach(dev); 245 if (err != 0) 246 goto error; 247 248 unit = &sc->unit; 249 unit->dev = dev; 250 251 iommu = &unit->iommu; 252 iommu->dev = dev; 253 254 LIST_INIT(&unit->domain_list); 255 256 /* Use memory start address as an xref. */ 257 sc->xref = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 258 259 err = iommu_register(iommu); 260 if (err) { 261 device_printf(dev, "Failed to register SMMU.\n"); 262 return (ENXIO); 263 } 264 265 return (0); 266 267 error: 268 if (bootverbose) { 269 device_printf(dev, 270 "Failed to attach. Error %d\n", err); 271 } 272 /* Failure so free resources. */ 273 smmu_detach(dev); 274 275 return (err); 276 } 277 278 static device_method_t smmu_acpi_methods[] = { 279 /* Device interface */ 280 DEVMETHOD(device_identify, smmu_acpi_identify), 281 DEVMETHOD(device_probe, smmu_acpi_probe), 282 DEVMETHOD(device_attach, smmu_acpi_attach), 283 284 /* End */ 285 DEVMETHOD_END 286 }; 287 288 DEFINE_CLASS_1(smmu, smmu_acpi_driver, smmu_acpi_methods, 289 sizeof(struct smmu_softc), smmu_driver); 290 291 EARLY_DRIVER_MODULE(smmu, acpi, smmu_acpi_driver, 0, 0, 292 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 293