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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * pci_resource.c -- routines to retrieve available bus resources from 26 * the MP Spec. Table and Hotplug Resource Table 27 */ 28 29 #include <sys/types.h> 30 #include <sys/memlist.h> 31 #include <sys/pci_impl.h> 32 #include <sys/systm.h> 33 #include <sys/cmn_err.h> 34 #include <acpica/include/acpi.h> 35 #include <sys/acpica.h> 36 #include "mps_table.h" 37 #include "pcihrt.h" 38 39 extern int pci_boot_debug; 40 extern int pci_bios_maxbus; 41 #define dprintf if (pci_boot_debug) printf 42 43 static int tbl_init = 0; 44 static uchar_t *mps_extp = NULL; 45 static uchar_t *mps_ext_endp = NULL; 46 static struct php_entry *hrt_hpep; 47 static int hrt_entry_cnt = 0; 48 static int acpi_cb_cnt = 0; 49 50 static void mps_probe(void); 51 static void acpi_pci_probe(void); 52 static int mps_find_bus_res(int, int, struct memlist **); 53 static void hrt_probe(void); 54 static int hrt_find_bus_res(int, int, struct memlist **); 55 static int acpi_find_bus_res(int, int, struct memlist **); 56 static uchar_t *find_sig(uchar_t *cp, int len, char *sig); 57 static int checksum(unsigned char *cp, int len); 58 static ACPI_STATUS acpi_wr_cb(ACPI_RESOURCE *rp, void *context); 59 void bus_res_fini(void); 60 static void acpi_trim_bus_ranges(void); 61 62 struct memlist *acpi_io_res[256]; 63 struct memlist *acpi_mem_res[256]; 64 struct memlist *acpi_pmem_res[256]; 65 struct memlist *acpi_bus_res[256]; 66 67 /* 68 * -1 = attempt ACPI resource discovery 69 * 0 = don't attempt ACPI resource discovery 70 * 1 = ACPI resource discovery successful 71 */ 72 volatile int acpi_resource_discovery = -1; 73 74 struct memlist * 75 find_bus_res(int bus, int type) 76 { 77 struct memlist *res = NULL; 78 79 if (tbl_init == 0) { 80 tbl_init = 1; 81 acpi_pci_probe(); 82 hrt_probe(); 83 mps_probe(); 84 } 85 86 if (acpi_find_bus_res(bus, type, &res) > 0) 87 return (res); 88 89 if (hrt_find_bus_res(bus, type, &res) > 0) 90 return (res); 91 92 (void) mps_find_bus_res(bus, type, &res); 93 return (res); 94 } 95 96 97 static void 98 acpi_pci_probe(void) 99 { 100 ACPI_HANDLE ah; 101 dev_info_t *dip; 102 int bus; 103 104 if (acpi_resource_discovery == 0) 105 return; 106 107 for (bus = 0; bus <= pci_bios_maxbus; bus++) { 108 /* if no dip or no ACPI handle, no resources to discover */ 109 dip = pci_bus_res[bus].dip; 110 if ((dip == NULL) || 111 (ACPI_FAILURE(acpica_get_handle(dip, &ah)))) 112 continue; 113 114 (void) AcpiWalkResources(ah, "_CRS", acpi_wr_cb, 115 (void *)(uintptr_t)bus); 116 } 117 118 if (acpi_cb_cnt > 0) { 119 acpi_resource_discovery = 1; 120 acpi_trim_bus_ranges(); 121 } 122 } 123 124 /* 125 * Trim overlapping bus ranges in acpi_bus_res[] 126 * Some BIOSes report root-bridges with bus ranges that 127 * overlap, for example:"0..255" and "8..255". Lower-numbered 128 * ranges are trimmed by upper-numbered ranges (so "0..255" would 129 * be trimmed to "0..7", in the example). 130 */ 131 static void 132 acpi_trim_bus_ranges() 133 { 134 struct memlist *ranges, *current; 135 int bus; 136 137 ranges = NULL; 138 139 /* 140 * Assumptions: 141 * - there exists at most 1 bus range entry for each bus number 142 * - there are no (broken) ranges that start at the same bus number 143 */ 144 for (bus = 0; bus < 256; bus++) { 145 struct memlist *prev, *orig, *new; 146 /* skip buses with no range entry */ 147 if ((orig = acpi_bus_res[bus]) == NULL) 148 continue; 149 150 /* 151 * create copy of existing range and overload 152 * 'prev' pointer to link existing to new copy 153 */ 154 new = memlist_alloc(); 155 new->ml_address = orig->ml_address; 156 new->ml_size = orig->ml_size; 157 new->ml_prev = orig; 158 159 /* sorted insertion of 'new' into ranges list */ 160 for (current = ranges, prev = NULL; current != NULL; 161 prev = current, current = current->ml_next) 162 if (new->ml_address < current->ml_address) 163 break; 164 165 if (prev == NULL) { 166 /* place at beginning of (possibly) empty list */ 167 new->ml_next = ranges; 168 ranges = new; 169 } else { 170 /* place in list (possibly at end) */ 171 new->ml_next = current; 172 prev->ml_next = new; 173 } 174 } 175 176 /* scan the list, perform trimming */ 177 current = ranges; 178 while (current != NULL) { 179 struct memlist *next = current->ml_next; 180 181 /* done when no range above current */ 182 if (next == NULL) 183 break; 184 185 /* 186 * trim size in original range element 187 * (current->ml_prev points to the original range) 188 */ 189 if ((current->ml_address + current->ml_size) > next->ml_address) 190 current->ml_prev->ml_size = 191 next->ml_address - current->ml_address; 192 193 current = next; 194 } 195 196 /* discard the list */ 197 memlist_free_all(&ranges); /* OK if ranges == NULL */ 198 } 199 200 static int 201 acpi_find_bus_res(int bus, int type, struct memlist **res) 202 { 203 204 switch (type) { 205 case IO_TYPE: 206 *res = acpi_io_res[bus]; 207 break; 208 case MEM_TYPE: 209 *res = acpi_mem_res[bus]; 210 break; 211 case PREFETCH_TYPE: 212 *res = acpi_pmem_res[bus]; 213 break; 214 case BUSRANGE_TYPE: 215 *res = acpi_bus_res[bus]; 216 break; 217 default: 218 *res = NULL; 219 break; 220 } 221 222 /* memlist_count() treats NULL head as zero-length */ 223 return (memlist_count(*res)); 224 } 225 226 void 227 bus_res_fini(void) 228 { 229 int bus; 230 231 for (bus = 0; bus <= pci_bios_maxbus; bus++) { 232 memlist_free_all(&acpi_io_res[bus]); 233 memlist_free_all(&acpi_mem_res[bus]); 234 memlist_free_all(&acpi_pmem_res[bus]); 235 memlist_free_all(&acpi_bus_res[bus]); 236 } 237 } 238 239 240 struct memlist ** 241 rlistpp(UINT8 t, UINT8 flags, int bus) 242 { 243 switch (t) { 244 245 case ACPI_MEMORY_RANGE: 246 /* is this really the best we've got? */ 247 if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY) 248 return (&acpi_pmem_res[bus]); 249 else 250 return (&acpi_mem_res[bus]); 251 252 case ACPI_IO_RANGE: return &acpi_io_res[bus]; 253 case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus]; 254 } 255 return ((struct memlist **)NULL); 256 } 257 258 259 ACPI_STATUS 260 acpi_wr_cb(ACPI_RESOURCE *rp, void *context) 261 { 262 int bus = (intptr_t)context; 263 264 /* ignore consumed resources */ 265 if (rp->Data.Address.ProducerConsumer == 1) 266 return (AE_OK); 267 268 switch (rp->Type) { 269 case ACPI_RESOURCE_TYPE_IRQ: 270 /* never expect to see a PCI bus produce an Interrupt */ 271 dprintf("%s\n", "IRQ"); 272 break; 273 274 case ACPI_RESOURCE_TYPE_DMA: 275 /* never expect to see a PCI bus produce DMA */ 276 dprintf("%s\n", "DMA"); 277 break; 278 279 case ACPI_RESOURCE_TYPE_START_DEPENDENT: 280 dprintf("%s\n", "START_DEPENDENT"); 281 break; 282 283 case ACPI_RESOURCE_TYPE_END_DEPENDENT: 284 dprintf("%s\n", "END_DEPENDENT"); 285 break; 286 287 case ACPI_RESOURCE_TYPE_IO: 288 if (rp->Data.Io.AddressLength == 0) 289 break; 290 acpi_cb_cnt++; 291 memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum, 292 rp->Data.Io.AddressLength); 293 break; 294 295 case ACPI_RESOURCE_TYPE_FIXED_IO: 296 /* only expect to see this as a consumer */ 297 dprintf("%s\n", "FIXED_IO"); 298 break; 299 300 case ACPI_RESOURCE_TYPE_VENDOR: 301 dprintf("%s\n", "VENDOR"); 302 break; 303 304 case ACPI_RESOURCE_TYPE_END_TAG: 305 dprintf("%s\n", "END_TAG"); 306 break; 307 308 case ACPI_RESOURCE_TYPE_MEMORY24: 309 /* only expect to see this as a consumer */ 310 dprintf("%s\n", "MEMORY24"); 311 break; 312 313 case ACPI_RESOURCE_TYPE_MEMORY32: 314 /* only expect to see this as a consumer */ 315 dprintf("%s\n", "MEMORY32"); 316 break; 317 318 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 319 /* only expect to see this as a consumer */ 320 dprintf("%s\n", "FIXED_MEMORY32"); 321 break; 322 323 case ACPI_RESOURCE_TYPE_ADDRESS16: 324 if (rp->Data.Address16.AddressLength == 0) 325 break; 326 acpi_cb_cnt++; 327 memlist_insert(rlistpp(rp->Data.Address16.ResourceType, 328 rp->Data.Address16.Info.TypeSpecific, bus), 329 rp->Data.Address16.Minimum, 330 rp->Data.Address16.AddressLength); 331 break; 332 333 case ACPI_RESOURCE_TYPE_ADDRESS32: 334 if (rp->Data.Address32.AddressLength == 0) 335 break; 336 acpi_cb_cnt++; 337 memlist_insert(rlistpp(rp->Data.Address32.ResourceType, 338 rp->Data.Address32.Info.TypeSpecific, bus), 339 rp->Data.Address32.Minimum, 340 rp->Data.Address32.AddressLength); 341 break; 342 343 case ACPI_RESOURCE_TYPE_ADDRESS64: 344 /* 345 * We comment out this block because we currently cannot deal with 346 * PCI 64-bit addresses. Will revisit this when we add PCI 64-bit MMIO 347 * support. 348 */ 349 #if 0 350 if (rp->Data.Address64.AddressLength == 0) 351 break; 352 acpi_cb_cnt++; 353 memlist_insert(rlistpp(rp->Data.Address64.ResourceType, 354 rp->Data.Address64.Info.TypeSpecific, bus), 355 rp->Data.Address64.Minimum, 356 rp->Data.Address64.AddressLength); 357 #endif 358 break; 359 360 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 361 #if 0 /* Will revisit this when we add PCI 64-bit MMIO support */ 362 if (rp->Data.ExtAddress64.AddressLength == 0) 363 break; 364 acpi_cb_cnt++; 365 memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType, 366 rp->Data.ExtAddress64.Info.TypeSpecific, bus), 367 rp->Data.ExtAddress64.Minimum, 368 rp->Data.ExtAddress64.AddressLength); 369 #endif 370 break; 371 372 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 373 /* never expect to see a PCI bus produce an Interrupt */ 374 dprintf("%s\n", "EXTENDED_IRQ"); 375 break; 376 377 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 378 /* never expect to see a PCI bus produce an GAS */ 379 dprintf("%s\n", "GENERIC_REGISTER"); 380 break; 381 } 382 383 return (AE_OK); 384 } 385 386 static void 387 mps_probe() 388 { 389 uchar_t *extp; 390 struct mps_fps_hdr *fpp = NULL; 391 struct mps_ct_hdr *ctp; 392 uintptr_t ebda_start, base_end; 393 ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg; 394 395 base_size = *((ushort_t *)(0x413)); 396 ebda_seg = *((ushort_t *)(0x40e)); 397 ebda_start = ((uint32_t)ebda_seg) << 4; 398 if (ebda_seg != 0) { 399 fpp = (struct mps_fps_hdr *)find_sig( 400 (uchar_t *)ebda_start, 1024, "_MP_"); 401 } 402 if (fpp == NULL) { 403 base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0; 404 if (base_end_seg != ebda_seg) { 405 base_end = ((uintptr_t)base_end_seg) << 4; 406 fpp = (struct mps_fps_hdr *)find_sig( 407 (uchar_t *)base_end, 1024, "_MP_"); 408 } 409 } 410 if (fpp == NULL) { 411 fpp = (struct mps_fps_hdr *)find_sig( 412 (uchar_t *)0xF0000, 0x10000, "_MP_"); 413 } 414 415 if (fpp == NULL) { 416 dprintf("MP Spec table doesn't exist"); 417 return; 418 } else { 419 dprintf("Found MP Floating Pointer Structure at %p\n", 420 (void *)fpp); 421 } 422 423 if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) { 424 dprintf("MP Floating Pointer Structure checksum error"); 425 return; 426 } 427 428 ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr; 429 if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */ 430 dprintf("MP Configuration Table signature is wrong"); 431 return; 432 } 433 434 base_len = ctp->ct_len; 435 if (checksum((uchar_t *)ctp, base_len) != 0) { 436 dprintf("MP Configuration Table checksum error"); 437 return; 438 } 439 if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */ 440 dprintf("MP Spec 1.1 found - extended table doesn't exist"); 441 return; 442 } 443 if ((ext_len = ctp->ct_ext_tbl_len) == 0) { 444 dprintf("MP Spec 1.4 found - extended table doesn't exist"); 445 return; 446 } 447 extp = (uchar_t *)ctp + base_len; 448 if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) { 449 dprintf("MP Extended Table checksum error"); 450 return; 451 } 452 mps_extp = extp; 453 mps_ext_endp = mps_extp + ext_len; 454 } 455 456 457 static int 458 mps_find_bus_res(int bus, int type, struct memlist **res) 459 { 460 struct sasm *sasmp; 461 uchar_t *extp; 462 int res_cnt; 463 464 if (mps_extp == NULL) 465 return (0); 466 extp = mps_extp; 467 res_cnt = 0; 468 while (extp < mps_ext_endp) { 469 switch (*extp) { 470 case SYS_AS_MAPPING: 471 sasmp = (struct sasm *)extp; 472 if (((int)sasmp->sasm_as_type) == type && 473 ((int)sasmp->sasm_bus_id) == bus) { 474 if (sasmp->sasm_as_base_hi != 0 || 475 sasmp->sasm_as_len_hi != 0) { 476 printf("64 bits address space\n"); 477 extp += SYS_AS_MAPPING_SIZE; 478 break; 479 } 480 memlist_insert(res, 481 (uint64_t)sasmp->sasm_as_base, 482 sasmp->sasm_as_len); 483 res_cnt++; 484 } 485 extp += SYS_AS_MAPPING_SIZE; 486 break; 487 case BUS_HIERARCHY_DESC: 488 extp += BUS_HIERARCHY_DESC_SIZE; 489 break; 490 case COMP_BUS_AS_MODIFIER: 491 extp += COMP_BUS_AS_MODIFIER_SIZE; 492 break; 493 default: 494 cmn_err(CE_WARN, "Unknown descriptor type %d" 495 " in BIOS Multiprocessor Spec table.", 496 *extp); 497 while (*res) { 498 struct memlist *tmp = *res; 499 *res = tmp->ml_next; 500 memlist_free(tmp); 501 } 502 return (0); 503 } 504 } 505 return (res_cnt); 506 } 507 508 static void 509 hrt_probe() 510 { 511 struct hrt_hdr *hrtp; 512 513 dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n"); 514 if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000, 515 0x10000, "$HRT")) == NULL) { 516 dprintf("NO PCI Hot-Plug Resource Table"); 517 return; 518 } 519 dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp); 520 if (hrtp->hrt_ver != 1) { 521 dprintf("PCI Hot-Plug Resource Table version no. <> 1\n"); 522 return; 523 } 524 hrt_entry_cnt = (int)hrtp->hrt_entry_cnt; 525 dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt); 526 hrt_hpep = (struct php_entry *)(hrtp + 1); 527 } 528 529 static int 530 hrt_find_bus_res(int bus, int type, struct memlist **res) 531 { 532 int res_cnt, i; 533 struct php_entry *hpep; 534 535 if (hrt_hpep == NULL || hrt_entry_cnt == 0) 536 return (0); 537 hpep = hrt_hpep; 538 res_cnt = 0; 539 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 540 if (hpep->php_pri_bus != bus) 541 continue; 542 if (type == IO_TYPE) { 543 if (hpep->php_io_start == 0 || hpep->php_io_size == 0) 544 continue; 545 memlist_insert(res, (uint64_t)hpep->php_io_start, 546 (uint64_t)hpep->php_io_size); 547 res_cnt++; 548 } else if (type == MEM_TYPE) { 549 if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0) 550 continue; 551 memlist_insert(res, 552 (uint64_t)(((int)hpep->php_mem_start) << 16), 553 (uint64_t)(((int)hpep->php_mem_size) << 16)); 554 res_cnt++; 555 } else if (type == PREFETCH_TYPE) { 556 if (hpep->php_pfmem_start == 0 || 557 hpep->php_pfmem_size == 0) 558 continue; 559 memlist_insert(res, 560 (uint64_t)(((int)hpep->php_pfmem_start) << 16), 561 (uint64_t)(((int)hpep->php_pfmem_size) << 16)); 562 res_cnt++; 563 } 564 } 565 return (res_cnt); 566 } 567 568 static uchar_t * 569 find_sig(uchar_t *cp, int len, char *sig) 570 { 571 long i; 572 573 /* Search for the "_MP_" or "$HRT" signature */ 574 for (i = 0; i < len; i += 16) { 575 if (cp[0] == sig[0] && cp[1] == sig[1] && 576 cp[2] == sig[2] && cp[3] == sig[3]) 577 return (cp); 578 cp += 16; 579 } 580 return (NULL); 581 } 582 583 static int 584 checksum(unsigned char *cp, int len) 585 { 586 int i; 587 unsigned int cksum; 588 589 for (i = cksum = 0; i < len; i++) 590 cksum += (unsigned int) *cp++; 591 592 return ((int)(cksum & 0xFF)); 593 } 594 595 #ifdef UNUSED_BUS_HIERARY_INFO 596 597 /* 598 * At this point, the bus hierarchy entries do not appear to 599 * provide anything we can't find out from PCI config space. 600 * The only interesting bit is the ISA bus number, which we 601 * don't care. 602 */ 603 int 604 mps_find_parent_bus(int bus) 605 { 606 struct sasm *sasmp; 607 uchar_t *extp; 608 609 if (mps_extp == NULL) 610 return (-1); 611 612 extp = mps_extp; 613 while (extp < mps_ext_endp) { 614 bhdp = (struct bhd *)extp; 615 switch (*extp) { 616 case SYS_AS_MAPPING: 617 extp += SYS_AS_MAPPING_SIZE; 618 break; 619 case BUS_HIERARCHY_DESC: 620 if (bhdp->bhd_bus_id == bus) 621 return (bhdp->bhd_parent); 622 extp += BUS_HIERARCHY_DESC_SIZE; 623 break; 624 case COMP_BUS_AS_MODIFIER: 625 extp += COMP_BUS_AS_MODIFIER_SIZE; 626 break; 627 default: 628 cmn_err(CE_WARN, "Unknown descriptor type %d" 629 " in BIOS Multiprocessor Spec table.", 630 *extp); 631 return (-1); 632 } 633 } 634 return (-1); 635 } 636 637 int 638 hrt_find_bus_range(int bus) 639 { 640 int i, max_bus, sub_bus; 641 struct php_entry *hpep; 642 643 if (hrt_hpep == NULL || hrt_entry_cnt == 0) { 644 return (-1); 645 } 646 hpep = hrt_hpep; 647 max_bus = -1; 648 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 649 if (hpep->php_pri_bus != bus) 650 continue; 651 sub_bus = (int)hpep->php_subord_bus; 652 if (sub_bus > max_bus) 653 max_bus = sub_bus; 654 } 655 return (max_bus); 656 } 657 658 #endif /* UNUSED_BUS_HIERARY_INFO */