1 /*- 2 * Copyright (c) 2016, Anish Gupta (anish@freebsd.org) 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 unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_acpi.h" 31 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/malloc.h> 36 37 #include <machine/vmparam.h> 38 39 #include <vm/vm.h> 40 #include <vm/pmap.h> 41 42 #include <contrib/dev/acpica/include/acpi.h> 43 #include <contrib/dev/acpica/include/accommon.h> 44 #include <dev/acpica/acpivar.h> 45 46 #include "io/iommu.h" 47 #include "amdvi_priv.h" 48 49 device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */ 50 int ivhd_count; /* Number of IVHD or AMD-Vi devices. */ 51 52 extern int amdvi_ptp_level; /* Page table levels. */ 53 54 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER * ptr, void *arg); 55 56 /* 57 * Iterate IVRS table for IVHD and IVMD device type. 58 */ 59 static void 60 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg) 61 { 62 ACPI_TABLE_IVRS *ivrs; 63 ACPI_IVRS_HEADER *ivrs_hdr, *end; 64 ACPI_STATUS status; 65 66 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs); 67 if (ACPI_FAILURE(status)) 68 return; 69 70 if (ivrs->Header.Length == 0) { 71 return; 72 } 73 74 ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1); 75 end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length); 76 77 while (ivrs_hdr < end) { 78 switch (ivrs_hdr->Type) { 79 case ACPI_IVRS_TYPE_HARDWARE: /* Legacy */ 80 case 0x11: 81 case 0x40: /* ACPI HID */ 82 if (!iter(ivrs_hdr, arg)) 83 return; 84 break; 85 86 case ACPI_IVRS_TYPE_MEMORY1: 87 case ACPI_IVRS_TYPE_MEMORY2: 88 case ACPI_IVRS_TYPE_MEMORY3: 89 if (!iter(ivrs_hdr, arg)) 90 return; 91 92 break; 93 94 default: 95 printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type); 96 97 } 98 99 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr + 100 ivrs_hdr->Length); 101 if (ivrs_hdr->Length < 0) { 102 printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n", ivrs_hdr->Length); 103 break; 104 } 105 } 106 } 107 108 static int 109 ivrs_is_ivhd(UINT8 type) 110 { 111 112 if ((type == ACPI_IVRS_TYPE_HARDWARE) || (type == 0x11) || (type == 0x40)) 113 return (1); 114 115 return (0); 116 } 117 118 /* Count the number of AMD-Vi devices in the system. */ 119 static int 120 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg) 121 { 122 123 if (ivrs_is_ivhd(ivrs_he->Type)) 124 ivhd_count++; 125 126 return (1); 127 } 128 129 struct find_ivrs_hdr_args { 130 int i; 131 ACPI_IVRS_HEADER *ptr; 132 }; 133 134 static int 135 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args) 136 { 137 struct find_ivrs_hdr_args *fi; 138 139 fi = (struct find_ivrs_hdr_args *)args; 140 if (ivrs_is_ivhd(ivrs_hdr->Type)) { 141 if (fi->i == 0) { 142 fi->ptr = ivrs_hdr; 143 return (0); 144 } 145 fi->i--; 146 } 147 148 return (1); 149 } 150 151 static ACPI_IVRS_HARDWARE * 152 ivhd_find_by_index(int idx) 153 { 154 struct find_ivrs_hdr_args fi; 155 156 fi.i = idx; 157 fi.ptr = NULL; 158 159 ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi); 160 161 return ((ACPI_IVRS_HARDWARE *)fi.ptr); 162 } 163 164 static void 165 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id, 166 uint32_t end_id, uint8_t cfg, bool ats) 167 { 168 struct ivhd_dev_cfg *dev_cfg; 169 170 /* If device doesn't have special data, don't add it. */ 171 if (!cfg) 172 return; 173 174 dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++]; 175 dev_cfg->start_id = start_id; 176 dev_cfg->end_id = end_id; 177 dev_cfg->data = cfg; 178 dev_cfg->enable_ats = ats; 179 } 180 181 /* 182 * Record device attributes as suggested by BIOS. 183 */ 184 static int 185 ivhd_dev_parse(ACPI_IVRS_HARDWARE * ivhd, struct amdvi_softc *softc) 186 { 187 ACPI_IVRS_DE_HEADER *de, *end; 188 int range_start_id = 0, range_end_id = 0; 189 uint32_t *extended; 190 uint8_t all_data = 0, range_data = 0; 191 bool range_enable_ats = false, enable_ats; 192 193 softc->start_dev_rid = ~0; 194 softc->end_dev_rid = 0; 195 196 de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd + 197 sizeof(ACPI_IVRS_HARDWARE)); 198 end = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd + 199 ivhd->Header.Length); 200 201 while (de < (ACPI_IVRS_DE_HEADER *) end) { 202 softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id); 203 softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id); 204 switch (de->Type) { 205 case ACPI_IVRS_TYPE_ALL: 206 all_data = de->DataSetting; 207 break; 208 209 case ACPI_IVRS_TYPE_SELECT: 210 case ACPI_IVRS_TYPE_ALIAS_SELECT: 211 case ACPI_IVRS_TYPE_EXT_SELECT: 212 enable_ats = false; 213 if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) { 214 extended = (uint32_t *)(de + 1); 215 enable_ats = 216 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ? 217 false : true; 218 } 219 ivhd_dev_add_entry(softc, de->Id, de->Id, 220 de->DataSetting | all_data, enable_ats); 221 break; 222 223 case ACPI_IVRS_TYPE_START: 224 case ACPI_IVRS_TYPE_ALIAS_START: 225 case ACPI_IVRS_TYPE_EXT_START: 226 range_start_id = de->Id; 227 range_data = de->DataSetting; 228 if (de->Type == ACPI_IVRS_TYPE_EXT_START) { 229 extended = (uint32_t *)(de + 1); 230 range_enable_ats = 231 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ? 232 false : true; 233 } 234 break; 235 236 case ACPI_IVRS_TYPE_END: 237 range_end_id = de->Id; 238 ivhd_dev_add_entry(softc, range_start_id, range_end_id, 239 range_data | all_data, range_enable_ats); 240 range_start_id = range_end_id = 0; 241 range_data = 0; 242 all_data = 0; 243 break; 244 245 case ACPI_IVRS_TYPE_PAD4: 246 break; 247 248 case ACPI_IVRS_TYPE_SPECIAL: 249 /* HPET or IOAPIC */ 250 break; 251 default: 252 if ((de->Type < 5) || 253 (de->Type >= ACPI_IVRS_TYPE_PAD8)) 254 device_printf(softc->dev, 255 "Unknown dev entry:0x%x\n", de->Type); 256 } 257 258 if (softc->dev_cfg_cnt > 259 (sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) { 260 device_printf(softc->dev, 261 "WARN Too many device entries.\n"); 262 return (EINVAL); 263 } 264 de++; 265 } 266 267 KASSERT((softc->end_dev_rid >= softc->start_dev_rid), 268 ("Device end[0x%x] < start[0x%x.\n", 269 softc->end_dev_rid, softc->start_dev_rid)); 270 271 return (0); 272 } 273 274 static void 275 ivhd_identify(driver_t *driver, device_t parent) 276 { 277 ACPI_TABLE_IVRS *ivrs; 278 ACPI_IVRS_HARDWARE *ivhd; 279 ACPI_STATUS status; 280 uint32_t info; 281 int i, count = 0; 282 283 if (acpi_disabled("ivhd")) 284 return; 285 286 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs); 287 if (ACPI_FAILURE(status)) 288 return; 289 290 if (ivrs->Header.Length == 0) { 291 return; 292 } 293 294 info = ivrs->Info; 295 printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n", 296 REG_BITS(info, 21, 15), REG_BITS(info, 14, 8), 297 REG_BITS(info, 7, 5), REG_BITS(info, 22, 22), 298 "\020\001HtAtsResv"); 299 300 ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL); 301 if (!ivhd_count) 302 return; 303 304 ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF, 305 M_WAITOK | M_ZERO); 306 for (i = 0; i < ivhd_count; i++) { 307 ivhd = ivhd_find_by_index(i); 308 if (ivhd == NULL) { 309 printf("Can't find IVHD entry%d\n", i); 310 continue; 311 } 312 313 ivhd_devs[i] = BUS_ADD_CHILD(parent, 1, "ivhd", i); 314 /* 315 * XXX: In case device was not destroyed before, add will fail. 316 * locate the old device instance. 317 */ 318 if (ivhd_devs[i] == NULL) { 319 ivhd_devs[i] = device_find_child(parent, "ivhd", i); 320 if (ivhd_devs[i] == NULL) { 321 printf("AMD-Vi: cant find AMD-Vi dev%d\n", i); 322 break; 323 } 324 } 325 count++; 326 } 327 328 /* 329 * Update device count in case failed to attach. 330 */ 331 ivhd_count = count; 332 } 333 334 static int 335 ivhd_probe(device_t dev) 336 { 337 338 if (acpi_get_handle(dev) != NULL) 339 return (ENXIO); 340 device_set_desc(dev, "AMD-Vi/IOMMU or ivhd"); 341 342 return (BUS_PROBE_NOWILDCARD); 343 } 344 345 static int 346 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd) 347 { 348 device_t dev; 349 int max_ptp_level; 350 351 dev = softc->dev; 352 device_printf(dev, "Flag:%b\n", softc->ivhd_flag, 353 "\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup" 354 "\006Coherent\007PreFSup\008PPRSup"); 355 /* 356 * If no extended feature[EFR], its rev1 with maximum paging level as 7. 357 */ 358 max_ptp_level = 7; 359 if (softc->ivhd_efr) { 360 device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d " 361 "MsiNumPr = %d PNBanks= %d PNCounters= %d\n" 362 "max PASID = %d EFR: %b \n", 363 REG_BITS(softc->ivhd_efr, 31, 30), 364 REG_BITS(softc->ivhd_efr, 29, 28), 365 REG_BITS(softc->ivhd_efr, 4, 3), 366 REG_BITS(softc->ivhd_efr, 27, 23), 367 REG_BITS(softc->ivhd_efr, 22, 17), 368 REG_BITS(softc->ivhd_efr, 16, 13), 369 REG_BITS(softc->ivhd_efr, 12, 8), 370 softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup" 371 "\006GASup\007HESup\008PPRSup"); 372 373 max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4; 374 } 375 376 /* Make sure device support minimum page level as requested by user. */ 377 if (max_ptp_level < amdvi_ptp_level) { 378 device_printf(dev, "Insufficient PTP level:%d\n", 379 max_ptp_level); 380 return (EINVAL); 381 } 382 383 device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n", 384 max_ptp_level, amdvi_ptp_level); 385 device_printf(softc->dev, "device supported range " 386 "[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid); 387 388 return (0); 389 } 390 391 static int 392 ivhd_attach(device_t dev) 393 { 394 ACPI_IVRS_HARDWARE *ivhd; 395 struct amdvi_softc *softc; 396 int status, unit; 397 398 unit = device_get_unit(dev); 399 /* Make sure its same device for which attach is called. */ 400 if (ivhd_devs[unit] != dev) 401 panic("Not same device old %p new %p", ivhd_devs[unit], dev); 402 403 softc = device_get_softc(dev); 404 softc->dev = dev; 405 ivhd = ivhd_find_by_index(unit); 406 if (ivhd == NULL) 407 return (EINVAL); 408 409 softc->pci_seg = ivhd->PciSegmentGroup; 410 softc->pci_rid = ivhd->Header.DeviceId; 411 softc->ivhd_flag = ivhd->Header.Flags; 412 softc->ivhd_efr = ivhd->Reserved; 413 /* 414 * PCI capability has more capabilities that are not part of IVRS. 415 */ 416 softc->cap_off = ivhd->CapabilityOffset; 417 418 #ifdef notyet 419 /* IVHD Info bit[4:0] is event MSI/X number. */ 420 softc->event_msix = ivhd->Info & 0x1F; 421 #endif 422 softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress); 423 status = ivhd_dev_parse(ivhd, softc); 424 if (status != 0) { 425 device_printf(dev, 426 "endpoint device parsing error=%d\n", status); 427 } 428 429 status = ivhd_print_cap(softc, ivhd); 430 if (status != 0) { 431 return (status); 432 } 433 434 status = amdvi_setup_hw(softc); 435 if (status != 0) { 436 device_printf(dev, "couldn't be initialised, error=%d\n", 437 status); 438 return (status); 439 } 440 441 return (0); 442 } 443 444 static int 445 ivhd_detach(device_t dev) 446 { 447 struct amdvi_softc *softc; 448 449 softc = device_get_softc(dev); 450 451 amdvi_teardown_hw(softc); 452 453 /* 454 * XXX: delete the device. 455 * don't allow detach, return EBUSY. 456 */ 457 return (0); 458 } 459 460 static int 461 ivhd_suspend(device_t dev) 462 { 463 464 return (0); 465 } 466 467 static int 468 ivhd_resume(device_t dev) 469 { 470 471 return (0); 472 } 473 474 static device_method_t ivhd_methods[] = { 475 DEVMETHOD(device_identify, ivhd_identify), 476 DEVMETHOD(device_probe, ivhd_probe), 477 DEVMETHOD(device_attach, ivhd_attach), 478 DEVMETHOD(device_detach, ivhd_detach), 479 DEVMETHOD(device_suspend, ivhd_suspend), 480 DEVMETHOD(device_resume, ivhd_resume), 481 DEVMETHOD_END 482 }; 483 484 static driver_t ivhd_driver = { 485 "ivhd", 486 ivhd_methods, 487 sizeof(struct amdvi_softc), 488 }; 489 490 static devclass_t ivhd_devclass; 491 492 /* 493 * Load this module at the end after PCI re-probing to configure interrupt. 494 */ 495 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0, 496 SI_ORDER_ANY); 497 MODULE_DEPEND(ivhd, acpi, 1, 1, 1); 498 MODULE_DEPEND(ivhd, pci, 1, 1, 1); 499