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 * Portions Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Copyright (c) 2009, Intel Corporation. 28 * All rights reserved. 29 */ 30 31 32 #include <sys/debug.h> 33 #include <sys/sysmacros.h> 34 #include <sys/types.h> 35 #include <sys/kmem.h> 36 #include <sys/sunddi.h> 37 #include <sys/list.h> 38 #include <sys/pci.h> 39 #include <sys/pci_cfgspace.h> 40 #include <sys/pci_impl.h> 41 #include <sys/sunndi.h> 42 #include <sys/ksynch.h> 43 #include <sys/cmn_err.h> 44 #include <sys/bootconf.h> 45 #include <sys/int_fmtio.h> 46 #include <sys/smbios.h> 47 #include <sys/acpi/acpi.h> 48 #include <sys/acpica.h> 49 #include <sys/iommulib.h> 50 #include <sys/immu.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, char *name) 642 { 643 struct ddi_parent_private_data *pdptr; 644 struct regspec reg; 645 dev_info_t *dip; 646 647 ndi_devi_alloc_sleep(root_devinfo, name, 648 DEVI_SID_NODEID, &dip); 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 pdptr = kmem_zalloc(sizeof (struct ddi_parent_private_data) 675 + sizeof (struct regspec), KM_SLEEP); 676 pdptr->par_nreg = 1; 677 pdptr->par_reg = (struct regspec *)(pdptr + 1); 678 pdptr->par_reg->regspec_bustype = 0; 679 pdptr->par_reg->regspec_addr = drhd->dr_regs; 680 pdptr->par_reg->regspec_size = IMMU_REGSZ; 681 ddi_set_parent_data(dip, pdptr); 682 } 683 684 /* 685 * dmar_devinfos_create() 686 * 687 * create the dev_info node in the device tree, 688 * the info node is a nuxus child of the root 689 * nexus 690 */ 691 static void 692 dmar_devinfos_create(dmar_table_t *tbl) 693 { 694 list_t *drhd_list; 695 drhd_t *drhd; 696 char name[IMMU_MAXNAMELEN]; 697 int i, unit; 698 699 for (i = 0; i < IMMU_MAXSEG; i++) { 700 701 drhd_list = &(tbl->tbl_drhd_list[i]); 702 703 if (list_is_empty(drhd_list)) 704 continue; 705 706 drhd = list_head(drhd_list); 707 for (unit = 0; drhd; 708 drhd = list_next(drhd_list, drhd), unit++) { 709 (void) snprintf(name, sizeof (name), 710 "drhd%d,%d", i, unit); 711 drhd_devi_create(drhd, name); 712 } 713 } 714 } 715 716 static void 717 drhd_devi_destroy(drhd_t *drhd) 718 { 719 dev_info_t *dip; 720 int count; 721 722 dip = drhd->dr_dip; 723 ASSERT(dip); 724 725 ndi_devi_enter(root_devinfo, &count); 726 if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != DDI_SUCCESS) { 727 ddi_err(DER_WARN, dip, "Failed to destroy"); 728 } 729 ndi_devi_exit(root_devinfo, count); 730 drhd->dr_dip = NULL; 731 } 732 733 /* 734 * dmar_devi_destroy() 735 * 736 * destroy dev_info nodes for all drhd units 737 */ 738 static void 739 dmar_devi_destroy(dmar_table_t *tbl) 740 { 741 drhd_t *drhd; 742 list_t *drhd_list; 743 int i; 744 745 for (i = 0; i < IMMU_MAXSEG; i++) { 746 drhd_list = &(tbl->tbl_drhd_list[i]); 747 if (list_is_empty(drhd_list)) 748 continue; 749 750 drhd = list_head(drhd_list); 751 for (; drhd; drhd = list_next(drhd_list, drhd)) { 752 drhd_devi_destroy(drhd); 753 } 754 } 755 } 756 757 static int 758 match_bdf(dev_info_t *ddip, void *arg) 759 { 760 immu_arg_t *imarg = (immu_arg_t *)arg; 761 immu_devi_t *immu_devi; 762 763 ASSERT(ddip); 764 ASSERT(imarg); 765 ASSERT(imarg->ima_seg == 0); 766 ASSERT(imarg->ima_bus >= 0); 767 ASSERT(imarg->ima_devfunc >= 0); 768 ASSERT(imarg->ima_ddip == NULL); 769 770 /* rdip can be NULL */ 771 772 mutex_enter(&(DEVI(ddip)->devi_lock)); 773 774 immu_devi = IMMU_DEVI(ddip); 775 ASSERT(immu_devi); 776 777 if (immu_devi->imd_seg == imarg->ima_seg && 778 immu_devi->imd_bus == imarg->ima_bus && 779 immu_devi->imd_devfunc == imarg->ima_devfunc) { 780 imarg->ima_ddip = ddip; 781 } 782 783 mutex_exit(&(DEVI(ddip)->devi_lock)); 784 785 return (imarg->ima_ddip ? DDI_WALK_TERMINATE : DDI_WALK_CONTINUE); 786 } 787 static void 788 dmar_table_destroy(dmar_table_t *tbl) 789 { 790 int i; 791 792 ASSERT(tbl); 793 794 /* destroy lists for DRHD and RMRR */ 795 for (i = 0; i < IMMU_MAXSEG; i++) { 796 rmrr_list_destroy(&(tbl->tbl_rmrr_list[i])); 797 drhd_list_destroy(&(tbl->tbl_drhd_list[i])); 798 } 799 800 /* free strings */ 801 kmem_free(tbl->tbl_oem_tblid, TBL_OEM_ID_SZ + 1); 802 kmem_free(tbl->tbl_oem_id, TBL_OEM_TBLID_SZ + 1); 803 tbl->tbl_raw = NULL; /* raw ACPI table doesn't have to be freed */ 804 mutex_destroy(&(tbl->tbl_lock)); 805 kmem_free(tbl, sizeof (dmar_table_t)); 806 } 807 808 /* 809 * ######################################################################### 810 * Functions exported by dmar.c 811 * This file deals with reading and processing the DMAR ACPI table 812 * ######################################################################### 813 */ 814 815 /* 816 * immu_dmar_setup() 817 * Check if the system has a DMAR ACPI table. If yes, the system 818 * has Intel IOMMU hardware 819 */ 820 int 821 immu_dmar_setup(void) 822 { 823 if (AcpiGetTable("DMAR", 1, (ACPI_TABLE_HEADER **)&dmar_raw) != AE_OK) { 824 ddi_err(DER_LOG, NULL, 825 "No DMAR ACPI table. No Intel IOMMU present\n"); 826 dmar_raw = NULL; 827 return (DDI_FAILURE); 828 } 829 ASSERT(dmar_raw); 830 return (DDI_SUCCESS); 831 } 832 833 /* 834 * immu_dmar_parse() 835 * Called by immu.c to parse and convert "raw" ACPI DMAR table 836 */ 837 int 838 immu_dmar_parse(void) 839 { 840 dmar_table_t *tbl = NULL; 841 842 /* we should already have found the "raw" table */ 843 ASSERT(dmar_raw); 844 845 ddi_err(DER_CONT, NULL, "?Processing DMAR ACPI table\n"); 846 847 dmar_table = NULL; 848 849 /* 850 * parse DMAR ACPI table 851 */ 852 if (dmar_parse(&tbl, dmar_raw) != DDI_SUCCESS) { 853 ASSERT(tbl == NULL); 854 return (DDI_FAILURE); 855 } 856 857 ASSERT(tbl); 858 859 /* 860 * create one devinfo for every drhd unit 861 * in the DMAR table 862 */ 863 dmar_devinfos_create(tbl); 864 865 /* 866 * print the dmar table if the debug option is set 867 */ 868 dmar_table_print(tbl); 869 870 dmar_table = tbl; 871 872 return (DDI_SUCCESS); 873 } 874 875 void 876 immu_dmar_startup(void) 877 { 878 /* nothing to do */ 879 } 880 881 void 882 immu_dmar_shutdown(void) 883 { 884 /* nothing to do */ 885 } 886 887 void 888 immu_dmar_destroy(void) 889 { 890 dmar_devi_destroy(dmar_table); 891 dmar_table_destroy(dmar_table); 892 ioapic_drhd_destroy(); 893 dmar_table = NULL; 894 dmar_raw = NULL; 895 } 896 897 boolean_t 898 immu_dmar_blacklisted(char **strptr, uint_t nstrs) 899 { 900 dmar_table_t *tbl = dmar_table; 901 int i; 902 char oem_rev[IMMU_MAXNAMELEN]; 903 904 ASSERT(tbl); 905 906 ASSERT((strptr == NULL) ^ (nstrs != 0)); 907 908 /* 909 * Must be a minimum of 4 910 */ 911 if (nstrs < 4) { 912 return (B_FALSE); 913 } 914 915 ddi_err(DER_CONT, NULL, "?System DMAR ACPI table information:\n"); 916 ddi_err(DER_CONT, NULL, "?OEM-ID = <%s>\n", tbl->tbl_oem_id); 917 ddi_err(DER_CONT, NULL, "?Table-ID = <%s>\n", tbl->tbl_oem_tblid); 918 (void) snprintf(oem_rev, sizeof (oem_rev), "%d", tbl->tbl_oem_rev); 919 ddi_err(DER_CONT, NULL, "?Revision = <%s>\n", oem_rev); 920 921 for (i = 0; nstrs - i >= 4; i++) { 922 if (strcmp(*strptr++, "DMAR") == 0) { 923 if (strcmp(*strptr++, tbl->tbl_oem_id) == 0 && 924 ((char *)strptr == '\0' || 925 strcmp(*strptr++, tbl->tbl_oem_tblid) == 0) && 926 ((char *)strptr == '\0' || 927 strcmp(*strptr++, oem_rev) == 0)) { 928 return (B_TRUE); 929 } 930 i += 3; /* for loops adds 1 as well, so only 3 here */ 931 } 932 } 933 return (B_FALSE); 934 } 935 936 void 937 immu_dmar_rmrr_map(void) 938 { 939 int seg; 940 int e; 941 int count; 942 dev_info_t *rdip; 943 scope_t *scope; 944 rmrr_t *rmrr; 945 dmar_table_t *tbl; 946 947 ASSERT(dmar_table); 948 949 tbl = dmar_table; 950 951 /* called during boot, when kernel is single threaded. No lock */ 952 953 /* 954 * for each segment, walk the rmrr list looking for an exact match 955 */ 956 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 957 rmrr = list_head(&(tbl->tbl_rmrr_list)[seg]); 958 for (; rmrr; rmrr = list_next(&(tbl->tbl_rmrr_list)[seg], 959 rmrr)) { 960 961 /* 962 * try to match BDF *exactly* to a device scope. 963 */ 964 scope = list_head(&(rmrr->rm_scope_list)); 965 for (; scope; 966 scope = list_next(&(rmrr->rm_scope_list), scope)) { 967 immu_arg_t imarg = {0}; 968 memrng_t mrng = {0}; 969 970 /* PCI endpoint devices only */ 971 if (scope->scp_type != DMAR_ENDPOINT) 972 continue; 973 974 imarg.ima_seg = seg; 975 imarg.ima_bus = scope->scp_bus; 976 imarg.ima_devfunc = 977 IMMU_PCI_DEVFUNC(scope->scp_dev, 978 scope->scp_func); 979 imarg.ima_ddip = NULL; 980 imarg.ima_rdip = NULL; 981 982 ASSERT(root_devinfo); 983 /* XXX should be optimized */ 984 ndi_devi_enter(root_devinfo, &count); 985 ddi_walk_devs(ddi_get_child(root_devinfo), 986 match_bdf, &imarg); 987 ndi_devi_exit(root_devinfo, count); 988 989 if (imarg.ima_ddip == NULL) { 990 ddi_err(DER_WARN, NULL, 991 "No dip found for " 992 "bus=0x%x, dev=0x%x, func= 0x%x", 993 scope->scp_bus, scope->scp_dev, 994 scope->scp_func); 995 continue; 996 } 997 998 rdip = imarg.ima_ddip; 999 /* 1000 * This address must be in the BIOS reserved 1001 * map 1002 */ 1003 if (!address_in_memlist(bios_rsvd, 1004 (uint64_t)rmrr->rm_base, rmrr->rm_limit - 1005 rmrr->rm_base + 1)) { 1006 ddi_err(DER_WARN, rdip, "RMRR range " 1007 " [0x%" PRIx64 " - 0x%" PRIx64 "]" 1008 " not in BIOS reserved map", 1009 rmrr->rm_base, rmrr->rm_limit); 1010 } 1011 1012 /* XXX could be more efficient */ 1013 memlist_read_lock(); 1014 if (address_in_memlist(phys_install, 1015 (uint64_t)rmrr->rm_base, rmrr->rm_limit - 1016 rmrr->rm_base + 1)) { 1017 ddi_err(DER_WARN, rdip, "RMRR range " 1018 " [0x%" PRIx64 " - 0x%" PRIx64 "]" 1019 " is in physinstall map", 1020 rmrr->rm_base, rmrr->rm_limit); 1021 } 1022 memlist_read_unlock(); 1023 1024 1025 ddi_err(DER_LOG, rdip, 1026 "IMMU: Mapping RMRR range " 1027 "[0x%" PRIx64 " - 0x%"PRIx64 "]", 1028 rmrr->rm_base, rmrr->rm_limit); 1029 1030 mrng.mrng_start = 1031 IMMU_ROUNDOWN((uintptr_t)rmrr->rm_base); 1032 mrng.mrng_npages = 1033 IMMU_ROUNDUP((uintptr_t)rmrr->rm_limit - 1034 (uintptr_t)rmrr->rm_base + 1) / 1035 IMMU_PAGESIZE; 1036 e = immu_dvma_map(NULL, NULL, &mrng, 0, rdip, 1037 IMMU_FLAGS_READ | IMMU_FLAGS_WRITE | 1038 IMMU_FLAGS_MEMRNG); 1039 /* 1040 * dip may have unity domain or xlate domain 1041 * If the former, PHYSICAL is returned else 1042 * MAPPED is returned. 1043 */ 1044 ASSERT(e == DDI_DMA_MAPPED || 1045 e == DDI_DMA_USE_PHYSICAL); 1046 } 1047 } 1048 } 1049 1050 } 1051 1052 immu_t * 1053 immu_dmar_get_immu(dev_info_t *rdip) 1054 { 1055 int seg; 1056 int tlevel; 1057 int level; 1058 drhd_t *drhd; 1059 drhd_t *tdrhd; 1060 scope_t *scope; 1061 dmar_table_t *tbl; 1062 1063 ASSERT(dmar_table); 1064 1065 tbl = dmar_table; 1066 1067 mutex_enter(&(tbl->tbl_lock)); 1068 1069 /* 1070 * for each segment, walk the drhd list looking for an exact match 1071 */ 1072 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 1073 drhd = list_head(&(tbl->tbl_drhd_list)[seg]); 1074 for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg], 1075 drhd)) { 1076 1077 /* 1078 * we are currently searching for exact matches so 1079 * skip "include all" (catchall) and subtree matches 1080 */ 1081 if (drhd->dr_include_all == B_TRUE) 1082 continue; 1083 1084 /* 1085 * try to match BDF *exactly* to a device scope. 1086 */ 1087 scope = list_head(&(drhd->dr_scope_list)); 1088 for (; scope; 1089 scope = list_next(&(drhd->dr_scope_list), scope)) { 1090 immu_arg_t imarg = {0}; 1091 1092 /* PCI endpoint devices only */ 1093 if (scope->scp_type != DMAR_ENDPOINT) 1094 continue; 1095 1096 imarg.ima_seg = seg; 1097 imarg.ima_bus = scope->scp_bus; 1098 imarg.ima_devfunc = 1099 IMMU_PCI_DEVFUNC(scope->scp_dev, 1100 scope->scp_func); 1101 imarg.ima_ddip = NULL; 1102 imarg.ima_rdip = rdip; 1103 level = 0; 1104 if (immu_walk_ancestor(rdip, NULL, match_bdf, 1105 &imarg, &level, IMMU_FLAGS_DONTPASS) 1106 != DDI_SUCCESS) { 1107 /* skip - nothing else we can do */ 1108 continue; 1109 } 1110 1111 /* Should have walked only 1 level i.e. rdip */ 1112 ASSERT(level == 1); 1113 1114 if (imarg.ima_ddip) { 1115 ASSERT(imarg.ima_ddip == rdip); 1116 goto found; 1117 } 1118 } 1119 } 1120 } 1121 1122 /* 1123 * walk the drhd list looking for subtree match 1124 * i.e. is the device a descendant of a devscope BDF. 1125 * We want the lowest subtree. 1126 */ 1127 tdrhd = NULL; 1128 tlevel = 0; 1129 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 1130 drhd = list_head(&(tbl->tbl_drhd_list)[seg]); 1131 for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg], 1132 drhd)) { 1133 1134 /* looking for subtree match */ 1135 if (drhd->dr_include_all == B_TRUE) 1136 continue; 1137 1138 /* 1139 * try to match the device scope 1140 */ 1141 scope = list_head(&(drhd->dr_scope_list)); 1142 for (; scope; 1143 scope = list_next(&(drhd->dr_scope_list), scope)) { 1144 immu_arg_t imarg = {0}; 1145 1146 /* PCI subtree only */ 1147 if (scope->scp_type != DMAR_SUBTREE) 1148 continue; 1149 1150 imarg.ima_seg = seg; 1151 imarg.ima_bus = scope->scp_bus; 1152 imarg.ima_devfunc = 1153 IMMU_PCI_DEVFUNC(scope->scp_dev, 1154 scope->scp_func); 1155 1156 imarg.ima_ddip = NULL; 1157 imarg.ima_rdip = rdip; 1158 level = 0; 1159 if (immu_walk_ancestor(rdip, NULL, match_bdf, 1160 &imarg, &level, 0) != DDI_SUCCESS) { 1161 /* skip - nothing else we can do */ 1162 continue; 1163 } 1164 1165 /* should have walked 1 level i.e. rdip */ 1166 ASSERT(level > 0); 1167 1168 /* look for lowest ancestor matching drhd */ 1169 if (imarg.ima_ddip && (tdrhd == NULL || 1170 level < tlevel)) { 1171 tdrhd = drhd; 1172 tlevel = level; 1173 } 1174 } 1175 } 1176 } 1177 1178 if ((drhd = tdrhd) != NULL) { 1179 goto found; 1180 } 1181 1182 for (seg = 0; seg < IMMU_MAXSEG; seg++) { 1183 drhd = list_head(&(tbl->tbl_drhd_list[seg])); 1184 for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg], 1185 drhd)) { 1186 /* Look for include all */ 1187 if (drhd->dr_include_all == B_TRUE) { 1188 break; 1189 } 1190 } 1191 } 1192 1193 /*FALLTHRU*/ 1194 1195 found: 1196 mutex_exit(&(tbl->tbl_lock)); 1197 1198 /* 1199 * No drhd (dmar unit) found for this device in the ACPI DMAR tables. 1200 * This may happen with buggy versions of BIOSes. Just warn instead 1201 * of panic as we don't want whole system to go down because of one 1202 * device. 1203 */ 1204 if (drhd == NULL) { 1205 ddi_err(DER_WARN, rdip, "can't find Intel IOMMU unit for " 1206 "device in ACPI DMAR table."); 1207 return (NULL); 1208 } 1209 1210 return (drhd->dr_immu); 1211 } 1212 1213 char * 1214 immu_dmar_unit_name(void *dmar_unit) 1215 { 1216 drhd_t *drhd = (drhd_t *)dmar_unit; 1217 1218 ASSERT(drhd->dr_dip); 1219 return (ddi_node_name(drhd->dr_dip)); 1220 } 1221 1222 dev_info_t * 1223 immu_dmar_unit_dip(void *dmar_unit) 1224 { 1225 drhd_t *drhd = (drhd_t *)dmar_unit; 1226 return (drhd->dr_dip); 1227 } 1228 1229 void * 1230 immu_dmar_walk_units(int seg, void *dmar_unit) 1231 { 1232 list_t *drhd_list; 1233 drhd_t *drhd = (drhd_t *)dmar_unit; 1234 1235 drhd_list = &(dmar_table->tbl_drhd_list[seg]); 1236 1237 if (drhd == NULL) { 1238 return ((void *)list_head(drhd_list)); 1239 } else { 1240 return ((void *)list_next(drhd_list, drhd)); 1241 } 1242 } 1243 1244 void 1245 immu_dmar_set_immu(void *dmar_unit, immu_t *immu) 1246 { 1247 drhd_t *drhd = (drhd_t *)dmar_unit; 1248 1249 ASSERT(drhd); 1250 ASSERT(immu); 1251 1252 drhd->dr_immu = immu; 1253 } 1254 1255 boolean_t 1256 immu_dmar_intrmap_supported(void) 1257 { 1258 ASSERT(dmar_table); 1259 return (dmar_table->tbl_intrmap); 1260 } 1261 1262 /* for a given ioapicid, find the source id and immu */ 1263 uint16_t 1264 immu_dmar_ioapic_sid(int ioapicid) 1265 { 1266 ioapic_drhd_t *idt; 1267 1268 idt = ioapic_drhd_lookup(ioapicid); 1269 if (idt == NULL) { 1270 ddi_err(DER_PANIC, NULL, "cannot determine source-id for " 1271 "IOAPIC (id = %d)", ioapicid); 1272 /*NOTREACHED*/ 1273 } 1274 1275 return (idt->ioapic_sid); 1276 } 1277 1278 /* for a given ioapicid, find the source id and immu */ 1279 immu_t * 1280 immu_dmar_ioapic_immu(int ioapicid) 1281 { 1282 ioapic_drhd_t *idt; 1283 1284 idt = ioapic_drhd_lookup(ioapicid); 1285 if (idt) { 1286 return (idt->ioapic_drhd ? idt->ioapic_drhd->dr_immu : NULL); 1287 } 1288 return (NULL); 1289 } 1290