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