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 */