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 2008 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 61 struct memlist *acpi_io_res[256]; 62 struct memlist *acpi_mem_res[256]; 63 struct memlist *acpi_pmem_res[256]; 64 struct memlist *acpi_bus_res[256]; 65 66 /* 67 * -1 = attempt ACPI resource discovery 68 * 0 = don't attempt ACPI resource discovery 69 * 1 = ACPI resource discovery successful 70 */ 71 volatile int acpi_resource_discovery = -1; 72 73 struct memlist * 74 find_bus_res(int bus, int type) 75 { 76 struct memlist *res = NULL; 77 78 if (tbl_init == 0) { 79 tbl_init = 1; 80 acpi_pci_probe(); 81 hrt_probe(); 82 mps_probe(); 83 } 84 85 if (acpi_find_bus_res(bus, type, &res) > 0) 86 return (res); 87 88 if (hrt_find_bus_res(bus, type, &res) > 0) 89 return (res); 90 91 (void) mps_find_bus_res(bus, type, &res); 92 return (res); 93 } 94 95 96 static void 97 acpi_pci_probe(void) 98 { 99 ACPI_HANDLE ah; 100 dev_info_t *dip; 101 int bus; 102 103 if (acpi_resource_discovery == 0) 104 return; 105 106 for (bus = 0; bus < pci_bios_nbus; bus++) { 107 /* if no dip or no ACPI handle, no resources to discover */ 108 dip = pci_bus_res[bus].dip; 109 if ((dip == NULL) || 110 (ACPI_FAILURE(acpica_get_handle(dip, &ah)))) 111 continue; 112 113 (void) AcpiWalkResources(ah, "_CRS", acpi_wr_cb, 114 (void *)(uintptr_t)bus); 115 } 116 117 if (acpi_cb_cnt > 0) 118 acpi_resource_discovery = 1; 119 } 120 121 static int 122 acpi_find_bus_res(int bus, int type, struct memlist **res) 123 { 124 125 switch (type) { 126 case IO_TYPE: 127 *res = acpi_io_res[bus]; 128 break; 129 case MEM_TYPE: 130 *res = acpi_mem_res[bus]; 131 break; 132 case PREFETCH_TYPE: 133 *res = acpi_pmem_res[bus]; 134 break; 135 case BUSRANGE_TYPE: 136 *res = acpi_bus_res[bus]; 137 break; 138 default: 139 *res = NULL; 140 break; 141 } 142 143 /* memlist_count() treats NULL head as zero-length */ 144 return (memlist_count(*res)); 145 } 146 147 void 148 bus_res_fini(void) 149 { 150 int bus; 151 152 for (bus = 0; bus < pci_bios_nbus; bus++) { 153 memlist_free_all(&acpi_io_res[bus]); 154 memlist_free_all(&acpi_mem_res[bus]); 155 memlist_free_all(&acpi_pmem_res[bus]); 156 memlist_free_all(&acpi_bus_res[bus]); 157 } 158 } 159 160 161 struct memlist ** 162 rlistpp(UINT8 t, UINT8 flags, int bus) 163 { 164 switch (t) { 165 166 case ACPI_MEMORY_RANGE: 167 /* is this really the best we've got? */ 168 if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY) 169 return (&acpi_pmem_res[bus]); 170 else 171 return (&acpi_mem_res[bus]); 172 173 case ACPI_IO_RANGE: return &acpi_io_res[bus]; 174 case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus]; 175 } 176 return ((struct memlist **)NULL); 177 } 178 179 180 ACPI_STATUS 181 acpi_wr_cb(ACPI_RESOURCE *rp, void *context) 182 { 183 int bus = (intptr_t)context; 184 185 /* ignore consumed resources */ 186 if (rp->Data.Address.ProducerConsumer == 1) 187 return (AE_OK); 188 189 switch (rp->Type) { 190 case ACPI_RESOURCE_TYPE_IRQ: 191 /* never expect to see a PCI bus produce an Interrupt */ 192 dprintf("%s\n", "IRQ"); 193 break; 194 195 case ACPI_RESOURCE_TYPE_DMA: 196 /* never expect to see a PCI bus produce DMA */ 197 dprintf("%s\n", "DMA"); 198 break; 199 200 case ACPI_RESOURCE_TYPE_START_DEPENDENT: 201 dprintf("%s\n", "START_DEPENDENT"); 202 break; 203 204 case ACPI_RESOURCE_TYPE_END_DEPENDENT: 205 dprintf("%s\n", "END_DEPENDENT"); 206 break; 207 208 case ACPI_RESOURCE_TYPE_IO: 209 if (rp->Data.Io.AddressLength == 0) 210 break; 211 acpi_cb_cnt++; 212 memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum, 213 rp->Data.Io.AddressLength); 214 break; 215 216 case ACPI_RESOURCE_TYPE_FIXED_IO: 217 /* only expect to see this as a consumer */ 218 dprintf("%s\n", "FIXED_IO"); 219 break; 220 221 case ACPI_RESOURCE_TYPE_VENDOR: 222 dprintf("%s\n", "VENDOR"); 223 break; 224 225 case ACPI_RESOURCE_TYPE_END_TAG: 226 dprintf("%s\n", "END_TAG"); 227 break; 228 229 case ACPI_RESOURCE_TYPE_MEMORY24: 230 /* only expect to see this as a consumer */ 231 dprintf("%s\n", "MEMORY24"); 232 break; 233 234 case ACPI_RESOURCE_TYPE_MEMORY32: 235 /* only expect to see this as a consumer */ 236 dprintf("%s\n", "MEMORY32"); 237 break; 238 239 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 240 /* only expect to see this as a consumer */ 241 dprintf("%s\n", "FIXED_MEMORY32"); 242 break; 243 244 case ACPI_RESOURCE_TYPE_ADDRESS16: 245 if (rp->Data.Address16.AddressLength == 0) 246 break; 247 acpi_cb_cnt++; 248 memlist_insert(rlistpp(rp->Data.Address16.ResourceType, 249 rp->Data.Address16.Info.TypeSpecific, bus), 250 rp->Data.Address16.Minimum, 251 rp->Data.Address16.AddressLength); 252 break; 253 254 case ACPI_RESOURCE_TYPE_ADDRESS32: 255 if (rp->Data.Address32.AddressLength == 0) 256 break; 257 acpi_cb_cnt++; 258 memlist_insert(rlistpp(rp->Data.Address32.ResourceType, 259 rp->Data.Address32.Info.TypeSpecific, bus), 260 rp->Data.Address32.Minimum, 261 rp->Data.Address32.AddressLength); 262 break; 263 264 case ACPI_RESOURCE_TYPE_ADDRESS64: 265 if (rp->Data.Address64.AddressLength == 0) 266 break; 267 acpi_cb_cnt++; 268 memlist_insert(rlistpp(rp->Data.Address64.ResourceType, 269 rp->Data.Address64.Info.TypeSpecific, bus), 270 rp->Data.Address64.Minimum, 271 rp->Data.Address64.AddressLength); 272 break; 273 274 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 275 if (rp->Data.ExtAddress64.AddressLength == 0) 276 break; 277 acpi_cb_cnt++; 278 memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType, 279 rp->Data.ExtAddress64.Info.TypeSpecific, bus), 280 rp->Data.ExtAddress64.Minimum, 281 rp->Data.ExtAddress64.AddressLength); 282 break; 283 284 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 285 /* never expect to see a PCI bus produce an Interrupt */ 286 dprintf("%s\n", "EXTENDED_IRQ"); 287 break; 288 289 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 290 /* never expect to see a PCI bus produce an GAS */ 291 dprintf("%s\n", "GENERIC_REGISTER"); 292 break; 293 } 294 295 return (AE_OK); 296 } 297 298 static void 299 mps_probe() 300 { 301 uchar_t *extp; 302 struct mps_fps_hdr *fpp = NULL; 303 struct mps_ct_hdr *ctp; 304 uintptr_t ebda_start, base_end; 305 ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg; 306 307 base_size = *((ushort_t *)(0x413)); 308 ebda_seg = *((ushort_t *)(0x40e)); 309 ebda_start = ((uint32_t)ebda_seg) << 4; 310 if (ebda_seg != 0) { 311 fpp = (struct mps_fps_hdr *)find_sig( 312 (uchar_t *)ebda_start, 1024, "_MP_"); 313 } 314 if (fpp == NULL) { 315 base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0; 316 if (base_end_seg != ebda_seg) { 317 base_end = ((uintptr_t)base_end_seg) << 4; 318 fpp = (struct mps_fps_hdr *)find_sig( 319 (uchar_t *)base_end, 1024, "_MP_"); 320 } 321 } 322 if (fpp == NULL) { 323 fpp = (struct mps_fps_hdr *)find_sig( 324 (uchar_t *)0xF0000, 0x10000, "_MP_"); 325 } 326 327 if (fpp == NULL) { 328 dprintf("MP Spec table doesn't exist"); 329 return; 330 } else { 331 dprintf("Found MP Floating Pointer Structure at %p\n", 332 (void *)fpp); 333 } 334 335 if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) { 336 dprintf("MP Floating Pointer Structure checksum error"); 337 return; 338 } 339 340 ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr; 341 if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */ 342 dprintf("MP Configuration Table signature is wrong"); 343 return; 344 } 345 346 base_len = ctp->ct_len; 347 if (checksum((uchar_t *)ctp, base_len) != 0) { 348 dprintf("MP Configuration Table checksum error"); 349 return; 350 } 351 if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */ 352 dprintf("MP Spec 1.1 found - extended table doesn't exist"); 353 return; 354 } 355 if ((ext_len = ctp->ct_ext_tbl_len) == 0) { 356 dprintf("MP Spec 1.4 found - extended table doesn't exist"); 357 return; 358 } 359 extp = (uchar_t *)ctp + base_len; 360 if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) { 361 dprintf("MP Extended Table checksum error"); 362 return; 363 } 364 mps_extp = extp; 365 mps_ext_endp = mps_extp + ext_len; 366 } 367 368 369 static int 370 mps_find_bus_res(int bus, int type, struct memlist **res) 371 { 372 struct sasm *sasmp; 373 uchar_t *extp; 374 int res_cnt; 375 376 if (mps_extp == NULL) 377 return (0); 378 extp = mps_extp; 379 res_cnt = 0; 380 while (extp < mps_ext_endp) { 381 switch (*extp) { 382 case SYS_AS_MAPPING: 383 sasmp = (struct sasm *)extp; 384 if (((int)sasmp->sasm_as_type) == type && 385 ((int)sasmp->sasm_bus_id) == bus) { 386 if (sasmp->sasm_as_base_hi != 0 || 387 sasmp->sasm_as_len_hi != 0) { 388 printf("64 bits address space\n"); 389 extp += SYS_AS_MAPPING_SIZE; 390 break; 391 } 392 memlist_insert(res, 393 (uint64_t)sasmp->sasm_as_base, 394 sasmp->sasm_as_len); 395 res_cnt++; 396 } 397 extp += SYS_AS_MAPPING_SIZE; 398 break; 399 case BUS_HIERARCHY_DESC: 400 extp += BUS_HIERARCHY_DESC_SIZE; 401 break; 402 case COMP_BUS_AS_MODIFIER: 403 extp += COMP_BUS_AS_MODIFIER_SIZE; 404 break; 405 default: 406 cmn_err(CE_WARN, "Unknown descriptor type %d" 407 " in BIOS Multiprocessor Spec table.", 408 *extp); 409 while (*res) { 410 struct memlist *tmp = *res; 411 *res = tmp->next; 412 memlist_free(tmp); 413 } 414 return (0); 415 } 416 } 417 return (res_cnt); 418 } 419 420 static void 421 hrt_probe() 422 { 423 struct hrt_hdr *hrtp; 424 425 dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n"); 426 if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000, 427 0x10000, "$HRT")) == NULL) { 428 dprintf("NO PCI Hot-Plug Resource Table"); 429 return; 430 } 431 dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp); 432 if (hrtp->hrt_ver != 1) { 433 dprintf("PCI Hot-Plug Resource Table version no. <> 1\n"); 434 return; 435 } 436 hrt_entry_cnt = (int)hrtp->hrt_entry_cnt; 437 dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt); 438 hrt_hpep = (struct php_entry *)(hrtp + 1); 439 } 440 441 static int 442 hrt_find_bus_res(int bus, int type, struct memlist **res) 443 { 444 int res_cnt, i; 445 struct php_entry *hpep; 446 447 if (hrt_hpep == NULL || hrt_entry_cnt == 0) 448 return (0); 449 hpep = hrt_hpep; 450 res_cnt = 0; 451 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 452 if (hpep->php_pri_bus != bus) 453 continue; 454 if (type == IO_TYPE) { 455 if (hpep->php_io_start == 0 || hpep->php_io_size == 0) 456 continue; 457 memlist_insert(res, (uint64_t)hpep->php_io_start, 458 (uint64_t)hpep->php_io_size); 459 res_cnt++; 460 } else if (type == MEM_TYPE) { 461 if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0) 462 continue; 463 memlist_insert(res, 464 (uint64_t)(((int)hpep->php_mem_start) << 16), 465 (uint64_t)(((int)hpep->php_mem_size) << 16)); 466 res_cnt++; 467 } else if (type == PREFETCH_TYPE) { 468 if (hpep->php_pfmem_start == 0 || 469 hpep->php_pfmem_size == 0) 470 continue; 471 memlist_insert(res, 472 (uint64_t)(((int)hpep->php_pfmem_start) << 16), 473 (uint64_t)(((int)hpep->php_pfmem_size) << 16)); 474 res_cnt++; 475 } 476 } 477 return (res_cnt); 478 } 479 480 static uchar_t * 481 find_sig(uchar_t *cp, int len, char *sig) 482 { 483 long i; 484 485 /* Search for the "_MP_" or "$HRT" signature */ 486 for (i = 0; i < len; i += 16) { 487 if (cp[0] == sig[0] && cp[1] == sig[1] && 488 cp[2] == sig[2] && cp[3] == sig[3]) 489 return (cp); 490 cp += 16; 491 } 492 return (NULL); 493 } 494 495 static int 496 checksum(unsigned char *cp, int len) 497 { 498 int i; 499 unsigned int cksum; 500 501 for (i = cksum = 0; i < len; i++) 502 cksum += (unsigned int) *cp++; 503 504 return ((int)(cksum & 0xFF)); 505 } 506 507 #ifdef UNUSED_BUS_HIERARY_INFO 508 509 /* 510 * At this point, the bus hierarchy entries do not appear to 511 * provide anything we can't find out from PCI config space. 512 * The only interesting bit is the ISA bus number, which we 513 * don't care. 514 */ 515 int 516 mps_find_parent_bus(int bus) 517 { 518 struct sasm *sasmp; 519 uchar_t *extp; 520 521 if (mps_extp == NULL) 522 return (-1); 523 524 extp = mps_extp; 525 while (extp < mps_ext_endp) { 526 bhdp = (struct bhd *)extp; 527 switch (*extp) { 528 case SYS_AS_MAPPING: 529 extp += SYS_AS_MAPPING_SIZE; 530 break; 531 case BUS_HIERARCHY_DESC: 532 if (bhdp->bhd_bus_id == bus) 533 return (bhdp->bhd_parent); 534 extp += BUS_HIERARCHY_DESC_SIZE; 535 break; 536 case COMP_BUS_AS_MODIFIER: 537 extp += COMP_BUS_AS_MODIFIER_SIZE; 538 break; 539 default: 540 cmn_err(CE_WARN, "Unknown descriptor type %d" 541 " in BIOS Multiprocessor Spec table.", 542 *extp); 543 return (-1); 544 } 545 } 546 return (-1); 547 } 548 549 int 550 hrt_find_bus_range(int bus) 551 { 552 int i, max_bus, sub_bus; 553 struct php_entry *hpep; 554 555 if (hrt_hpep == NULL || hrt_entry_cnt == 0) { 556 return (-1); 557 } 558 hpep = hrt_hpep; 559 max_bus = -1; 560 for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 561 if (hpep->php_pri_bus != bus) 562 continue; 563 sub_bus = (int)hpep->php_subord_bus; 564 if (sub_bus > max_bus) 565 max_bus = sub_bus; 566 } 567 return (max_bus); 568 } 569 570 #endif /* UNUSED_BUS_HIERARY_INFO */ 571