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 if (rp->Data.Address64.AddressLength == 0) 344 break; 345 acpi_cb_cnt++; 346 memlist_insert(rlistpp(rp->Data.Address64.ResourceType, 347 rp->Data.Address64.Info.TypeSpecific, bus), 348 rp->Data.Address64.Minimum, 349 rp->Data.Address64.AddressLength); 350 break; 351 352 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 353 if (rp->Data.ExtAddress64.AddressLength == 0) 354 break; 355 acpi_cb_cnt++; 356 memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType, 357 rp->Data.ExtAddress64.Info.TypeSpecific, bus), 358 rp->Data.ExtAddress64.Minimum, 359 rp->Data.ExtAddress64.AddressLength); 360 break; 361 362 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 363 /* never expect to see a PCI bus produce an Interrupt */ 364 dprintf("%s\n", "EXTENDED_IRQ"); 365 break; 366 367 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 368 /* never expect to see a PCI bus produce an GAS */ 369 dprintf("%s\n", "GENERIC_REGISTER"); 370 break; 371 } 372 373 return (AE_OK); 374 } 375 376 static void 377 mps_probe() 378 { 379 uchar_t *extp; 380 struct mps_fps_hdr *fpp = NULL; 381 struct mps_ct_hdr *ctp; 382 uintptr_t ebda_start, base_end; 383 ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg; 384 385 base_size = *((ushort_t *)(0x413)); 386 ebda_seg = *((ushort_t *)(0x40e)); 387 ebda_start = ((uint32_t)ebda_seg) << 4; 388 if (ebda_seg != 0) { 389 fpp = (struct mps_fps_hdr *)find_sig( 390 (uchar_t *)ebda_start, 1024, "_MP_"); 391 } 392 if (fpp == NULL) { 393 base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0; 394 if (base_end_seg != ebda_seg) { 395 base_end = ((uintptr_t)base_end_seg) << 4; 396 fpp = (struct mps_fps_hdr *)find_sig( 397 (uchar_t *)base_end, 1024, "_MP_"); 398 } 399 } 400 if (fpp == NULL) { 401 fpp = (struct mps_fps_hdr *)find_sig( 402 (uchar_t *)0xF0000, 0x10000, "_MP_"); 403 } 404 405 if (fpp == NULL) { 406 dprintf("MP Spec table doesn't exist"); 407 return; 408 } else { 409 dprintf("Found MP Floating Pointer Structure at %p\n", 410 (void *)fpp); 411 } 412 413 if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) { 414 dprintf("MP Floating Pointer Structure checksum error"); 415 return; 416 } 417 418 ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr; 419 if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */ 420 dprintf("MP Configuration Table signature is wrong"); 421 return; 422 } 423 424 base_len = ctp->ct_len; 425 if (checksum((uchar_t *)ctp, base_len) != 0) { 426 dprintf("MP Configuration Table checksum error"); 427 return; 428 } 429 if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */ 430 dprintf("MP Spec 1.1 found - extended table doesn't exist"); 431 return; 432 } 433 if ((ext_len = ctp->ct_ext_tbl_len) == 0) { 434 dprintf("MP Spec 1.4 found - extended table doesn't exist"); 435 return; 436 } 437 extp = (uchar_t *)ctp + base_len; 438 if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) { 439 dprintf("MP Extended Table checksum error"); 440 return; 441 } 442 mps_extp = extp; 443 mps_ext_endp = mps_extp + ext_len; 444 } 445 446 447 static int 448 mps_find_bus_res(int bus, int type, struct memlist **res) 449 { 450 struct sasm *sasmp; 451 uchar_t *extp; 452 int res_cnt; 453 454 if (mps_extp == NULL) 455 return (0); 456 extp = mps_extp; 457 res_cnt = 0; 458 while (extp < mps_ext_endp) { 459 switch (*extp) { 460 case SYS_AS_MAPPING: 461 sasmp = (struct sasm *)extp; 462 if (((int)sasmp->sasm_as_type) == type && 463 ((int)sasmp->sasm_bus_id) == bus) { 464 if (sasmp->sasm_as_base_hi != 0 || 465 sasmp->sasm_as_len_hi != 0) { 466 printf("64 bits address space\n"); 467 extp += SYS_AS_MAPPING_SIZE; 468 break; 469 } 470 memlist_insert(res, 471 (uint64_t)sasmp->sasm_as_base, 472 sasmp->sasm_as_len); 473 res_cnt++; 474 } 475 extp += SYS_AS_MAPPING_SIZE; 476 break; 477 case BUS_HIERARCHY_DESC: 478 extp += BUS_HIERARCHY_DESC_SIZE; 479 break; 480 case COMP_BUS_AS_MODIFIER: 481 extp += COMP_BUS_AS_MODIFIER_SIZE; 482 break; 483 default: 484 cmn_err(CE_WARN, "Unknown descriptor type %d" 485 " in BIOS Multiprocessor Spec table.", 486 *extp); 487 while (*res) { 488 struct memlist *tmp = *res; 489 *res = tmp->next; 490 memlist_free(tmp); 491 } 492 return (0); 493 } 494 } 495 return (res_cnt); 496 } 497 498 static void 499 hrt_probe() 500 { 501 struct hrt_hdr *hrtp; 502 503 dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n"); 504 if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000, 505 0x10000, "$HRT")) == NULL) { 506 dprintf("NO PCI Hot-Plug Resource Table"); 507 return; 508 } 509 dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp); 510 if (hrtp->hrt_ver != 1) { 511 dprintf("PCI Hot-Plug Resource Table version no. <> 1\n"); 512 return; 513 } 514 hrt_entry_cnt = (int)hrtp->hrt_entry_cnt; 515 dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt); 516 hrt_hpep = (struct php_entry *)(hrtp + 1); 517 } 518 519 static int 520 hrt_find_bus_res(int bus, int type, struct memlist **res) 521 { 522 int res_cnt, i; 523 struct php_entry *hpep; 524 525 if (hrt_hpep == NULL || hrt_entry_cnt == 0) 526 return (0); 527 hpep = hrt_hpep; 528 res_cnt = 0; 529 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 530 if (hpep->php_pri_bus != bus) 531 continue; 532 if (type == IO_TYPE) { 533 if (hpep->php_io_start == 0 || hpep->php_io_size == 0) 534 continue; 535 memlist_insert(res, (uint64_t)hpep->php_io_start, 536 (uint64_t)hpep->php_io_size); 537 res_cnt++; 538 } else if (type == MEM_TYPE) { 539 if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0) 540 continue; 541 memlist_insert(res, 542 (uint64_t)(((int)hpep->php_mem_start) << 16), 543 (uint64_t)(((int)hpep->php_mem_size) << 16)); 544 res_cnt++; 545 } else if (type == PREFETCH_TYPE) { 546 if (hpep->php_pfmem_start == 0 || 547 hpep->php_pfmem_size == 0) 548 continue; 549 memlist_insert(res, 550 (uint64_t)(((int)hpep->php_pfmem_start) << 16), 551 (uint64_t)(((int)hpep->php_pfmem_size) << 16)); 552 res_cnt++; 553 } 554 } 555 return (res_cnt); 556 } 557 558 static uchar_t * 559 find_sig(uchar_t *cp, int len, char *sig) 560 { 561 long i; 562 563 /* Search for the "_MP_" or "$HRT" signature */ 564 for (i = 0; i < len; i += 16) { 565 if (cp[0] == sig[0] && cp[1] == sig[1] && 566 cp[2] == sig[2] && cp[3] == sig[3]) 567 return (cp); 568 cp += 16; 569 } 570 return (NULL); 571 } 572 573 static int 574 checksum(unsigned char *cp, int len) 575 { 576 int i; 577 unsigned int cksum; 578 579 for (i = cksum = 0; i < len; i++) 580 cksum += (unsigned int) *cp++; 581 582 return ((int)(cksum & 0xFF)); 583 } 584 585 #ifdef UNUSED_BUS_HIERARY_INFO 586 587 /* 588 * At this point, the bus hierarchy entries do not appear to 589 * provide anything we can't find out from PCI config space. 590 * The only interesting bit is the ISA bus number, which we 591 * don't care. 592 */ 593 int 594 mps_find_parent_bus(int bus) 595 { 596 struct sasm *sasmp; 597 uchar_t *extp; 598 599 if (mps_extp == NULL) 600 return (-1); 601 602 extp = mps_extp; 603 while (extp < mps_ext_endp) { 604 bhdp = (struct bhd *)extp; 605 switch (*extp) { 606 case SYS_AS_MAPPING: 607 extp += SYS_AS_MAPPING_SIZE; 608 break; 609 case BUS_HIERARCHY_DESC: 610 if (bhdp->bhd_bus_id == bus) 611 return (bhdp->bhd_parent); 612 extp += BUS_HIERARCHY_DESC_SIZE; 613 break; 614 case COMP_BUS_AS_MODIFIER: 615 extp += COMP_BUS_AS_MODIFIER_SIZE; 616 break; 617 default: 618 cmn_err(CE_WARN, "Unknown descriptor type %d" 619 " in BIOS Multiprocessor Spec table.", 620 *extp); 621 return (-1); 622 } 623 } 624 return (-1); 625 } 626 627 int 628 hrt_find_bus_range(int bus) 629 { 630 int i, max_bus, sub_bus; 631 struct php_entry *hpep; 632 633 if (hrt_hpep == NULL || hrt_entry_cnt == 0) { 634 return (-1); 635 } 636 hpep = hrt_hpep; 637 max_bus = -1; 638 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 639 if (hpep->php_pri_bus != bus) 640 continue; 641 sub_bus = (int)hpep->php_subord_bus; 642 if (sub_bus > max_bus) 643 max_bus = sub_bus; 644 } 645 return (max_bus); 646 } 647 648 #endif /* UNUSED_BUS_HIERARY_INFO */ 649