1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * Copyright (c) 2009, Intel Corporation. 27 * All rights reserved. 28 */ 29 30 /* 31 * Copyright 2023 Oxide Computer Company 32 */ 33 34 #include <sys/debug.h> 35 #include <sys/sysmacros.h> 36 #include <sys/types.h> 37 #include <sys/kmem.h> 38 #include <sys/sunddi.h> 39 #include <sys/list.h> 40 #include <sys/pci.h> 41 #include <sys/pci_cfgspace.h> 42 #include <sys/pci_impl.h> 43 #include <sys/sunndi.h> 44 #include <sys/ksynch.h> 45 #include <sys/cmn_err.h> 46 #include <sys/bootconf.h> 47 #include <sys/int_fmtio.h> 48 #include <sys/smbios.h> 49 #include <sys/apic.h> 50 #include <sys/acpi/acpi.h> 51 #include <sys/acpica.h> 52 #include <sys/immu.h> 53 #include <sys/smp_impldefs.h> 54 55 static void dmar_table_destroy(dmar_table_t *tbl); 56 57 /* 58 * internal global variables 59 */ 60 static char *dmar_raw; /* raw DMAR ACPI table */ 61 static dmar_table_t *dmar_table; /* converted form of DMAR table */ 62 63 /* 64 * global variables exported outside this file 65 */ 66 boolean_t dmar_print = B_FALSE; 67 kmutex_t ioapic_drhd_lock; 68 list_t ioapic_drhd_list; 69 70 /* ######################################################################### */ 71 72 /* 73 * helper functions to read the "raw" DMAR table 74 */ 75 76 static uint8_t 77 get_uint8(char *cp) 78 { 79 uint8_t val = *((uint8_t *)cp); 80 return (val); 81 } 82 83 static uint16_t 84 get_uint16(char *cp) 85 { 86 uint16_t val = *((uint16_t *)cp); 87 return (val); 88 } 89 90 static uint32_t 91 get_uint32(char *cp) 92 { 93 uint32_t val = *((uint32_t *)cp); 94 return (val); 95 } 96 97 static uint64_t 98 get_uint64(char *cp) 99 { 100 uint64_t val = *((uint64_t *)cp); 101 return (val); 102 } 103 104 static char * 105 get_str(char *cp, uint_t len) 106 { 107 char *str = kmem_alloc(len + 1, KM_SLEEP); 108 109 (void) strlcpy(str, cp, len + 1); 110 111 return (str); 112 } 113 114 static void 115 scope_list_free(list_t *scope_list) 116 { 117 scope_t *scope; 118 119 if (list_is_empty(scope_list)) { 120 list_destroy(scope_list); 121 return; 122 } 123 124 while ((scope = list_remove_head(scope_list)) != NULL) { 125 kmem_free(scope, sizeof (scope_t)); 126 } 127 128 ASSERT(list_is_empty(scope_list)); 129 list_destroy(scope_list); 130 } 131 132 static void 133 drhd_list_destroy(list_t *drhd_list) 134 { 135 drhd_t *drhd; 136 137 ASSERT(drhd_list); 138 139 if (list_is_empty(drhd_list)) { 140 list_destroy(drhd_list); 141 return; 142 } 143 144 while ((drhd = list_remove_head(drhd_list)) != NULL) { 145 scope_list_free(&(drhd->dr_scope_list)); 146 kmem_free(drhd, sizeof (drhd_t)); 147 } 148 149 ASSERT(list_is_empty(drhd_list)); 150 list_destroy(drhd_list); 151 } 152 153 static void 154 rmrr_list_destroy(list_t *rmrr_list) 155 { 156 rmrr_t *rmrr; 157 158 ASSERT(rmrr_list); 159 160 if (list_is_empty(rmrr_list)) { 161 list_destroy(rmrr_list); 162 return; 163 } 164 165 while ((rmrr = list_remove_head(rmrr_list)) != NULL) { 166 scope_list_free(&(rmrr->rm_scope_list)); 167 kmem_free(rmrr, sizeof (rmrr_t)); 168 } 169 170 ASSERT(list_is_empty(rmrr_list)); 171 list_destroy(rmrr_list); 172 } 173 174 /* 175 * parse_scope() 176 * parse a scope structure in the "raw" table 177 */ 178 static scope_t * 179 parse_scope(char *shead) 180 { 181 scope_t *scope; 182 char *phead; 183 int bus, dev, func; 184 uint8_t startbus; 185 uint8_t len; 186 int depth; 187 188 ASSERT(shead); 189 190 scope = kmem_zalloc(sizeof (scope_t), KM_SLEEP); 191 scope->scp_type = get_uint8(&shead[0]); 192 scope->scp_enumid = get_uint8(&shead[4]); 193 194 len = get_uint8(&shead[1]); 195 startbus = get_uint8(&shead[5]); 196 depth = (len - 6)/2; 197 ASSERT(depth >= 1); 198 199 phead = &shead[6]; 200 201 bus = startbus; 202 dev = get_uint8(phead++); 203 func = get_uint8(phead++); 204 205 for (depth--; depth > 0; depth--) { 206 bus = pci_getb_func(bus, dev, func, PCI_BCNF_SECBUS); 207 dev = get_uint8(phead++); 208 func = get_uint8(phead++); 209 } 210 211 ASSERT(bus >= 0 && bus < 256); 212 ASSERT(dev >= 0 && dev < 32); 213 ASSERT(func >= 0 && func < 8); 214 215 /* ok we got the device BDF */ 216 scope->scp_bus = bus; 217 scope->scp_dev = dev; 218 scope->scp_func = func; 219 220 return (scope); 221 } 222 223 224 /* setup the ioapic_drhd structure */ 225 static void 226 ioapic_drhd_setup(void) 227 { 228 mutex_init(&(ioapic_drhd_lock), NULL, MUTEX_DEFAULT, NULL); 229 230 mutex_enter(&(ioapic_drhd_lock)); 231 list_create(&(ioapic_drhd_list), sizeof (ioapic_drhd_t), 232 offsetof(ioapic_drhd_t, ioapic_node)); 233 mutex_exit(&(ioapic_drhd_lock)); 234 } 235 236 /* get ioapic source id for interrupt remapping */ 237 static void 238 ioapic_drhd_insert(scope_t *scope, drhd_t *drhd) 239 { 240 ioapic_drhd_t *idt; 241 242 idt = kmem_zalloc(sizeof (ioapic_drhd_t), KM_SLEEP); 243 idt->ioapic_ioapicid = scope->scp_enumid; 244 idt->ioapic_sid = ((scope->scp_bus << 8) | (scope->scp_dev << 3) | 245 (scope->scp_func)); 246 idt->ioapic_drhd = drhd; 247 248 mutex_enter(&ioapic_drhd_lock); 249 list_insert_tail(&ioapic_drhd_list, idt); 250 mutex_exit(&ioapic_drhd_lock); 251 } 252 253 static ioapic_drhd_t * 254 ioapic_drhd_lookup(int ioapicid) 255 { 256 ioapic_drhd_t *idt; 257 258 mutex_enter(&ioapic_drhd_lock); 259 idt = list_head(&ioapic_drhd_list); 260 for (; idt; idt = list_next(&ioapic_drhd_list, idt)) { 261 if (idt->ioapic_ioapicid == ioapicid) { 262 break; 263 } 264 } 265 mutex_exit(&ioapic_drhd_lock); 266 267 return (idt); 268 } 269 270 static void 271 ioapic_drhd_destroy(void) 272 { 273 ioapic_drhd_t *idt; 274 275 mutex_enter(&ioapic_drhd_lock); 276 while (idt = list_remove_head(&ioapic_drhd_list)) { 277 kmem_free(idt, sizeof (ioapic_drhd_t)); 278 } 279 list_destroy(&ioapic_drhd_list); 280 mutex_exit(&(ioapic_drhd_lock)); 281 282 mutex_destroy(&(ioapic_drhd_lock)); 283 } 284 285 /* 286 * parse_drhd() 287 * parse the drhd uints in dmar table 288 */ 289 static int 290 parse_drhd(char *uhead, dmar_table_t *tbl) 291 { 292 drhd_t *drhd; 293 int seg; 294 int len; 295 char *shead; 296 scope_t *scope; 297 298 ASSERT(uhead); 299 ASSERT(tbl); 300 ASSERT(get_uint16(&uhead[0]) == DMAR_DRHD); 301 302 seg = get_uint16(&uhead[6]); 303 if (seg < 0 || seg >= IMMU_MAXSEG) { 304 ddi_err(DER_WARN, NULL, "invalid segment# <%d>" 305 "in DRHD unit in ACPI DMAR table", seg); 306 return (DDI_FAILURE); 307 } 308 309 drhd = kmem_zalloc(sizeof (drhd_t), KM_SLEEP); 310 mutex_init(&(drhd->dr_lock), NULL, MUTEX_DEFAULT, NULL); 311 list_create(&(drhd->dr_scope_list), sizeof (scope_t), 312 offsetof(scope_t, scp_node)); 313 314 len = get_uint16(&uhead[2]); 315 drhd->dr_include_all = 316 (get_uint8(&uhead[4]) & DMAR_INCLUDE_ALL) ? B_TRUE : B_FALSE; 317 drhd->dr_seg = seg; 318 drhd->dr_regs = get_uint64(&uhead[8]); 319 320 /* 321 * parse each scope. 322 */ 323 shead = &uhead[16]; 324 while (shead < &uhead[len - 1]) { 325 scope = parse_scope(shead); 326 if (scope == NULL) { 327 return (DDI_FAILURE); 328 } 329 330 if (scope->scp_type == DMAR_IOAPIC) { 331 ioapic_drhd_insert(scope, drhd); 332 } 333 334 list_insert_tail(&(drhd->dr_scope_list), scope); 335 shead += get_uint8(&shead[1]); 336 } 337 338 list_insert_tail(&(tbl->tbl_drhd_list[drhd->dr_seg]), drhd); 339 340 return (DDI_SUCCESS); 341 } 342 343 /* 344 * parse_rmrr() 345 * parse the rmrr units in dmar table 346 */ 347 static int 348 parse_rmrr(char *uhead, dmar_table_t *tbl) 349 { 350 rmrr_t *rmrr; 351 int seg; 352 int len; 353 char *shead; 354 scope_t *scope; 355 356 ASSERT(uhead); 357 ASSERT(tbl); 358 ASSERT(get_uint16(&uhead[0]) == DMAR_RMRR); 359 360 seg = get_uint16(&uhead[6]); 361 if (seg < 0 || seg >= IMMU_MAXSEG) { 362 ddi_err(DER_WARN, NULL, "invalid segment# <%d>" 363 "in RMRR unit in ACPI DMAR table", seg); 364 return (DDI_FAILURE); 365 } 366 367 rmrr = kmem_zalloc(sizeof (rmrr_t), KM_SLEEP); 368 mutex_init(&(rmrr->rm_lock), NULL, MUTEX_DEFAULT, NULL); 369 list_create(&(rmrr->rm_scope_list), sizeof (scope_t), 370 offsetof(scope_t, scp_node)); 371 372 /* RMRR region is [base,limit] */ 373 len = get_uint16(&uhead[2]); 374 rmrr->rm_seg = get_uint16(&uhead[6]); 375 rmrr->rm_base = get_uint64(&uhead[8]); 376 rmrr->rm_limit = get_uint64(&uhead[16]); 377 378 if (rmrr->rm_base > rmrr->rm_limit) { 379 ddi_err(DER_WARN, NULL, "IMMU: BIOS bug detected: " 380 "RMRR: base (%lx) > limit (%lx)", 381 rmrr->rm_base, rmrr->rm_limit); 382 list_destroy(&(rmrr->rm_scope_list)); 383 mutex_destroy(&(rmrr->rm_lock)); 384 kmem_free(rmrr, sizeof (rmrr_t)); 385 return (DDI_SUCCESS); 386 } 387 388 /* 389 * parse each scope in RMRR 390 */ 391 shead = &uhead[24]; 392 while (shead < &uhead[len - 1]) { 393 scope = parse_scope(shead); 394 if (scope == NULL) { 395 return (DDI_FAILURE); 396 } 397 list_insert_tail(&(rmrr->rm_scope_list), scope); 398 shead += get_uint8(&shead[1]); 399 } 400 401 list_insert_tail(&(tbl->tbl_rmrr_list[rmrr->rm_seg]), rmrr); 402 403 return (DDI_SUCCESS); 404 } 405 406 #define TBL_OEM_ID_SZ (6) 407 #define TBL_OEM_TBLID_SZ (8) 408 409 /* 410 * parse the "raw" DMAR table and convert it 411 * into a useful form. 412 */ 413 static int 414 dmar_parse(dmar_table_t **tblpp, char *raw) 415 { 416 char *uhead; 417 dmar_table_t *tbl; 418 int i; 419 char *unmstr; 420 421 ASSERT(raw); 422 ASSERT(tblpp); 423 424 *tblpp = NULL; 425 426 /* 427 * do a sanity check. make sure the raw table 428 * has the right signature 429 */ 430 if (raw[0] != 'D' || raw[1] != 'M' || 431 raw[2] != 'A' || raw[3] != 'R') { 432 ddi_err(DER_WARN, NULL, "IOMMU ACPI " 433 "signature != \"DMAR\""); 434 return (DDI_FAILURE); 435 } 436 437 /* 438 * the platform has intel iommu, create processed ACPI struct 439 */ 440 tbl = kmem_zalloc(sizeof (dmar_table_t), KM_SLEEP); 441 mutex_init(&(tbl->tbl_lock), NULL, MUTEX_DEFAULT, NULL); 442 443 tbl->tbl_raw = raw; 444 445 /* 446 * Note we explicitly show offsets for clarity 447 */ 448 tbl->tbl_rawlen = get_uint32(&raw[4]); 449 450 /* XXX TO DO verify checksum of table */ 451 tbl->tbl_oem_id = get_str(&raw[10], TBL_OEM_ID_SZ); 452 tbl->tbl_oem_tblid = get_str(&raw[16], TBL_OEM_TBLID_SZ); 453 tbl->tbl_oem_rev = get_uint32(&raw[24]); 454 tbl->tbl_haw = get_uint8(&raw[36]) + 1; 455 tbl->tbl_intrmap = (get_uint8(&raw[37]) & DMAR_INTRMAP_SUPPORT) 456 ? B_TRUE : B_FALSE; 457 458 /* create lists for DRHD and RMRR */ 459 for (i = 0; i < IMMU_MAXSEG; i++) { 460 list_create(&(tbl->tbl_drhd_list[i]), sizeof (drhd_t), 461 offsetof(drhd_t, dr_node)); 462 list_create(&(tbl->tbl_rmrr_list[i]), sizeof (rmrr_t), 463 offsetof(rmrr_t, rm_node)); 464 } 465 466 ioapic_drhd_setup(); 467 468 /* 469 * parse each unit. Currently only DRHD and RMRR types 470 * are parsed. We ignore all other types of units. 471 */ 472 uhead = &raw[48]; 473 while (uhead < &raw[tbl->tbl_rawlen - 1]) { 474 unmstr = NULL; 475 switch (get_uint16(uhead)) { 476 case DMAR_DRHD: 477 if (parse_drhd(uhead, tbl) != DDI_SUCCESS) { 478 goto failed; 479 } 480 break; 481 case DMAR_RMRR: 482 if (parse_rmrr(uhead, tbl) != DDI_SUCCESS) { 483 goto failed; 484 } 485 break; 486 case DMAR_ATSR: 487 unmstr = "ATSR"; 488 break; 489 case DMAR_RHSA: 490 unmstr = "RHSA"; 491 break; 492 default: 493 unmstr = "unknown unity type"; 494 break; 495 } 496 if (unmstr) { 497 ddi_err(DER_NOTE, NULL, "DMAR ACPI table: " 498 "skipping unsupported unit type %s", unmstr); 499 } 500 uhead += get_uint16(&uhead[2]); 501 } 502 503 *tblpp = tbl; 504 return (DDI_SUCCESS); 505 506 failed: 507 dmar_table_destroy(tbl); 508 return (DDI_FAILURE); 509 } 510 511 static char * 512 scope_type(int devtype) 513 { 514 char *typestr; 515 516 switch (devtype) { 517 case DMAR_ENDPOINT: 518 typestr = "endpoint-device"; 519 break; 520 case DMAR_SUBTREE: 521 typestr = "subtree-device"; 522 break; 523 case DMAR_IOAPIC: 524 typestr = "IOAPIC"; 525 break; 526 case DMAR_HPET: 527 typestr = "HPET"; 528 break; 529 default: 530 typestr = "Unknown device"; 531 break; 532 } 533 534 return (typestr); 535 } 536 537 static void 538 print_scope_list(list_t *scope_list) 539 { 540 scope_t *scope; 541 542 if (list_is_empty(scope_list)) 543 return; 544 545 ddi_err(DER_CONT, NULL, "\tdevice list:\n"); 546 547 for (scope = list_head(scope_list); scope; 548 scope = list_next(scope_list, scope)) { 549 ddi_err(DER_CONT, NULL, "\t\ttype = %s\n", 550 scope_type(scope->scp_type)); 551 ddi_err(DER_CONT, NULL, "\n\t\tbus = %d\n", 552 scope->scp_bus); 553 ddi_err(DER_CONT, NULL, "\t\tdev = %d\n", 554 scope->scp_dev); 555 ddi_err(DER_CONT, NULL, "\t\tfunc = %d\n", 556 scope->scp_func); 557 } 558 } 559 560 static void 561 print_drhd_list(list_t *drhd_list) 562 { 563 drhd_t *drhd; 564 565 if (list_is_empty(drhd_list)) 566 return; 567 568 ddi_err(DER_CONT, NULL, "\ndrhd list:\n"); 569 570 for (drhd = list_head(drhd_list); drhd; 571 drhd = list_next(drhd_list, drhd)) { 572 573 ddi_err(DER_CONT, NULL, "\n\tsegment = %d\n", 574 drhd->dr_seg); 575 ddi_err(DER_CONT, NULL, "\treg_base = 0x%" PRIx64 "\n", 576 drhd->dr_regs); 577 ddi_err(DER_CONT, NULL, "\tinclude_all = %s\n", 578 drhd->dr_include_all == B_TRUE ? "TRUE" : "FALSE"); 579 ddi_err(DER_CONT, NULL, "\tdip = 0x%p\n", 580 (void *)drhd->dr_dip); 581 582 print_scope_list(&(drhd->dr_scope_list)); 583 } 584 } 585 586 587 static void 588 print_rmrr_list(list_t *rmrr_list) 589 { 590 rmrr_t *rmrr; 591 592 if (list_is_empty(rmrr_list)) 593 return; 594 595 ddi_err(DER_CONT, NULL, "\nrmrr list:\n"); 596 597 for (rmrr = list_head(rmrr_list); rmrr; 598 rmrr = list_next(rmrr_list, rmrr)) { 599 600 ddi_err(DER_CONT, NULL, "\n\tsegment = %d\n", 601 rmrr->rm_seg); 602 ddi_err(DER_CONT, NULL, "\tbase = 0x%lx\n", 603 rmrr->rm_base); 604 ddi_err(DER_CONT, NULL, "\tlimit = 0x%lx\n", 605 rmrr->rm_limit); 606 607 print_scope_list(&(rmrr->rm_scope_list)); 608 } 609 } 610 611 /* 612 * print DMAR table 613 */ 614 static void 615 dmar_table_print(dmar_table_t *tbl) 616 { 617 int i; 618 619 if (dmar_print == B_FALSE) { 620 return; 621 } 622 623 /* print the title */ 624 ddi_err(DER_CONT, NULL, "#### Start of dmar_table ####\n"); 625 ddi_err(DER_CONT, NULL, "\thaw = %d\n", tbl->tbl_haw); 626 ddi_err(DER_CONT, NULL, "\tintr_remap = %s\n", 627 tbl->tbl_intrmap == B_TRUE ? "<true>" : "<false>"); 628 629 /* print drhd list */ 630 for (i = 0; i < IMMU_MAXSEG; i++) { 631 print_drhd_list(&(tbl->tbl_drhd_list[i])); 632 } 633 634 635 /* print rmrr list */ 636 for (i = 0; i < IMMU_MAXSEG; i++) { 637 print_rmrr_list(&(tbl->tbl_rmrr_list[i])); 638 } 639 640 ddi_err(DER_CONT, NULL, "#### END of dmar_table ####\n"); 641 } 642 643 static void 644 drhd_devi_create(drhd_t *drhd, int unit) 645 { 646 struct ddi_parent_private_data *pdptr; 647 struct regspec reg; 648 dev_info_t *dip; 649 650 dip = ddi_add_child(root_devinfo, IMMU_UNIT_NAME, 651 DEVI_SID_NODEID, unit); 652 653 drhd->dr_dip = dip; 654 655 reg.regspec_bustype = 0; 656 reg.regspec_addr = drhd->dr_regs; 657 reg.regspec_size = IMMU_REGSZ; 658 659 /* 660 * update the reg properties 661 * 662 * reg property will be used for register 663 * set access 664 * 665 * refer to the bus_map of root nexus driver 666 * I/O or memory mapping: 667 * 668 * <bustype=0, addr=x, len=x>: memory 669 * <bustype=1, addr=x, len=x>: i/o 670 * <bustype>1, addr=0, len=x>: x86-compatibility i/o 671 */ 672 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, 673 dip, "reg", (int *)®, 674 sizeof (struct regspec) / sizeof (int)); 675 676 /* 677 * This is an artificially constructed dev_info, and we 678 * need to set a few more things to be able to use it 679 * for ddi_dma_alloc_handle/free_handle. 680 */ 681 ddi_set_driver(dip, ddi_get_driver(ddi_root_node())); 682 DEVI(dip)->devi_bus_dma_allochdl = 683 DEVI(ddi_get_driver((ddi_root_node()))); 684 685 pdptr = kmem_zalloc(sizeof (struct ddi_parent_private_data) 686 + sizeof (struct regspec), KM_SLEEP); 687 pdptr->par_nreg = 1; 688 pdptr->par_reg = (struct regspec *)(pdptr + 1); 689 pdptr->par_reg->regspec_bustype = 0; 690 pdptr->par_reg->regspec_addr = drhd->dr_regs; 691 pdptr->par_reg->regspec_size = IMMU_REGSZ; 692 ddi_set_parent_data(dip, pdptr); 693 } 694 695 /* 696 * dmar_devinfos_create() 697 * 698 * create the dev_info node in the device tree, 699 * the info node is a nuxus child of the root 700 * nexus 701 */ 702 static void 703 dmar_devinfos_create(dmar_table_t *tbl) 704 { 705 list_t *drhd_list; 706 drhd_t *drhd; 707 int i, unit; 708 709 for (i = 0; i < IMMU_MAXSEG; i++) { 710 711 drhd_list = &(tbl->tbl_drhd_list[i]); 712 713 if (list_is_empty(drhd_list)) 714 continue; 715 716 drhd = list_head(drhd_list); 717 for (unit = 0; drhd; 718 drhd = list_next(drhd_list, drhd), unit++) { 719 drhd_devi_create(drhd, unit); 720 } 721 } 722 } 723 724 static void 725 drhd_devi_destroy(drhd_t *drhd) 726 { 727 dev_info_t *dip; 728 729 dip = drhd->dr_dip; 730 ASSERT(dip); 731 732 ndi_devi_enter(root_devinfo); 733 if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != DDI_SUCCESS) { 734 ddi_err(DER_WARN, dip, "Failed to destroy"); 735 } 736 ndi_devi_exit(root_devinfo); 737 drhd->dr_dip = NULL; 738 } 739 740 /* 741 * dmar_devi_destroy() 742 * 743 * destroy dev_info nodes for all drhd units 744 */ 745 static void 746 dmar_devi_destroy(dmar_table_t *tbl) 747 { 748 drhd_t *drhd; 749 list_t *drhd_list; 750 int i; 751 752 for (i = 0; i < IMMU_MAXSEG; i++) { 753 drhd_list = &(tbl->tbl_drhd_list[i]); 754 if (list_is_empty(drhd_list)) 755 continue; 756 757 drhd = list_head(drhd_list); 758 for (; drhd; drhd = list_next(drhd_list, drhd)) { 759 drhd_devi_destroy(drhd); 760 } 761 } 762 } 763 764 static int 765 match_bdf(dev_info_t *ddip, void *arg) 766 { 767 immu_arg_t *imarg = (immu_arg_t *)arg; 768 immu_devi_t *immu_devi; 769 770 ASSERT(ddip); 771 ASSERT(imarg); 772 ASSERT(imarg->ima_seg == 0); 773 ASSERT(imarg->ima_bus >= 0); 774 ASSERT(imarg->ima_devfunc >= 0); 775 ASSERT(imarg->ima_ddip == NULL); 776 777 /* rdip can be NULL */ 778 779 mutex_enter(&(DEVI(ddip)->devi_lock)); 780 781 immu_devi = IMMU_DEVI(ddip); 782 ASSERT(immu_devi); 783 784 if (immu_devi->imd_seg == imarg->ima_seg && 785 immu_devi->imd_bus == imarg->ima_bus && 786 immu_devi->imd_devfunc == imarg->ima_devfunc) { 787 imarg->ima_ddip = ddip; 788 } 789 790 mutex_exit(&(DEVI(ddip)->devi_lock)); 791 792 return (imarg->ima_ddip ? DDI_WALK_TERMINATE : DDI_WALK_CONTINUE); 793 } 794 static void 795 dmar_table_destroy(dmar_table_t *tbl) 796 { 797 int i; 798 799 ASSERT(tbl); 800 801 /* destroy lists for DRHD and RMRR */ 802 for (i = 0; i < IMMU_MAXSEG; i++) { 803 rmrr_list_destroy(&(tbl->tbl_rmrr_list[i])); 804 drhd_list_destroy(&(tbl->tbl_drhd_list[i])); 805 } 806 807 /* free strings */ 808 kmem_free(tbl->tbl_oem_tblid, TBL_OEM_TBLID_SZ + 1); 809 kmem_free(tbl->tbl_oem_id, TBL_OEM_ID_SZ + 1); 810 tbl->tbl_raw = NULL; /* raw ACPI table doesn't have to be freed */ 811 mutex_destroy(&(tbl->tbl_lock)); 812 kmem_free(tbl, sizeof (dmar_table_t)); 813 } 814 815 /* 816 * ######################################################################### 817 * Functions exported by dmar.c 818 * This file deals with reading and processing the DMAR ACPI table 819 * ######################################################################### 820 */ 821 822 /* 823 * immu_dmar_setup() 824 * Check if the system has a DMAR ACPI table. If yes, the system 825 * has Intel IOMMU hardware 826 */ 827 int 828 immu_dmar_setup(void) 829 { 830 if (AcpiGetTable("DMAR", 1, (ACPI_TABLE_HEADER **)&dmar_raw) != AE_OK) { 831 ddi_err(DER_LOG, NULL, 832 "No DMAR ACPI table. No Intel IOMMU present\n"); 833 dmar_raw = NULL; 834 return (DDI_FAILURE); 835 } 836 ASSERT(dmar_raw); 837 return (DDI_SUCCESS); 838 } 839 840 /* 841 * immu_dmar_parse() 842 * Called by immu.c to parse and convert "raw" ACPI DMAR table 843 */ 844 int 845 immu_dmar_parse(void) 846 { 847 dmar_table_t *tbl = NULL; 848 849 /* we should already have found the "raw" table */ 850 ASSERT(dmar_raw); 851 852 ddi_err(DER_CONT, NULL, "?Processing DMAR ACPI table\n"); 853 854 dmar_table = NULL; 855 856 /* 857 * parse DMAR ACPI table 858 */ 859 if (dmar_parse(&tbl, dmar_raw) != DDI_SUCCESS) { 860 ASSERT(tbl == NULL); 861 return (DDI_FAILURE); 862 } 863 864 ASSERT(tbl); 865 866 /* 867 * create one devinfo for every drhd unit 868 * in the DMAR table 869 */ 870 dmar_devinfos_create(tbl); 871 872 /* 873 * print the dmar table if the debug option is set 874 */ 875 dmar_table_print(tbl); 876 877 dmar_table = tbl; 878 879 return (DDI_SUCCESS); 880 } 881 882 void 883 immu_dmar_startup(void) 884 { 885 /* nothing to do */ 886 } 887 888 void 889 immu_dmar_shutdown(void) 890 { 891 /* nothing to do */ 892 } 893 894 void 895 immu_dmar_destroy(void) 896 { 897 dmar_devi_destroy(dmar_table); 898 dmar_table_destroy(dmar_table); 899 ioapic_drhd_destroy(); 900 dmar_table = NULL; 901 dmar_raw = NULL; 902 } 903 904 boolean_t 905 immu_dmar_blacklisted(char **strptr, uint_t nstrs) 906 { 907 dmar_table_t *tbl = dmar_table; 908 int i; 909 char oem_rev[IMMU_MAXNAMELEN]; 910 911 ASSERT(tbl); 912 913 ASSERT((strptr == NULL) ^ (nstrs != 0)); 914 915 /* 916 * Must be a minimum of 4 917 */ 918 if (nstrs < 4) { 919 return (B_FALSE); 920 } 921 922 ddi_err(DER_CONT, NULL, "?System DMAR ACPI table information:\n"); 923 ddi_err(DER_CONT, NULL, "?OEM-ID = <%s>\n", tbl->tbl_oem_id); 924 ddi_err(DER_CONT, NULL, "?Table-ID = <%s>\n", tbl->tbl_oem_tblid); 925 (void) snprintf(oem_rev, sizeof (oem_rev), "%d", tbl->tbl_oem_rev); 926 ddi_err(DER_CONT, NULL, "?Revision = <%s>\n", oem_rev); 927 928 for (i = 0; nstrs - i >= 4; i++) { 929 if (strcmp(*strptr++, "DMAR") == 0) { 930 if (strcmp(*strptr++, tbl->tbl_oem_id) == 0 && 931 (*strptr[0] == '\0' || 932 strcmp(*strptr++, tbl->tbl_oem_tblid) == 0) && 933 (*strptr[0] == '\0' || 934 strcmp(*strptr++, oem_rev) == 0)) { 935 return (B_TRUE); 936 } 937 i += 3; /* for loops adds 1 as well, so only 3 here */ 938 } 939 } 940 return (B_FALSE); 941 } 942 943 void 944 immu_dmar_rmrr_map(void) 945 { 946 int seg; 947 dev_info_t *rdip; 948 scope_t *scope; 949 rmrr_t *rmrr; 950 dmar_table_t *tbl; 951 952 ASSERT(dmar_table); 953 954 tbl = dmar_table; 955 956 /* called during boot, when kernel is single threaded. No lock */ 957 958 /* 959 * for each segment, walk the rmrr list looking for an exact match 960 */ 961 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 962 rmrr = list_head(&(tbl->tbl_rmrr_list)[seg]); 963 for (; rmrr; rmrr = list_next(&(tbl->tbl_rmrr_list)[seg], 964 rmrr)) { 965 966 /* 967 * try to match BDF *exactly* to a device scope. 968 */ 969 scope = list_head(&(rmrr->rm_scope_list)); 970 for (; scope; 971 scope = list_next(&(rmrr->rm_scope_list), scope)) { 972 immu_arg_t imarg = {0}; 973 memrng_t mrng = {0}; 974 975 /* PCI endpoint devices only */ 976 if (scope->scp_type != DMAR_ENDPOINT) 977 continue; 978 979 imarg.ima_seg = seg; 980 imarg.ima_bus = scope->scp_bus; 981 imarg.ima_devfunc = 982 IMMU_PCI_DEVFUNC(scope->scp_dev, 983 scope->scp_func); 984 imarg.ima_ddip = NULL; 985 imarg.ima_rdip = NULL; 986 987 ASSERT(root_devinfo); 988 /* XXX should be optimized */ 989 ndi_devi_enter(root_devinfo); 990 ddi_walk_devs(ddi_get_child(root_devinfo), 991 match_bdf, &imarg); 992 ndi_devi_exit(root_devinfo); 993 994 if (imarg.ima_ddip == NULL) { 995 ddi_err(DER_WARN, NULL, 996 "No dip found for " 997 "bus=0x%x, dev=0x%x, func= 0x%x", 998 scope->scp_bus, scope->scp_dev, 999 scope->scp_func); 1000 continue; 1001 } 1002 1003 rdip = imarg.ima_ddip; 1004 /* 1005 * This address must be in the BIOS reserved 1006 * map 1007 */ 1008 if (!address_in_memlist(bios_rsvd, 1009 (uint64_t)rmrr->rm_base, rmrr->rm_limit - 1010 rmrr->rm_base + 1)) { 1011 ddi_err(DER_WARN, rdip, "RMRR range " 1012 " [0x%" PRIx64 " - 0x%" PRIx64 "]" 1013 " not in BIOS reserved map", 1014 rmrr->rm_base, rmrr->rm_limit); 1015 } 1016 1017 /* XXX could be more efficient */ 1018 memlist_read_lock(); 1019 if (address_in_memlist(phys_install, 1020 (uint64_t)rmrr->rm_base, rmrr->rm_limit - 1021 rmrr->rm_base + 1)) { 1022 ddi_err(DER_WARN, rdip, "RMRR range " 1023 " [0x%" PRIx64 " - 0x%" PRIx64 "]" 1024 " is in physinstall map", 1025 rmrr->rm_base, rmrr->rm_limit); 1026 } 1027 memlist_read_unlock(); 1028 1029 (void) immu_dvma_device_setup(rdip, 0); 1030 1031 ddi_err(DER_LOG, rdip, 1032 "IMMU: Mapping RMRR range " 1033 "[0x%" PRIx64 " - 0x%"PRIx64 "]", 1034 rmrr->rm_base, rmrr->rm_limit); 1035 1036 mrng.mrng_start = 1037 IMMU_ROUNDOWN((uintptr_t)rmrr->rm_base); 1038 mrng.mrng_npages = 1039 IMMU_ROUNDUP((uintptr_t)rmrr->rm_limit - 1040 (uintptr_t)rmrr->rm_base + 1) / 1041 IMMU_PAGESIZE; 1042 1043 (void) immu_map_memrange(rdip, &mrng); 1044 } 1045 } 1046 } 1047 1048 } 1049 1050 immu_t * 1051 immu_dmar_get_immu(dev_info_t *rdip) 1052 { 1053 int seg; 1054 int tlevel; 1055 int level; 1056 drhd_t *drhd; 1057 drhd_t *tdrhd; 1058 scope_t *scope; 1059 dmar_table_t *tbl; 1060 1061 ASSERT(dmar_table); 1062 1063 tbl = dmar_table; 1064 1065 mutex_enter(&(tbl->tbl_lock)); 1066 1067 /* 1068 * for each segment, walk the drhd list looking for an exact match 1069 */ 1070 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 1071 drhd = list_head(&(tbl->tbl_drhd_list)[seg]); 1072 for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg], 1073 drhd)) { 1074 1075 /* 1076 * we are currently searching for exact matches so 1077 * skip "include all" (catchall) and subtree matches 1078 */ 1079 if (drhd->dr_include_all == B_TRUE) 1080 continue; 1081 1082 /* 1083 * try to match BDF *exactly* to a device scope. 1084 */ 1085 scope = list_head(&(drhd->dr_scope_list)); 1086 for (; scope; 1087 scope = list_next(&(drhd->dr_scope_list), scope)) { 1088 immu_arg_t imarg = {0}; 1089 1090 /* PCI endpoint devices only */ 1091 if (scope->scp_type != DMAR_ENDPOINT) 1092 continue; 1093 1094 imarg.ima_seg = seg; 1095 imarg.ima_bus = scope->scp_bus; 1096 imarg.ima_devfunc = 1097 IMMU_PCI_DEVFUNC(scope->scp_dev, 1098 scope->scp_func); 1099 imarg.ima_ddip = NULL; 1100 imarg.ima_rdip = rdip; 1101 level = 0; 1102 if (immu_walk_ancestor(rdip, NULL, match_bdf, 1103 &imarg, &level, IMMU_FLAGS_DONTPASS) 1104 != DDI_SUCCESS) { 1105 /* skip - nothing else we can do */ 1106 continue; 1107 } 1108 1109 /* Should have walked only 1 level i.e. rdip */ 1110 ASSERT(level == 1); 1111 1112 if (imarg.ima_ddip) { 1113 ASSERT(imarg.ima_ddip == rdip); 1114 goto found; 1115 } 1116 } 1117 } 1118 } 1119 1120 /* 1121 * walk the drhd list looking for subtree match 1122 * i.e. is the device a descendant of a devscope BDF. 1123 * We want the lowest subtree. 1124 */ 1125 tdrhd = NULL; 1126 tlevel = 0; 1127 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 1128 drhd = list_head(&(tbl->tbl_drhd_list)[seg]); 1129 for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg], 1130 drhd)) { 1131 1132 /* looking for subtree match */ 1133 if (drhd->dr_include_all == B_TRUE) 1134 continue; 1135 1136 /* 1137 * try to match the device scope 1138 */ 1139 scope = list_head(&(drhd->dr_scope_list)); 1140 for (; scope; 1141 scope = list_next(&(drhd->dr_scope_list), scope)) { 1142 immu_arg_t imarg = {0}; 1143 1144 /* PCI subtree only */ 1145 if (scope->scp_type != DMAR_SUBTREE) 1146 continue; 1147 1148 imarg.ima_seg = seg; 1149 imarg.ima_bus = scope->scp_bus; 1150 imarg.ima_devfunc = 1151 IMMU_PCI_DEVFUNC(scope->scp_dev, 1152 scope->scp_func); 1153 1154 imarg.ima_ddip = NULL; 1155 imarg.ima_rdip = rdip; 1156 level = 0; 1157 if (immu_walk_ancestor(rdip, NULL, match_bdf, 1158 &imarg, &level, 0) != DDI_SUCCESS) { 1159 /* skip - nothing else we can do */ 1160 continue; 1161 } 1162 1163 /* should have walked 1 level i.e. rdip */ 1164 ASSERT(level > 0); 1165 1166 /* look for lowest ancestor matching drhd */ 1167 if (imarg.ima_ddip && (tdrhd == NULL || 1168 level < tlevel)) { 1169 tdrhd = drhd; 1170 tlevel = level; 1171 } 1172 } 1173 } 1174 } 1175 1176 if ((drhd = tdrhd) != NULL) { 1177 goto found; 1178 } 1179 1180 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 1181 drhd = list_head(&(tbl->tbl_drhd_list[seg])); 1182 for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg], 1183 drhd)) { 1184 /* Look for include all */ 1185 if (drhd->dr_include_all == B_TRUE) { 1186 break; 1187 } 1188 } 1189 } 1190 1191 /*FALLTHRU*/ 1192 1193 found: 1194 mutex_exit(&(tbl->tbl_lock)); 1195 1196 /* 1197 * No drhd (dmar unit) found for this device in the ACPI DMAR tables. 1198 * This may happen with buggy versions of BIOSes. Just warn instead 1199 * of panic as we don't want whole system to go down because of one 1200 * device. 1201 */ 1202 if (drhd == NULL) { 1203 ddi_err(DER_WARN, rdip, "can't find Intel IOMMU unit for " 1204 "device in ACPI DMAR table."); 1205 return (NULL); 1206 } 1207 1208 return (drhd->dr_immu); 1209 } 1210 1211 dev_info_t * 1212 immu_dmar_unit_dip(void *dmar_unit) 1213 { 1214 drhd_t *drhd = (drhd_t *)dmar_unit; 1215 return (drhd->dr_dip); 1216 } 1217 1218 void * 1219 immu_dmar_walk_units(int seg, void *dmar_unit) 1220 { 1221 list_t *drhd_list; 1222 drhd_t *drhd = (drhd_t *)dmar_unit; 1223 1224 drhd_list = &(dmar_table->tbl_drhd_list[seg]); 1225 1226 if (drhd == NULL) { 1227 return ((void *)list_head(drhd_list)); 1228 } else { 1229 return ((void *)list_next(drhd_list, drhd)); 1230 } 1231 } 1232 1233 void 1234 immu_dmar_set_immu(void *dmar_unit, immu_t *immu) 1235 { 1236 drhd_t *drhd = (drhd_t *)dmar_unit; 1237 1238 ASSERT(drhd); 1239 ASSERT(immu); 1240 1241 drhd->dr_immu = immu; 1242 } 1243 1244 boolean_t 1245 immu_dmar_intrmap_supported(void) 1246 { 1247 ASSERT(dmar_table); 1248 return (dmar_table->tbl_intrmap); 1249 } 1250 1251 /* for a given ioapicid, find the source id and immu */ 1252 uint16_t 1253 immu_dmar_ioapic_sid(int ioapic_ix) 1254 { 1255 ioapic_drhd_t *idt; 1256 1257 idt = ioapic_drhd_lookup(psm_get_ioapicid(ioapic_ix)); 1258 if (idt == NULL) { 1259 ddi_err(DER_PANIC, NULL, "cannot determine source-id for " 1260 "IOAPIC (index = %d)", ioapic_ix); 1261 /*NOTREACHED*/ 1262 } 1263 1264 return (idt->ioapic_sid); 1265 } 1266 1267 /* for a given ioapicid, find the source id and immu */ 1268 immu_t * 1269 immu_dmar_ioapic_immu(int ioapic_ix) 1270 { 1271 ioapic_drhd_t *idt; 1272 1273 idt = ioapic_drhd_lookup(psm_get_ioapicid(ioapic_ix)); 1274 if (idt) { 1275 return (idt->ioapic_drhd ? idt->ioapic_drhd->dr_immu : NULL); 1276 } 1277 return (NULL); 1278 } 1279