13446Smrj /* 23446Smrj * CDDL HEADER START 33446Smrj * 43446Smrj * The contents of this file are subject to the terms of the 58420SDana.Myers@Sun.COM * Common Development and Distribution License (the "License"). 68420SDana.Myers@Sun.COM * You may not use this file except in compliance with the License. 73446Smrj * 83446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93446Smrj * or http://www.opensolaris.org/os/licensing. 103446Smrj * See the License for the specific language governing permissions 113446Smrj * and limitations under the License. 123446Smrj * 133446Smrj * When distributing Covered Code, include this CDDL HEADER in each 143446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153446Smrj * If applicable, add the following below this CDDL HEADER, with the 163446Smrj * fields enclosed by brackets "[]" replaced with your own identifying 173446Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 183446Smrj * 193446Smrj * CDDL HEADER END 203446Smrj */ 213446Smrj /* 229428SDana.Myers@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 233446Smrj * Use is subject to license terms. 243446Smrj * 253446Smrj * pci_resource.c -- routines to retrieve available bus resources from 263446Smrj * the MP Spec. Table and Hotplug Resource Table 273446Smrj */ 283446Smrj 293446Smrj #include <sys/types.h> 303446Smrj #include <sys/memlist.h> 313446Smrj #include <sys/pci_impl.h> 323446Smrj #include <sys/systm.h> 333446Smrj #include <sys/cmn_err.h> 348420SDana.Myers@Sun.COM #include <sys/acpi/acpi.h> 358420SDana.Myers@Sun.COM #include <sys/acpica.h> 363446Smrj #include "mps_table.h" 373446Smrj #include "pcihrt.h" 383446Smrj 393446Smrj extern int pci_boot_debug; 408420SDana.Myers@Sun.COM extern int pci_bios_nbus; 413446Smrj #define dprintf if (pci_boot_debug) printf 423446Smrj 433446Smrj static int tbl_init = 0; 443446Smrj static uchar_t *mps_extp = NULL; 453446Smrj static uchar_t *mps_ext_endp = NULL; 463446Smrj static struct php_entry *hrt_hpep; 473446Smrj static int hrt_entry_cnt = 0; 488420SDana.Myers@Sun.COM static int acpi_cb_cnt = 0; 493446Smrj 503446Smrj static void mps_probe(void); 518420SDana.Myers@Sun.COM static void acpi_pci_probe(void); 523446Smrj static int mps_find_bus_res(int, int, struct memlist **); 533446Smrj static void hrt_probe(void); 543446Smrj static int hrt_find_bus_res(int, int, struct memlist **); 558420SDana.Myers@Sun.COM static int acpi_find_bus_res(int, int, struct memlist **); 563446Smrj static uchar_t *find_sig(uchar_t *cp, int len, char *sig); 573446Smrj static int checksum(unsigned char *cp, int len); 588420SDana.Myers@Sun.COM static ACPI_STATUS acpi_wr_cb(ACPI_RESOURCE *rp, void *context); 598420SDana.Myers@Sun.COM void bus_res_fini(void); 609428SDana.Myers@Sun.COM static void acpi_trim_bus_ranges(void); 618420SDana.Myers@Sun.COM 628420SDana.Myers@Sun.COM struct memlist *acpi_io_res[256]; 638420SDana.Myers@Sun.COM struct memlist *acpi_mem_res[256]; 648420SDana.Myers@Sun.COM struct memlist *acpi_pmem_res[256]; 658420SDana.Myers@Sun.COM struct memlist *acpi_bus_res[256]; 668420SDana.Myers@Sun.COM 678420SDana.Myers@Sun.COM /* 688420SDana.Myers@Sun.COM * -1 = attempt ACPI resource discovery 698420SDana.Myers@Sun.COM * 0 = don't attempt ACPI resource discovery 708420SDana.Myers@Sun.COM * 1 = ACPI resource discovery successful 718420SDana.Myers@Sun.COM */ 728420SDana.Myers@Sun.COM volatile int acpi_resource_discovery = -1; 733446Smrj 743446Smrj struct memlist * 753446Smrj find_bus_res(int bus, int type) 763446Smrj { 773446Smrj struct memlist *res = NULL; 783446Smrj 793446Smrj if (tbl_init == 0) { 803446Smrj tbl_init = 1; 818420SDana.Myers@Sun.COM acpi_pci_probe(); 823446Smrj hrt_probe(); 833446Smrj mps_probe(); 843446Smrj } 853446Smrj 868420SDana.Myers@Sun.COM if (acpi_find_bus_res(bus, type, &res) > 0) 878420SDana.Myers@Sun.COM return (res); 888420SDana.Myers@Sun.COM 893446Smrj if (hrt_find_bus_res(bus, type, &res) > 0) 903446Smrj return (res); 913446Smrj 923446Smrj (void) mps_find_bus_res(bus, type, &res); 933446Smrj return (res); 943446Smrj } 953446Smrj 968420SDana.Myers@Sun.COM 978420SDana.Myers@Sun.COM static void 988420SDana.Myers@Sun.COM acpi_pci_probe(void) 998420SDana.Myers@Sun.COM { 1008420SDana.Myers@Sun.COM ACPI_HANDLE ah; 1018420SDana.Myers@Sun.COM dev_info_t *dip; 1028420SDana.Myers@Sun.COM int bus; 1038420SDana.Myers@Sun.COM 1048420SDana.Myers@Sun.COM if (acpi_resource_discovery == 0) 1058420SDana.Myers@Sun.COM return; 1068420SDana.Myers@Sun.COM 1078420SDana.Myers@Sun.COM for (bus = 0; bus < pci_bios_nbus; bus++) { 1088420SDana.Myers@Sun.COM /* if no dip or no ACPI handle, no resources to discover */ 1098420SDana.Myers@Sun.COM dip = pci_bus_res[bus].dip; 1108420SDana.Myers@Sun.COM if ((dip == NULL) || 1118420SDana.Myers@Sun.COM (ACPI_FAILURE(acpica_get_handle(dip, &ah)))) 1128420SDana.Myers@Sun.COM continue; 1138420SDana.Myers@Sun.COM 1148420SDana.Myers@Sun.COM (void) AcpiWalkResources(ah, "_CRS", acpi_wr_cb, 1158420SDana.Myers@Sun.COM (void *)(uintptr_t)bus); 1168420SDana.Myers@Sun.COM } 1178420SDana.Myers@Sun.COM 1189428SDana.Myers@Sun.COM if (acpi_cb_cnt > 0) { 1198420SDana.Myers@Sun.COM acpi_resource_discovery = 1; 1209428SDana.Myers@Sun.COM acpi_trim_bus_ranges(); 1219428SDana.Myers@Sun.COM } 1229428SDana.Myers@Sun.COM } 1239428SDana.Myers@Sun.COM 1249428SDana.Myers@Sun.COM /* 1259428SDana.Myers@Sun.COM * Trim overlapping bus ranges in acpi_bus_res[] 1269428SDana.Myers@Sun.COM * Some BIOSes report root-bridges with bus ranges that 1279428SDana.Myers@Sun.COM * overlap, for example:"0..255" and "8..255". Lower-numbered 1289428SDana.Myers@Sun.COM * ranges are trimmed by upper-numbered ranges (so "0..255" would 1299428SDana.Myers@Sun.COM * be trimmed to "0..7", in the example). 1309428SDana.Myers@Sun.COM */ 1319428SDana.Myers@Sun.COM static void 1329428SDana.Myers@Sun.COM acpi_trim_bus_ranges() 1339428SDana.Myers@Sun.COM { 1349428SDana.Myers@Sun.COM struct memlist *ranges, *current; 1359428SDana.Myers@Sun.COM int bus; 1369428SDana.Myers@Sun.COM 1379428SDana.Myers@Sun.COM ranges = NULL; 1389428SDana.Myers@Sun.COM 1399428SDana.Myers@Sun.COM /* 1409428SDana.Myers@Sun.COM * Assumptions: 1419428SDana.Myers@Sun.COM * - there exists at most 1 bus range entry for each bus number 1429428SDana.Myers@Sun.COM * - there are no (broken) ranges that start at the same bus number 1439428SDana.Myers@Sun.COM */ 1449428SDana.Myers@Sun.COM for (bus = 0; bus < 256; bus++) { 1459428SDana.Myers@Sun.COM struct memlist *prev, *orig, *new; 1469428SDana.Myers@Sun.COM /* skip buses with no range entry */ 1479428SDana.Myers@Sun.COM if ((orig = acpi_bus_res[bus]) == NULL) 1489428SDana.Myers@Sun.COM continue; 1499428SDana.Myers@Sun.COM 1509428SDana.Myers@Sun.COM /* 1519428SDana.Myers@Sun.COM * create copy of existing range and overload 1529428SDana.Myers@Sun.COM * 'prev' pointer to link existing to new copy 1539428SDana.Myers@Sun.COM */ 1549428SDana.Myers@Sun.COM new = memlist_alloc(); 1559428SDana.Myers@Sun.COM new->address = orig->address; 1569428SDana.Myers@Sun.COM new->size = orig->size; 1579428SDana.Myers@Sun.COM new->prev = orig; 1589428SDana.Myers@Sun.COM 1599428SDana.Myers@Sun.COM /* sorted insertion of 'new' into ranges list */ 1609428SDana.Myers@Sun.COM for (current = ranges, prev = NULL; current != NULL; 1619428SDana.Myers@Sun.COM prev = current, current = current->next) 1629428SDana.Myers@Sun.COM if (new->address < current->address) 1639428SDana.Myers@Sun.COM break; 1649428SDana.Myers@Sun.COM 1659428SDana.Myers@Sun.COM if (prev == NULL) { 1669428SDana.Myers@Sun.COM /* place at beginning of (possibly) empty list */ 1679428SDana.Myers@Sun.COM new->next = ranges; 1689428SDana.Myers@Sun.COM ranges = new; 1699428SDana.Myers@Sun.COM } else { 1709428SDana.Myers@Sun.COM /* place in list (possibly at end) */ 1719428SDana.Myers@Sun.COM new->next = current; 1729428SDana.Myers@Sun.COM prev->next = new; 1739428SDana.Myers@Sun.COM } 1749428SDana.Myers@Sun.COM } 1759428SDana.Myers@Sun.COM 1769428SDana.Myers@Sun.COM /* scan the list, perform trimming */ 1779428SDana.Myers@Sun.COM current = ranges; 1789428SDana.Myers@Sun.COM while (current != NULL) { 1799428SDana.Myers@Sun.COM struct memlist *next = current->next; 1809428SDana.Myers@Sun.COM 1819428SDana.Myers@Sun.COM /* done when no range above current */ 1829428SDana.Myers@Sun.COM if (next == NULL) 1839428SDana.Myers@Sun.COM break; 1849428SDana.Myers@Sun.COM 1859428SDana.Myers@Sun.COM /* 1869428SDana.Myers@Sun.COM * trim size in original range element 1879428SDana.Myers@Sun.COM * (current->prev points to the original range) 1889428SDana.Myers@Sun.COM */ 1899428SDana.Myers@Sun.COM if ((current->address + current->size) > next->address) 1909428SDana.Myers@Sun.COM current->prev->size = next->address - current->address; 1919428SDana.Myers@Sun.COM 1929428SDana.Myers@Sun.COM current = next; 1939428SDana.Myers@Sun.COM } 1949428SDana.Myers@Sun.COM 1959428SDana.Myers@Sun.COM /* discard the list */ 1969428SDana.Myers@Sun.COM memlist_free_all(&ranges); /* OK if ranges == NULL */ 1978420SDana.Myers@Sun.COM } 1988420SDana.Myers@Sun.COM 1998420SDana.Myers@Sun.COM static int 2008420SDana.Myers@Sun.COM acpi_find_bus_res(int bus, int type, struct memlist **res) 2018420SDana.Myers@Sun.COM { 2028420SDana.Myers@Sun.COM 2038420SDana.Myers@Sun.COM switch (type) { 2048420SDana.Myers@Sun.COM case IO_TYPE: 2058420SDana.Myers@Sun.COM *res = acpi_io_res[bus]; 2068420SDana.Myers@Sun.COM break; 2078420SDana.Myers@Sun.COM case MEM_TYPE: 2088420SDana.Myers@Sun.COM *res = acpi_mem_res[bus]; 2098420SDana.Myers@Sun.COM break; 2108420SDana.Myers@Sun.COM case PREFETCH_TYPE: 2118420SDana.Myers@Sun.COM *res = acpi_pmem_res[bus]; 2128420SDana.Myers@Sun.COM break; 2138420SDana.Myers@Sun.COM case BUSRANGE_TYPE: 2148420SDana.Myers@Sun.COM *res = acpi_bus_res[bus]; 2158420SDana.Myers@Sun.COM break; 2168420SDana.Myers@Sun.COM default: 2178420SDana.Myers@Sun.COM *res = NULL; 2188420SDana.Myers@Sun.COM break; 2198420SDana.Myers@Sun.COM } 2208420SDana.Myers@Sun.COM 2218420SDana.Myers@Sun.COM /* memlist_count() treats NULL head as zero-length */ 2228420SDana.Myers@Sun.COM return (memlist_count(*res)); 2238420SDana.Myers@Sun.COM } 2248420SDana.Myers@Sun.COM 2258420SDana.Myers@Sun.COM void 2268420SDana.Myers@Sun.COM bus_res_fini(void) 2278420SDana.Myers@Sun.COM { 2288420SDana.Myers@Sun.COM int bus; 2298420SDana.Myers@Sun.COM 2308420SDana.Myers@Sun.COM for (bus = 0; bus < pci_bios_nbus; bus++) { 2318420SDana.Myers@Sun.COM memlist_free_all(&acpi_io_res[bus]); 2328420SDana.Myers@Sun.COM memlist_free_all(&acpi_mem_res[bus]); 2338420SDana.Myers@Sun.COM memlist_free_all(&acpi_pmem_res[bus]); 2348420SDana.Myers@Sun.COM memlist_free_all(&acpi_bus_res[bus]); 2358420SDana.Myers@Sun.COM } 2368420SDana.Myers@Sun.COM } 2378420SDana.Myers@Sun.COM 2388420SDana.Myers@Sun.COM 2398420SDana.Myers@Sun.COM struct memlist ** 2408420SDana.Myers@Sun.COM rlistpp(UINT8 t, UINT8 flags, int bus) 2418420SDana.Myers@Sun.COM { 2428420SDana.Myers@Sun.COM switch (t) { 2438420SDana.Myers@Sun.COM 2448420SDana.Myers@Sun.COM case ACPI_MEMORY_RANGE: 2458420SDana.Myers@Sun.COM /* is this really the best we've got? */ 2468420SDana.Myers@Sun.COM if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY) 2478420SDana.Myers@Sun.COM return (&acpi_pmem_res[bus]); 2488420SDana.Myers@Sun.COM else 2498420SDana.Myers@Sun.COM return (&acpi_mem_res[bus]); 2508420SDana.Myers@Sun.COM 2518420SDana.Myers@Sun.COM case ACPI_IO_RANGE: return &acpi_io_res[bus]; 2528420SDana.Myers@Sun.COM case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus]; 2538420SDana.Myers@Sun.COM } 2548420SDana.Myers@Sun.COM return ((struct memlist **)NULL); 2558420SDana.Myers@Sun.COM } 2568420SDana.Myers@Sun.COM 2578420SDana.Myers@Sun.COM 2588420SDana.Myers@Sun.COM ACPI_STATUS 2598420SDana.Myers@Sun.COM acpi_wr_cb(ACPI_RESOURCE *rp, void *context) 2608420SDana.Myers@Sun.COM { 2618420SDana.Myers@Sun.COM int bus = (intptr_t)context; 2628420SDana.Myers@Sun.COM 2638420SDana.Myers@Sun.COM /* ignore consumed resources */ 2648420SDana.Myers@Sun.COM if (rp->Data.Address.ProducerConsumer == 1) 2658420SDana.Myers@Sun.COM return (AE_OK); 2668420SDana.Myers@Sun.COM 2678420SDana.Myers@Sun.COM switch (rp->Type) { 2688420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_IRQ: 2698420SDana.Myers@Sun.COM /* never expect to see a PCI bus produce an Interrupt */ 2708420SDana.Myers@Sun.COM dprintf("%s\n", "IRQ"); 2718420SDana.Myers@Sun.COM break; 2728420SDana.Myers@Sun.COM 2738420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_DMA: 2748420SDana.Myers@Sun.COM /* never expect to see a PCI bus produce DMA */ 2758420SDana.Myers@Sun.COM dprintf("%s\n", "DMA"); 2768420SDana.Myers@Sun.COM break; 2778420SDana.Myers@Sun.COM 2788420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_START_DEPENDENT: 2798420SDana.Myers@Sun.COM dprintf("%s\n", "START_DEPENDENT"); 2808420SDana.Myers@Sun.COM break; 2818420SDana.Myers@Sun.COM 2828420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_END_DEPENDENT: 2838420SDana.Myers@Sun.COM dprintf("%s\n", "END_DEPENDENT"); 2848420SDana.Myers@Sun.COM break; 2858420SDana.Myers@Sun.COM 2868420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_IO: 2878420SDana.Myers@Sun.COM if (rp->Data.Io.AddressLength == 0) 2888420SDana.Myers@Sun.COM break; 2898420SDana.Myers@Sun.COM acpi_cb_cnt++; 2908420SDana.Myers@Sun.COM memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum, 2918420SDana.Myers@Sun.COM rp->Data.Io.AddressLength); 2928420SDana.Myers@Sun.COM break; 2938420SDana.Myers@Sun.COM 2948420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_FIXED_IO: 2958420SDana.Myers@Sun.COM /* only expect to see this as a consumer */ 2968420SDana.Myers@Sun.COM dprintf("%s\n", "FIXED_IO"); 2978420SDana.Myers@Sun.COM break; 2988420SDana.Myers@Sun.COM 2998420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_VENDOR: 3008420SDana.Myers@Sun.COM dprintf("%s\n", "VENDOR"); 3018420SDana.Myers@Sun.COM break; 3028420SDana.Myers@Sun.COM 3038420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_END_TAG: 3048420SDana.Myers@Sun.COM dprintf("%s\n", "END_TAG"); 3058420SDana.Myers@Sun.COM break; 3068420SDana.Myers@Sun.COM 3078420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_MEMORY24: 3088420SDana.Myers@Sun.COM /* only expect to see this as a consumer */ 3098420SDana.Myers@Sun.COM dprintf("%s\n", "MEMORY24"); 3108420SDana.Myers@Sun.COM break; 3118420SDana.Myers@Sun.COM 3128420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_MEMORY32: 3138420SDana.Myers@Sun.COM /* only expect to see this as a consumer */ 3148420SDana.Myers@Sun.COM dprintf("%s\n", "MEMORY32"); 3158420SDana.Myers@Sun.COM break; 3168420SDana.Myers@Sun.COM 3178420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 3188420SDana.Myers@Sun.COM /* only expect to see this as a consumer */ 3198420SDana.Myers@Sun.COM dprintf("%s\n", "FIXED_MEMORY32"); 3208420SDana.Myers@Sun.COM break; 3218420SDana.Myers@Sun.COM 3228420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_ADDRESS16: 3238420SDana.Myers@Sun.COM if (rp->Data.Address16.AddressLength == 0) 3248420SDana.Myers@Sun.COM break; 3258420SDana.Myers@Sun.COM acpi_cb_cnt++; 3268420SDana.Myers@Sun.COM memlist_insert(rlistpp(rp->Data.Address16.ResourceType, 3278420SDana.Myers@Sun.COM rp->Data.Address16.Info.TypeSpecific, bus), 3288420SDana.Myers@Sun.COM rp->Data.Address16.Minimum, 3298420SDana.Myers@Sun.COM rp->Data.Address16.AddressLength); 3308420SDana.Myers@Sun.COM break; 3318420SDana.Myers@Sun.COM 3328420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_ADDRESS32: 3338420SDana.Myers@Sun.COM if (rp->Data.Address32.AddressLength == 0) 3348420SDana.Myers@Sun.COM break; 3358420SDana.Myers@Sun.COM acpi_cb_cnt++; 3368420SDana.Myers@Sun.COM memlist_insert(rlistpp(rp->Data.Address32.ResourceType, 3378420SDana.Myers@Sun.COM rp->Data.Address32.Info.TypeSpecific, bus), 3388420SDana.Myers@Sun.COM rp->Data.Address32.Minimum, 3398420SDana.Myers@Sun.COM rp->Data.Address32.AddressLength); 3408420SDana.Myers@Sun.COM break; 3418420SDana.Myers@Sun.COM 3428420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_ADDRESS64: 343*10576SKerry.Shu@Sun.COM /* 344*10576SKerry.Shu@Sun.COM * We comment out this block because we currently cannot deal with 345*10576SKerry.Shu@Sun.COM * PCI 64-bit addresses. Will revisit this when we add PCI 64-bit MMIO 346*10576SKerry.Shu@Sun.COM * support. 347*10576SKerry.Shu@Sun.COM */ 348*10576SKerry.Shu@Sun.COM #if 0 3498420SDana.Myers@Sun.COM if (rp->Data.Address64.AddressLength == 0) 3508420SDana.Myers@Sun.COM break; 3518420SDana.Myers@Sun.COM acpi_cb_cnt++; 3528420SDana.Myers@Sun.COM memlist_insert(rlistpp(rp->Data.Address64.ResourceType, 3538420SDana.Myers@Sun.COM rp->Data.Address64.Info.TypeSpecific, bus), 3548420SDana.Myers@Sun.COM rp->Data.Address64.Minimum, 3558420SDana.Myers@Sun.COM rp->Data.Address64.AddressLength); 356*10576SKerry.Shu@Sun.COM #endif 3578420SDana.Myers@Sun.COM break; 3588420SDana.Myers@Sun.COM 3598420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: 360*10576SKerry.Shu@Sun.COM #if 0 /* Will revisit this when we add PCI 64-bit MMIO support */ 3618420SDana.Myers@Sun.COM if (rp->Data.ExtAddress64.AddressLength == 0) 3628420SDana.Myers@Sun.COM break; 3638420SDana.Myers@Sun.COM acpi_cb_cnt++; 3648420SDana.Myers@Sun.COM memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType, 3658420SDana.Myers@Sun.COM rp->Data.ExtAddress64.Info.TypeSpecific, bus), 3668420SDana.Myers@Sun.COM rp->Data.ExtAddress64.Minimum, 3678420SDana.Myers@Sun.COM rp->Data.ExtAddress64.AddressLength); 368*10576SKerry.Shu@Sun.COM #endif 3698420SDana.Myers@Sun.COM break; 3708420SDana.Myers@Sun.COM 3718420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 3728420SDana.Myers@Sun.COM /* never expect to see a PCI bus produce an Interrupt */ 3738420SDana.Myers@Sun.COM dprintf("%s\n", "EXTENDED_IRQ"); 3748420SDana.Myers@Sun.COM break; 3758420SDana.Myers@Sun.COM 3768420SDana.Myers@Sun.COM case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: 3778420SDana.Myers@Sun.COM /* never expect to see a PCI bus produce an GAS */ 3788420SDana.Myers@Sun.COM dprintf("%s\n", "GENERIC_REGISTER"); 3798420SDana.Myers@Sun.COM break; 3808420SDana.Myers@Sun.COM } 3818420SDana.Myers@Sun.COM 3828420SDana.Myers@Sun.COM return (AE_OK); 3838420SDana.Myers@Sun.COM } 3848420SDana.Myers@Sun.COM 3853446Smrj static void 3863446Smrj mps_probe() 3873446Smrj { 3883446Smrj uchar_t *extp; 3893446Smrj struct mps_fps_hdr *fpp = NULL; 3903446Smrj struct mps_ct_hdr *ctp; 3913446Smrj uintptr_t ebda_start, base_end; 3923446Smrj ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg; 3933446Smrj 3943446Smrj base_size = *((ushort_t *)(0x413)); 3953446Smrj ebda_seg = *((ushort_t *)(0x40e)); 3963446Smrj ebda_start = ((uint32_t)ebda_seg) << 4; 3973446Smrj if (ebda_seg != 0) { 3983446Smrj fpp = (struct mps_fps_hdr *)find_sig( 3993446Smrj (uchar_t *)ebda_start, 1024, "_MP_"); 4003446Smrj } 4013446Smrj if (fpp == NULL) { 4023446Smrj base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0; 4033446Smrj if (base_end_seg != ebda_seg) { 4043446Smrj base_end = ((uintptr_t)base_end_seg) << 4; 4053446Smrj fpp = (struct mps_fps_hdr *)find_sig( 4068420SDana.Myers@Sun.COM (uchar_t *)base_end, 1024, "_MP_"); 4073446Smrj } 4083446Smrj } 4093446Smrj if (fpp == NULL) { 4103446Smrj fpp = (struct mps_fps_hdr *)find_sig( 4113446Smrj (uchar_t *)0xF0000, 0x10000, "_MP_"); 4123446Smrj } 4133446Smrj 4143446Smrj if (fpp == NULL) { 4153446Smrj dprintf("MP Spec table doesn't exist"); 4163446Smrj return; 4173446Smrj } else { 4183446Smrj dprintf("Found MP Floating Pointer Structure at %p\n", 4193446Smrj (void *)fpp); 4203446Smrj } 4213446Smrj 4223446Smrj if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) { 4233446Smrj dprintf("MP Floating Pointer Structure checksum error"); 4243446Smrj return; 4253446Smrj } 4263446Smrj 4273446Smrj ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr; 4283446Smrj if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */ 4293446Smrj dprintf("MP Configuration Table signature is wrong"); 4303446Smrj return; 4313446Smrj } 4323446Smrj 4333446Smrj base_len = ctp->ct_len; 4343446Smrj if (checksum((uchar_t *)ctp, base_len) != 0) { 4353446Smrj dprintf("MP Configuration Table checksum error"); 4363446Smrj return; 4373446Smrj } 4383446Smrj if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */ 4393446Smrj dprintf("MP Spec 1.1 found - extended table doesn't exist"); 4403446Smrj return; 4413446Smrj } 4423446Smrj if ((ext_len = ctp->ct_ext_tbl_len) == 0) { 4433446Smrj dprintf("MP Spec 1.4 found - extended table doesn't exist"); 4443446Smrj return; 4453446Smrj } 4463446Smrj extp = (uchar_t *)ctp + base_len; 4473446Smrj if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) { 4483446Smrj dprintf("MP Extended Table checksum error"); 4493446Smrj return; 4503446Smrj } 4513446Smrj mps_extp = extp; 4523446Smrj mps_ext_endp = mps_extp + ext_len; 4533446Smrj } 4543446Smrj 4553446Smrj 4563446Smrj static int 4573446Smrj mps_find_bus_res(int bus, int type, struct memlist **res) 4583446Smrj { 4593446Smrj struct sasm *sasmp; 4603446Smrj uchar_t *extp; 4613446Smrj int res_cnt; 4623446Smrj 4633446Smrj if (mps_extp == NULL) 4643446Smrj return (0); 4653446Smrj extp = mps_extp; 4663446Smrj res_cnt = 0; 4673446Smrj while (extp < mps_ext_endp) { 4683446Smrj switch (*extp) { 4693446Smrj case SYS_AS_MAPPING: 4703446Smrj sasmp = (struct sasm *)extp; 4713446Smrj if (((int)sasmp->sasm_as_type) == type && 4723446Smrj ((int)sasmp->sasm_bus_id) == bus) { 4733446Smrj if (sasmp->sasm_as_base_hi != 0 || 4748420SDana.Myers@Sun.COM sasmp->sasm_as_len_hi != 0) { 4753446Smrj printf("64 bits address space\n"); 4763446Smrj extp += SYS_AS_MAPPING_SIZE; 4773446Smrj break; 4783446Smrj } 4793446Smrj memlist_insert(res, 4803446Smrj (uint64_t)sasmp->sasm_as_base, 4813446Smrj sasmp->sasm_as_len); 4823446Smrj res_cnt++; 4833446Smrj } 4843446Smrj extp += SYS_AS_MAPPING_SIZE; 4853446Smrj break; 4863446Smrj case BUS_HIERARCHY_DESC: 4873446Smrj extp += BUS_HIERARCHY_DESC_SIZE; 4883446Smrj break; 4893446Smrj case COMP_BUS_AS_MODIFIER: 4903446Smrj extp += COMP_BUS_AS_MODIFIER_SIZE; 4913446Smrj break; 4923446Smrj default: 4933446Smrj cmn_err(CE_WARN, "Unknown descriptor type %d" 4943446Smrj " in BIOS Multiprocessor Spec table.", 4953446Smrj *extp); 4963446Smrj while (*res) { 4973446Smrj struct memlist *tmp = *res; 4983446Smrj *res = tmp->next; 4993446Smrj memlist_free(tmp); 5003446Smrj } 5013446Smrj return (0); 5023446Smrj } 5033446Smrj } 5043446Smrj return (res_cnt); 5053446Smrj } 5063446Smrj 5073446Smrj static void 5083446Smrj hrt_probe() 5093446Smrj { 5103446Smrj struct hrt_hdr *hrtp; 5113446Smrj 5123446Smrj dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n"); 5133446Smrj if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000, 5143446Smrj 0x10000, "$HRT")) == NULL) { 5153446Smrj dprintf("NO PCI Hot-Plug Resource Table"); 5163446Smrj return; 5173446Smrj } 5183446Smrj dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp); 5193446Smrj if (hrtp->hrt_ver != 1) { 5203446Smrj dprintf("PCI Hot-Plug Resource Table version no. <> 1\n"); 5213446Smrj return; 5223446Smrj } 5233446Smrj hrt_entry_cnt = (int)hrtp->hrt_entry_cnt; 5243446Smrj dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt); 5253446Smrj hrt_hpep = (struct php_entry *)(hrtp + 1); 5263446Smrj } 5273446Smrj 5283446Smrj static int 5293446Smrj hrt_find_bus_res(int bus, int type, struct memlist **res) 5303446Smrj { 5313446Smrj int res_cnt, i; 5323446Smrj struct php_entry *hpep; 5333446Smrj 5343446Smrj if (hrt_hpep == NULL || hrt_entry_cnt == 0) 5353446Smrj return (0); 5363446Smrj hpep = hrt_hpep; 5373446Smrj res_cnt = 0; 5383446Smrj for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 5393446Smrj if (hpep->php_pri_bus != bus) 5403446Smrj continue; 5413446Smrj if (type == IO_TYPE) { 5423446Smrj if (hpep->php_io_start == 0 || hpep->php_io_size == 0) 5433446Smrj continue; 5443446Smrj memlist_insert(res, (uint64_t)hpep->php_io_start, 5453446Smrj (uint64_t)hpep->php_io_size); 5463446Smrj res_cnt++; 5473446Smrj } else if (type == MEM_TYPE) { 5483446Smrj if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0) 5493446Smrj continue; 5503446Smrj memlist_insert(res, 5513446Smrj (uint64_t)(((int)hpep->php_mem_start) << 16), 5523446Smrj (uint64_t)(((int)hpep->php_mem_size) << 16)); 5533446Smrj res_cnt++; 5543446Smrj } else if (type == PREFETCH_TYPE) { 5553446Smrj if (hpep->php_pfmem_start == 0 || 5563446Smrj hpep->php_pfmem_size == 0) 5573446Smrj continue; 5583446Smrj memlist_insert(res, 5593446Smrj (uint64_t)(((int)hpep->php_pfmem_start) << 16), 5603446Smrj (uint64_t)(((int)hpep->php_pfmem_size) << 16)); 5613446Smrj res_cnt++; 5623446Smrj } 5633446Smrj } 5643446Smrj return (res_cnt); 5653446Smrj } 5663446Smrj 5673446Smrj static uchar_t * 5683446Smrj find_sig(uchar_t *cp, int len, char *sig) 5693446Smrj { 5703446Smrj long i; 5713446Smrj 5723446Smrj /* Search for the "_MP_" or "$HRT" signature */ 5733446Smrj for (i = 0; i < len; i += 16) { 5743446Smrj if (cp[0] == sig[0] && cp[1] == sig[1] && 5753446Smrj cp[2] == sig[2] && cp[3] == sig[3]) 5763446Smrj return (cp); 5773446Smrj cp += 16; 5783446Smrj } 5793446Smrj return (NULL); 5803446Smrj } 5813446Smrj 5823446Smrj static int 5833446Smrj checksum(unsigned char *cp, int len) 5843446Smrj { 5853446Smrj int i; 5863446Smrj unsigned int cksum; 5873446Smrj 5883446Smrj for (i = cksum = 0; i < len; i++) 5893446Smrj cksum += (unsigned int) *cp++; 5903446Smrj 5913446Smrj return ((int)(cksum & 0xFF)); 5923446Smrj } 5933446Smrj 5943446Smrj #ifdef UNUSED_BUS_HIERARY_INFO 5953446Smrj 5963446Smrj /* 5973446Smrj * At this point, the bus hierarchy entries do not appear to 5983446Smrj * provide anything we can't find out from PCI config space. 5993446Smrj * The only interesting bit is the ISA bus number, which we 6003446Smrj * don't care. 6013446Smrj */ 6023446Smrj int 6033446Smrj mps_find_parent_bus(int bus) 6043446Smrj { 6053446Smrj struct sasm *sasmp; 6063446Smrj uchar_t *extp; 6073446Smrj 6083446Smrj if (mps_extp == NULL) 6093446Smrj return (-1); 6103446Smrj 6113446Smrj extp = mps_extp; 6123446Smrj while (extp < mps_ext_endp) { 6133446Smrj bhdp = (struct bhd *)extp; 6143446Smrj switch (*extp) { 6153446Smrj case SYS_AS_MAPPING: 6163446Smrj extp += SYS_AS_MAPPING_SIZE; 6173446Smrj break; 6183446Smrj case BUS_HIERARCHY_DESC: 6193446Smrj if (bhdp->bhd_bus_id == bus) 6203446Smrj return (bhdp->bhd_parent); 6213446Smrj extp += BUS_HIERARCHY_DESC_SIZE; 6223446Smrj break; 6233446Smrj case COMP_BUS_AS_MODIFIER: 6243446Smrj extp += COMP_BUS_AS_MODIFIER_SIZE; 6253446Smrj break; 6263446Smrj default: 6273446Smrj cmn_err(CE_WARN, "Unknown descriptor type %d" 6283446Smrj " in BIOS Multiprocessor Spec table.", 6293446Smrj *extp); 6303446Smrj return (-1); 6313446Smrj } 6323446Smrj } 6333446Smrj return (-1); 6343446Smrj } 6353446Smrj 6363446Smrj int 6373446Smrj hrt_find_bus_range(int bus) 6383446Smrj { 6393446Smrj int i, max_bus, sub_bus; 6403446Smrj struct php_entry *hpep; 6413446Smrj 6423446Smrj if (hrt_hpep == NULL || hrt_entry_cnt == 0) { 6433446Smrj return (-1); 6443446Smrj } 6453446Smrj hpep = hrt_hpep; 6463446Smrj max_bus = -1; 6473446Smrj for (i = 0; i < hrt_entry_cnt; i++, hpep++) { 6483446Smrj if (hpep->php_pri_bus != bus) 6493446Smrj continue; 6503446Smrj sub_bus = (int)hpep->php_subord_bus; 6513446Smrj if (sub_bus > max_bus) 6523446Smrj max_bus = sub_bus; 6533446Smrj } 6543446Smrj return (max_bus); 6553446Smrj } 6563446Smrj 6573446Smrj #endif /* UNUSED_BUS_HIERARY_INFO */ 658