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 2009 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 <sys/acpi/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_nbus; 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_nbus; 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->address = orig->address; 156 new->size = orig->size; 157 new->prev = orig; 158 159 /* sorted insertion of 'new' into ranges list */ 160 for (current = ranges, prev = NULL; current != NULL; 161 prev = current, current = current->next) 162 if (new->address < current->address) 163 break; 164 165 if (prev == NULL) { 166 /* place at beginning of (possibly) empty list */ 167 new->next = ranges; 168 ranges = new; 169 } else { 170 /* place in list (possibly at end) */ 171 new->next = current; 172 prev->next = new; 173 } 174 } 175 176 /* scan the list, perform trimming */ 177 current = ranges; 178 while (current != NULL) { 179 struct memlist *next = current->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->prev points to the original range) 188 */ 189 if ((current->address + current->size) > next->address) 190 current->prev->size = next->address - current->address; 191 192 current = next; 193 } 194 195 /* discard the list */ 196 memlist_free_all(&ranges); /* OK if ranges == NULL */ 197 } 198 199 static int 200 acpi_find_bus_res(int bus, int type, struct memlist **res) 201 { 202 203 switch (type) { 204 case IO_TYPE: 205 *res = acpi_io_res[bus]; 206 break; 207 case MEM_TYPE: 208 *res = acpi_mem_res[bus]; 209 break; 210 case PREFETCH_TYPE: 211 *res = acpi_pmem_res[bus]; 212 break; 213 case BUSRANGE_TYPE: 214 *res = acpi_bus_res[bus]; 215 break; 216 default: 217 *res = NULL; 218 break; 219 } 220 221 /* memlist_count() treats NULL head as zero-length */ 222 return (memlist_count(*res)); 223 } 224 225 void 226 bus_res_fini(void) 227 { 228 int bus; 229 230 for (bus = 0; bus < pci_bios_nbus; bus++) { 231 memlist_free_all(&acpi_io_res[bus]); 232 memlist_free_all(&acpi_mem_res[bus]); 233 memlist_free_all(&acpi_pmem_res[bus]); 234 memlist_free_all(&acpi_bus_res[bus]); 235 } 236 } 237 238 239 struct memlist ** 240 rlistpp(UINT8 t, UINT8 flags, int bus) 241 { 242 switch (t) { 243 244 case ACPI_MEMORY_RANGE: 245 /* is this really the best we've got? */ 246 if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY) 247 return (&acpi_pmem_res[bus]); 248 else 249 return (&acpi_mem_res[bus]); 250 251 case ACPI_IO_RANGE: return &acpi_io_res[bus]; 252 case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus]; 253 } 254 return ((struct memlist **)NULL); 255 } 256 257 258 ACPI_STATUS 259 acpi_wr_cb(ACPI_RESOURCE *rp, void *context) 260 { 261 int bus = (intptr_t)context; 262 263 /* ignore consumed resources */ 264 if (rp->Data.Address.ProducerConsumer == 1) 265 return (AE_OK); 266 267 switch (rp->Type) { 268 case ACPI_RESOURCE_TYPE_IRQ: 269 /* never expect to see a PCI bus produce an Interrupt */ 270 dprintf("%s\n", "IRQ"); 271 break; 272 273 case ACPI_RESOURCE_TYPE_DMA: 274 /* never expect to see a PCI bus produce DMA */ 275 dprintf("%s\n", "DMA"); 276 break; 277 278 case ACPI_RESOURCE_TYPE_START_DEPENDENT: 279 dprintf("%s\n", "START_DEPENDENT"); 280 break; 281 282 case ACPI_RESOURCE_TYPE_END_DEPENDENT: 283 dprintf("%s\n", "END_DEPENDENT"); 284 break; 285 286 case ACPI_RESOURCE_TYPE_IO: 287 if (rp->Data.Io.AddressLength == 0) 288 break; 289 acpi_cb_cnt++; 290 memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum, 291 rp->Data.Io.AddressLength); 292 break; 293 294 case ACPI_RESOURCE_TYPE_FIXED_IO: 295 /* only expect to see this as a consumer */ 296 dprintf("%s\n", "FIXED_IO"); 297 break; 298 299 case ACPI_RESOURCE_TYPE_VENDOR: 300 dprintf("%s\n", "VENDOR"); 301 break; 302 303 case ACPI_RESOURCE_TYPE_END_TAG: 304 dprintf("%s\n", "END_TAG"); 305 break; 306 307 case ACPI_RESOURCE_TYPE_MEMORY24: 308 /* only expect to see this as a consumer */ 309 dprintf("%s\n", "MEMORY24"); 310 break; 311 312 case ACPI_RESOURCE_TYPE_MEMORY32: 313 /* only expect to see this as a consumer */ 314 dprintf("%s\n", "MEMORY32"); 315 break; 316 317 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 318 /* only expect to see this as a consumer */ 319 dprintf("%s\n", "FIXED_MEMORY32"); 320 break; 321 322 case ACPI_RESOURCE_TYPE_ADDRESS16: 323 if (rp->Data.Address16.AddressLength == 0) 324 break; 325 acpi_cb_cnt++; 326 memlist_insert(rlistpp(rp->Data.Address16.ResourceType, 327 rp->Data.Address16.Info.TypeSpecific, bus), 328 rp->Data.Address16.Minimum, 329 rp->Data.Address16.AddressLength); 330 break; 331 332 case ACPI_RESOURCE_TYPE_ADDRESS32: 333 if (rp->Data.Address32.AddressLength == 0) 334 break; 335 acpi_cb_cnt++; 336 memlist_insert(rlistpp(rp->Data.Address32.ResourceType, 337 rp->Data.Address32.Info.TypeSpecific, bus), 338 rp->Data.Address32.Minimum, 339 rp->Data.Address32.AddressLength); 340 break; 341 342 case ACPI_RESOURCE_TYPE_ADDRESS64: 343 /* 344 * We comment out this block because we currently cannot deal with 345 * PCI 64-bit addresses. Will revisit this when we add PCI 64-bit MMIO 346 * support. 347 */ 348 #if 0 349 if (rp->Data.Address64.AddressLength == 0) 350 break; 351 acpi_cb_cnt++; 352 memlist_insert(rlistpp(rp->Data.Address64.ResourceType, 353 rp->Data.Address64.Info.TypeSpecific, bus), 354 rp->Data.Address64.Minimum, 355 rp->Data.Address64.AddressLength); 356 #endif 357 break; 358 359 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 360 #if 0 /* Will revisit this when we add PCI 64-bit MMIO support */ 361 if (rp->Data.ExtAddress64.AddressLength == 0) 362 break; 363 acpi_cb_cnt++; 364 memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType, 365 rp->Data.ExtAddress64.Info.TypeSpecific, bus), 366 rp->Data.ExtAddress64.Minimum, 367 rp->Data.ExtAddress64.AddressLength); 368 #endif 369 break; 370 371 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 372 /* never expect to see a PCI bus produce an Interrupt */ 373 dprintf("%s\n", "EXTENDED_IRQ"); 374 break; 375 376 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 377 /* never expect to see a PCI bus produce an GAS */ 378 dprintf("%s\n", "GENERIC_REGISTER"); 379 break; 380 } 381 382 return (AE_OK); 383 } 384 385 static void 386 mps_probe() 387 { 388 uchar_t *extp; 389 struct mps_fps_hdr *fpp = NULL; 390 struct mps_ct_hdr *ctp; 391 uintptr_t ebda_start, base_end; 392 ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg; 393 394 base_size = *((ushort_t *)(0x413)); 395 ebda_seg = *((ushort_t *)(0x40e)); 396 ebda_start = ((uint32_t)ebda_seg) << 4; 397 if (ebda_seg != 0) { 398 fpp = (struct mps_fps_hdr *)find_sig( 399 (uchar_t *)ebda_start, 1024, "_MP_"); 400 } 401 if (fpp == NULL) { 402 base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0; 403 if (base_end_seg != ebda_seg) { 404 base_end = ((uintptr_t)base_end_seg) << 4; 405 fpp = (struct mps_fps_hdr *)find_sig( 406 (uchar_t *)base_end, 1024, "_MP_"); 407 } 408 } 409 if (fpp == NULL) { 410 fpp = (struct mps_fps_hdr *)find_sig( 411 (uchar_t *)0xF0000, 0x10000, "_MP_"); 412 } 413 414 if (fpp == NULL) { 415 dprintf("MP Spec table doesn't exist"); 416 return; 417 } else { 418 dprintf("Found MP Floating Pointer Structure at %p\n", 419 (void *)fpp); 420 } 421 422 if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) { 423 dprintf("MP Floating Pointer Structure checksum error"); 424 return; 425 } 426 427 ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr; 428 if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */ 429 dprintf("MP Configuration Table signature is wrong"); 430 return; 431 } 432 433 base_len = ctp->ct_len; 434 if (checksum((uchar_t *)ctp, base_len) != 0) { 435 dprintf("MP Configuration Table checksum error"); 436 return; 437 } 438 if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */ 439 dprintf("MP Spec 1.1 found - extended table doesn't exist"); 440 return; 441 } 442 if ((ext_len = ctp->ct_ext_tbl_len) == 0) { 443 dprintf("MP Spec 1.4 found - extended table doesn't exist"); 444 return; 445 } 446 extp = (uchar_t *)ctp + base_len; 447 if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) { 448 dprintf("MP Extended Table checksum error"); 449 return; 450 } 451 mps_extp = extp; 452 mps_ext_endp = mps_extp + ext_len; 453 } 454 455 456 static int 457 mps_find_bus_res(int bus, int type, struct memlist **res) 458 { 459 struct sasm *sasmp; 460 uchar_t *extp; 461 int res_cnt; 462 463 if (mps_extp == NULL) 464 return (0); 465 extp = mps_extp; 466 res_cnt = 0; 467 while (extp < mps_ext_endp) { 468 switch (*extp) { 469 case SYS_AS_MAPPING: 470 sasmp = (struct sasm *)extp; 471 if (((int)sasmp->sasm_as_type) == type && 472 ((int)sasmp->sasm_bus_id) == bus) { 473 if (sasmp->sasm_as_base_hi != 0 || 474 sasmp->sasm_as_len_hi != 0) { 475 printf("64 bits address space\n"); 476 extp += SYS_AS_MAPPING_SIZE; 477 break; 478 } 479 memlist_insert(res, 480 (uint64_t)sasmp->sasm_as_base, 481 sasmp->sasm_as_len); 482 res_cnt++; 483 } 484 extp += SYS_AS_MAPPING_SIZE; 485 break; 486 case BUS_HIERARCHY_DESC: 487 extp += BUS_HIERARCHY_DESC_SIZE; 488 break; 489 case COMP_BUS_AS_MODIFIER: 490 extp += COMP_BUS_AS_MODIFIER_SIZE; 491 break; 492 default: 493 cmn_err(CE_WARN, "Unknown descriptor type %d" 494 " in BIOS Multiprocessor Spec table.", 495 *extp); 496 while (*res) { 497 struct memlist *tmp = *res; 498 *res = tmp->next; 499 memlist_free(tmp); 500 } 501 return (0); 502 } 503 } 504 return (res_cnt); 505 } 506 507 static void 508 hrt_probe() 509 { 510 struct hrt_hdr *hrtp; 511 512 dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n"); 513 if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000, 514 0x10000, "$HRT")) == NULL) { 515 dprintf("NO PCI Hot-Plug Resource Table"); 516 return; 517 } 518 dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp); 519 if (hrtp->hrt_ver != 1) { 520 dprintf("PCI Hot-Plug Resource Table version no. <> 1\n"); 521 return; 522 } 523 hrt_entry_cnt = (int)hrtp->hrt_entry_cnt; 524 dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt); 525 hrt_hpep = (struct php_entry *)(hrtp + 1); 526 } 527 528 static int 529 hrt_find_bus_res(int bus, int type, struct memlist **res) 530 { 531 int res_cnt, i; 532 struct php_entry *hpep; 533 534 if (hrt_hpep == NULL || hrt_entry_cnt == 0) 535 return (0); 536 hpep = hrt_hpep; 537 res_cnt = 0; 538 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 539 if (hpep->php_pri_bus != bus) 540 continue; 541 if (type == IO_TYPE) { 542 if (hpep->php_io_start == 0 || hpep->php_io_size == 0) 543 continue; 544 memlist_insert(res, (uint64_t)hpep->php_io_start, 545 (uint64_t)hpep->php_io_size); 546 res_cnt++; 547 } else if (type == MEM_TYPE) { 548 if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0) 549 continue; 550 memlist_insert(res, 551 (uint64_t)(((int)hpep->php_mem_start) << 16), 552 (uint64_t)(((int)hpep->php_mem_size) << 16)); 553 res_cnt++; 554 } else if (type == PREFETCH_TYPE) { 555 if (hpep->php_pfmem_start == 0 || 556 hpep->php_pfmem_size == 0) 557 continue; 558 memlist_insert(res, 559 (uint64_t)(((int)hpep->php_pfmem_start) << 16), 560 (uint64_t)(((int)hpep->php_pfmem_size) << 16)); 561 res_cnt++; 562 } 563 } 564 return (res_cnt); 565 } 566 567 static uchar_t * 568 find_sig(uchar_t *cp, int len, char *sig) 569 { 570 long i; 571 572 /* Search for the "_MP_" or "$HRT" signature */ 573 for (i = 0; i < len; i += 16) { 574 if (cp[0] == sig[0] && cp[1] == sig[1] && 575 cp[2] == sig[2] && cp[3] == sig[3]) 576 return (cp); 577 cp += 16; 578 } 579 return (NULL); 580 } 581 582 static int 583 checksum(unsigned char *cp, int len) 584 { 585 int i; 586 unsigned int cksum; 587 588 for (i = cksum = 0; i < len; i++) 589 cksum += (unsigned int) *cp++; 590 591 return ((int)(cksum & 0xFF)); 592 } 593 594 #ifdef UNUSED_BUS_HIERARY_INFO 595 596 /* 597 * At this point, the bus hierarchy entries do not appear to 598 * provide anything we can't find out from PCI config space. 599 * The only interesting bit is the ISA bus number, which we 600 * don't care. 601 */ 602 int 603 mps_find_parent_bus(int bus) 604 { 605 struct sasm *sasmp; 606 uchar_t *extp; 607 608 if (mps_extp == NULL) 609 return (-1); 610 611 extp = mps_extp; 612 while (extp < mps_ext_endp) { 613 bhdp = (struct bhd *)extp; 614 switch (*extp) { 615 case SYS_AS_MAPPING: 616 extp += SYS_AS_MAPPING_SIZE; 617 break; 618 case BUS_HIERARCHY_DESC: 619 if (bhdp->bhd_bus_id == bus) 620 return (bhdp->bhd_parent); 621 extp += BUS_HIERARCHY_DESC_SIZE; 622 break; 623 case COMP_BUS_AS_MODIFIER: 624 extp += COMP_BUS_AS_MODIFIER_SIZE; 625 break; 626 default: 627 cmn_err(CE_WARN, "Unknown descriptor type %d" 628 " in BIOS Multiprocessor Spec table.", 629 *extp); 630 return (-1); 631 } 632 } 633 return (-1); 634 } 635 636 int 637 hrt_find_bus_range(int bus) 638 { 639 int i, max_bus, sub_bus; 640 struct php_entry *hpep; 641 642 if (hrt_hpep == NULL || hrt_entry_cnt == 0) { 643 return (-1); 644 } 645 hpep = hrt_hpep; 646 max_bus = -1; 647 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 648 if (hpep->php_pri_bus != bus) 649 continue; 650 sub_bus = (int)hpep->php_subord_bus; 651 if (sub_bus > max_bus) 652 max_bus = sub_bus; 653 } 654 return (max_bus); 655 } 656 657 #endif /* UNUSED_BUS_HIERARY_INFO */ 658