From 34ff71eecd9320d511d491ea54a86f0725a409f3 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 15 Jul 2011 21:08:58 +0000 Subject: [PATCH] Respect the BIOS/firmware's notion of acceptable address ranges for PCI resource allocation on x86 platforms: - Add a new helper API that Host-PCI bridge drivers can use to restrict resource allocation requests to a set of address ranges for different resource types. - For the ACPI Host-PCI bridge driver, use Producer address range resources in _CRS to enumerate valid address ranges for a given Host-PCI bridge. This can be disabled by including "hostres" in the debug.acpi.disabled tunable. - For the MPTable Host-PCI bridge driver, use entries in the extended MPTable to determine the valid address ranges for a given Host-PCI bridge. This required adding code to parse extended table entries. Similar to the new PCI-PCI bridge driver, these changes are only enabled if the NEW_PCIB kernel option is enabled (which is enabled by default on amd64 and i386). Approved by: re (kib) --- sys/dev/acpica/acpi_pcib_acpi.c | 166 ++++++++++++++++++++++++++++++ sys/dev/pci/pci_pci.c | 12 --- sys/dev/pci/pci_subr.c | 157 ++++++++++++++++++++++++++++- sys/dev/pci/pcib_private.h | 28 ++++++ sys/sys/bus.h | 1 + sys/x86/include/mptable.h | 62 +++++++++++- sys/x86/x86/mptable.c | 173 +++++++++++++++++++++++++++++++- sys/x86/x86/mptable_pci.c | 85 +++++++++++++++- 8 files changed, 666 insertions(+), 18 deletions(-) diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c index 73b2f70d5cf9..d3d499e47185 100644 --- a/sys/dev/acpica/acpi_pcib_acpi.c +++ b/sys/dev/acpica/acpi_pcib_acpi.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -62,6 +63,9 @@ struct acpi_hpcib_softc { int ap_bus; /* bios-assigned bus number */ ACPI_BUFFER ap_prt; /* interrupt routing table */ +#ifdef NEW_PCIB + struct pcib_host_resources ap_host_res; +#endif }; static int acpi_pcib_acpi_probe(device_t bus); @@ -87,6 +91,11 @@ static struct resource *acpi_pcib_acpi_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); +#ifdef NEW_PCIB +static int acpi_pcib_acpi_adjust_resource(device_t dev, + device_t child, int type, struct resource *r, + u_long start, u_long end); +#endif static device_method_t acpi_pcib_acpi_methods[] = { /* Device interface */ @@ -101,7 +110,11 @@ static device_method_t acpi_pcib_acpi_methods[] = { DEVMETHOD(bus_read_ivar, acpi_pcib_read_ivar), DEVMETHOD(bus_write_ivar, acpi_pcib_write_ivar), DEVMETHOD(bus_alloc_resource, acpi_pcib_acpi_alloc_resource), +#ifdef NEW_PCIB + DEVMETHOD(bus_adjust_resource, acpi_pcib_acpi_adjust_resource), +#else DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), +#endif DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), @@ -149,6 +162,120 @@ acpi_pcib_acpi_probe(device_t dev) return (0); } +#ifdef NEW_PCIB +static ACPI_STATUS +acpi_pcib_producer_handler(ACPI_RESOURCE *res, void *context) +{ + struct acpi_hpcib_softc *sc; + UINT64 length, min, max; + u_int flags; + int error, type; + + sc = context; + switch (res->Type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + panic("host bridge has depenedent resources"); + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + if (res->Data.Address.ProducerConsumer != ACPI_PRODUCER) + break; + switch (res->Type) { + case ACPI_RESOURCE_TYPE_ADDRESS16: + min = res->Data.Address16.Minimum; + max = res->Data.Address16.Maximum; + length = res->Data.Address16.AddressLength; + break; + case ACPI_RESOURCE_TYPE_ADDRESS32: + min = res->Data.Address32.Minimum; + max = res->Data.Address32.Maximum; + length = res->Data.Address32.AddressLength; + break; + case ACPI_RESOURCE_TYPE_ADDRESS64: + min = res->Data.Address64.Minimum; + max = res->Data.Address64.Maximum; + length = res->Data.Address64.AddressLength; + break; + default: + KASSERT(res->Type == + ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64, + ("should never happen")); + min = res->Data.ExtAddress64.Minimum; + max = res->Data.ExtAddress64.Maximum; + length = res->Data.ExtAddress64.AddressLength; + break; + } + if (length == 0 || + res->Data.Address.MinAddressFixed != ACPI_ADDRESS_FIXED || + res->Data.Address.MaxAddressFixed != ACPI_ADDRESS_FIXED) + break; + flags = 0; + switch (res->Data.Address.ResourceType) { + case ACPI_MEMORY_RANGE: + type = SYS_RES_MEMORY; + if (res->Type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64) { + if (res->Data.Address.Info.Mem.Caching == + ACPI_PREFETCHABLE_MEMORY) + flags |= RF_PREFETCHABLE; + } else { + /* + * XXX: Parse prefetch flag out of + * TypeSpecific. + */ + } + break; + case ACPI_IO_RANGE: + type = SYS_RES_IOPORT; + break; +#ifdef PCI_RES_BUS + case ACPI_BUS_NUMBER_RANGE: + type = PCI_RES_BUS; + break; +#endif + default: + return (AE_OK); + } + + /* XXX: Not sure this is correct? */ + if (res->Data.Address.Decode != ACPI_POS_DECODE) { + device_printf(sc->ap_dev, + "Ignoring %d range (%#jx-%#jx) due to negative decode\n", + type, (uintmax_t)min, (uintmax_t)max); + break; + } + if (min + length - 1 != max) + device_printf(sc->ap_dev, + "Length mismatch for %d range: %jx vs %jx\n", type, + (uintmax_t)max - min + 1, (uintmax_t)length); +#ifdef __i386__ + if (min > ULONG_MAX) { + device_printf(sc->ap_dev, + "Ignoring %d range above 4GB (%#jx-%#jx)\n", + type, (uintmax_t)min, (uintmax_t)max); + break; + } + if (max > ULONG_MAX) { + device_printf(sc->ap_dev, + "Truncating end of %d range above 4GB (%#jx-%#jx)\n", + type, (uintmax_t)min, (uintmax_t)max); + max = ULONG_MAX; + } +#endif + error = pcib_host_res_decodes(&sc->ap_host_res, type, min, max, + flags); + if (error) + panic("Failed to manage %d range (%#jx-%#jx): %d", + type, (uintmax_t)min, (uintmax_t)max, error); + break; + default: + break; + } + return (AE_OK); +} +#endif + static int acpi_pcib_acpi_attach(device_t dev) { @@ -179,6 +306,22 @@ acpi_pcib_acpi_attach(device_t dev) sc->ap_segment = 0; } +#ifdef NEW_PCIB + /* + * Determine which address ranges this bridge decodes and setup + * resource managers for those ranges. + */ + if (pcib_host_res_init(sc->ap_dev, &sc->ap_host_res) != 0) + panic("failed to init hostb resources"); + if (!acpi_disabled("hostres")) { + status = AcpiWalkResources(sc->ap_handle, "_CRS", + acpi_pcib_producer_handler, sc); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) + device_printf(sc->ap_dev, "failed to parse resources: %s\n", + AcpiFormatException(status)); + } +#endif + /* * Get our base bus number by evaluating _BBN. * If this doesn't work, we assume we're bus number 0. @@ -361,10 +504,33 @@ struct resource * acpi_pcib_acpi_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { +#ifdef NEW_PCIB + struct acpi_hpcib_softc *sc; +#endif #if defined(__i386__) || defined(__amd64__) start = hostb_alloc_start(type, start, end, count); #endif + +#ifdef NEW_PCIB + sc = device_get_softc(dev); + return (pcib_host_res_alloc(&sc->ap_host_res, child, type, rid, start, end, + count, flags)); +#else return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); +#endif } + +#ifdef NEW_PCIB +int +acpi_pcib_acpi_adjust_resource(device_t dev, device_t child, int type, + struct resource *r, u_long start, u_long end) +{ + struct acpi_hpcib_softc *sc; + + sc = device_get_softc(dev); + return (pcib_host_res_adjust(&sc->ap_host_res, child, type, r, start, + end)); +} +#endif diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index 5cbfb4d043a3..fa14816792d3 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -767,18 +767,6 @@ pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) } #ifdef NEW_PCIB -static const char * -pcib_child_name(device_t child) -{ - static char buf[64]; - - if (device_get_nameunit(child) != NULL) - return (device_get_nameunit(child)); - snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child), - pci_get_bus(child), pci_get_slot(child), pci_get_function(child)); - return (buf); -} - /* * Attempt to allocate a resource from the existing resources assigned * to a window. diff --git a/sys/dev/pci/pci_subr.c b/sys/dev/pci/pci_subr.c index 7f0c806f8fea..2d1689689967 100644 --- a/sys/dev/pci/pci_subr.c +++ b/sys/dev/pci/pci_subr.c @@ -33,9 +33,10 @@ __FBSDID("$FreeBSD$"); * provide PCI domains. */ -#include +#include #include #include +#include #include #include @@ -128,3 +129,157 @@ host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func, return 1; } + +#ifdef NEW_PCIB +/* + * Return a pointer to a pretty name for a PCI device. If the device + * has a driver attached, the device's name is used, otherwise a name + * is generated from the device's PCI address. + */ +const char * +pcib_child_name(device_t child) +{ + static char buf[64]; + + if (device_get_nameunit(child) != NULL) + return (device_get_nameunit(child)); + snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child), + pci_get_bus(child), pci_get_slot(child), pci_get_function(child)); + return (buf); +} + +/* + * Some Host-PCI bridge drivers know which resource ranges they can + * decode and should only allocate subranges to child PCI devices. + * This API provides a way to manage this. The bridge drive should + * initialize this structure during attach and call + * pcib_host_res_decodes() on each resource range it decodes. It can + * then use pcib_host_res_alloc() and pcib_host_res_adjust() as helper + * routines for BUS_ALLOC_RESOURCE() and BUS_ADJUST_RESOURCE(). This + * API assumes that resources for any decoded ranges can be safely + * allocated from the parent via bus_generic_alloc_resource(). + */ +int +pcib_host_res_init(device_t pcib, struct pcib_host_resources *hr) +{ + + hr->hr_pcib = pcib; + resource_list_init(&hr->hr_rl); + return (0); +} + +int +pcib_host_res_free(device_t pcib, struct pcib_host_resources *hr) +{ + + resource_list_free(&hr->hr_rl); + return (0); +} + +int +pcib_host_res_decodes(struct pcib_host_resources *hr, int type, u_long start, + u_long end, u_int flags) +{ + struct resource_list_entry *rle; + int rid; + + if (bootverbose) + device_printf(hr->hr_pcib, "decoding %d %srange %#lx-%#lx\n", + type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start, + end); + rid = resource_list_add_next(&hr->hr_rl, type, start, end, + end - start + 1); + if (flags & RF_PREFETCHABLE) { + KASSERT(type == SYS_RES_MEMORY, + ("only memory is prefetchable")); + rle = resource_list_find(&hr->hr_rl, type, rid); + rle->flags = RLE_PREFETCH; + } + return (0); +} + +struct resource * +pcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type, + int *rid, u_long start, u_long end, u_long count, u_int flags) +{ + struct resource_list_entry *rle; + struct resource *r; + u_long new_start, new_end; + + if (flags & RF_PREFETCHABLE) + KASSERT(type == SYS_RES_MEMORY, + ("only memory is prefetchable")); + + rle = resource_list_find(&hr->hr_rl, type, 0); + if (rle == NULL) { + /* + * No decoding ranges for this resource type, just pass + * the request up to the parent. + */ + return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid, + start, end, count, flags)); + } + +restart: + /* Try to allocate from each decoded range. */ + for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) { + if (rle->type != type) + continue; + if (((flags & RF_PREFETCHABLE) != 0) != + ((rle->flags & RLE_PREFETCH) != 0)) + continue; + new_start = ulmax(start, rle->start); + new_end = ulmin(end, rle->end); + if (new_start > new_end || + new_start + count - 1 > new_end || + new_start + count < new_start) + continue; + r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid, + new_start, new_end, count, flags); + if (r != NULL) { + if (bootverbose) + device_printf(hr->hr_pcib, + "allocated type %d (%#lx-%#lx) for rid %x of %s\n", + type, rman_get_start(r), rman_get_end(r), + *rid, pcib_child_name(dev)); + return (r); + } + } + + /* + * If we failed to find a prefetch range for a memory + * resource, try again without prefetch. + */ + if (flags & RF_PREFETCHABLE) { + flags &= ~RF_PREFETCHABLE; + rle = resource_list_find(&hr->hr_rl, type, 0); + goto restart; + } + return (NULL); +} + +int +pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type, + struct resource *r, u_long start, u_long end) +{ + struct resource_list_entry *rle; + + rle = resource_list_find(&hr->hr_rl, type, 0); + if (rle == NULL) { + /* + * No decoding ranges for this resource type, just pass + * the request up to the parent. + */ + return (bus_generic_adjust_resource(hr->hr_pcib, dev, type, r, + start, end)); + } + + /* Only allow adjustments that stay within a decoded range. */ + for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) { + if (rle->start <= start && rle->end >= end) + return (bus_generic_adjust_resource(hr->hr_pcib, dev, + type, r, start, end)); + } + return (ERANGE); +} +#endif /* NEW_PCIB */ diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h index 1574deb752b3..056158138723 100644 --- a/sys/dev/pci/pcib_private.h +++ b/sys/dev/pci/pcib_private.h @@ -33,6 +33,31 @@ #ifndef __PCIB_PRIVATE_H__ #define __PCIB_PRIVATE_H__ +#ifdef NEW_PCIB +/* + * Data structure and routines that Host to PCI bridge drivers can use + * to restrict allocations for child devices to ranges decoded by the + * bridge. + */ +struct pcib_host_resources { + device_t hr_pcib; + struct resource_list hr_rl; +}; + +int pcib_host_res_init(device_t pcib, + struct pcib_host_resources *hr); +int pcib_host_res_free(device_t pcib, + struct pcib_host_resources *hr); +int pcib_host_res_decodes(struct pcib_host_resources *hr, int type, + u_long start, u_long end, u_int flags); +struct resource *pcib_host_res_alloc(struct pcib_host_resources *hr, + device_t dev, int type, int *rid, u_long start, u_long end, + u_long count, u_int flags); +int pcib_host_res_adjust(struct pcib_host_resources *hr, + device_t dev, int type, struct resource *r, u_long start, + u_long end); +#endif + /* * Export portions of generic PCI:PCI bridge support so that it can be * used by subclasses. @@ -90,6 +115,9 @@ struct pcib_softc typedef uint32_t pci_read_config_fn(int b, int s, int f, int reg, int width); +#ifdef NEW_PCIB +const char *pcib_child_name(device_t child); +#endif int host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func, uint8_t *busnum); int pcib_attach(device_t dev); diff --git a/sys/sys/bus.h b/sys/sys/bus.h index e02bdd34596d..09b8911eabec 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -247,6 +247,7 @@ STAILQ_HEAD(resource_list, resource_list_entry); #define RLE_RESERVED 0x0001 /* Reserved by the parent bus. */ #define RLE_ALLOCATED 0x0002 /* Reserved resource is allocated. */ +#define RLE_PREFETCH 0x0004 /* Resource is a prefetch range. */ void resource_list_init(struct resource_list *rl); void resource_list_free(struct resource_list *rl); diff --git a/sys/x86/include/mptable.h b/sys/x86/include/mptable.h index 451d9ce14d55..08fc927e469f 100644 --- a/sys/x86/include/mptable.h +++ b/sys/x86/include/mptable.h @@ -72,6 +72,8 @@ typedef struct MPCTH { u_char reserved; } *mpcth_t; +/* Base table entries */ + #define MPCT_ENTRY_PROCESSOR 0 #define MPCT_ENTRY_BUS 1 #define MPCT_ENTRY_IOAPIC 2 @@ -132,7 +134,56 @@ typedef struct INTENTRY { #define INTENTRY_FLAGS_TRIGGER_EDGE 0x4 #define INTENTRY_FLAGS_TRIGGER_LEVEL 0xc -/* descriptions of MP basetable entries */ +/* Extended table entries */ + +typedef struct EXTENTRY { + u_char type; + u_char length; +} *ext_entry_ptr; + +#define MPCT_EXTENTRY_SAS 0x80 +#define MPCT_EXTENTRY_BHD 0x81 +#define MPCT_EXTENTRY_CBASM 0x82 + +typedef struct SASENTRY { + u_char type; + u_char length; + u_char bus_id; + u_char address_type; + uint64_t address_base; + uint64_t address_length; +} __attribute__((__packed__)) *sas_entry_ptr; + +#define SASENTRY_TYPE_IO 0 +#define SASENTRY_TYPE_MEMORY 1 +#define SASENTRY_TYPE_PREFETCH 2 + +typedef struct BHDENTRY { + u_char type; + u_char length; + u_char bus_id; + u_char bus_info; + u_char parent_bus; + u_char reserved[3]; +} *bhd_entry_ptr; + +#define BHDENTRY_INFO_SUBTRACTIVE_DECODE 0x1 + +typedef struct CBASMENTRY { + u_char type; + u_char length; + u_char bus_id; + u_char address_mod; + u_int predefined_range; +} *cbasm_entry_ptr; + +#define CBASMENTRY_ADDRESS_MOD_ADD 0x0 +#define CBASMENTRY_ADDRESS_MOD_SUBTRACT 0x1 + +#define CBASMENTRY_RANGE_ISA_IO 0 +#define CBASMENTRY_RANGE_VGA_IO 1 + +/* descriptions of MP table entries */ typedef struct BASETABLE_ENTRY { u_char type; u_char length; @@ -140,6 +191,15 @@ typedef struct BASETABLE_ENTRY { } basetable_entry; #ifdef _KERNEL +#ifdef NEW_PCIB +struct mptable_hostb_softc { + struct pcib_host_resources sc_host_res; + int sc_decodes_vga_io; + int sc_decodes_isa_io; +}; + +void mptable_pci_host_res_init(device_t pcib); +#endif int mptable_pci_probe_table(int bus); int mptable_pci_route_interrupt(device_t pcib, device_t dev, int pin); #endif diff --git a/sys/x86/x86/mptable.c b/sys/x86/x86/mptable.c index 4b240a6cb606..8dcd9ad88abe 100644 --- a/sys/x86/x86/mptable.c +++ b/sys/x86/x86/mptable.c @@ -32,22 +32,31 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#ifdef NEW_PCIB +#include +#endif #include #include #include +#include +#ifdef NEW_PCIB +#include +#endif #include #include #include #include #include #include +#ifdef NEW_PCIB +#include +#endif #include -#include - /* string defined by the Intel MP Spec as identifying the MP table */ #define MP_SIG 0x5f504d5f /* _MP_ */ @@ -67,6 +76,7 @@ __FBSDID("$FreeBSD$"); #define BIOS_COUNT (BIOS_SIZE/4) typedef void mptable_entry_handler(u_char *entry, void *arg); +typedef void mptable_extended_entry_handler(ext_entry_ptr entry, void *arg); static basetable_entry basetable_entry_types[] = { @@ -146,6 +156,7 @@ struct pci_route_interrupt_args { static mpfps_t mpfps; static mpcth_t mpct; +static ext_entry_ptr mpet; static void *ioapics[MAX_APIC_ID + 1]; static bus_datum *busses; static int mptable_nioapics, mptable_nbusses, mptable_maxbusid; @@ -181,6 +192,8 @@ static void mptable_probe_cpus_handler(u_char *entry, void *arg __unused); static void mptable_register(void *dummy); static int mptable_setup_local(void); static int mptable_setup_io(void); +static void mptable_walk_extended_table( + mptable_extended_entry_handler *handler, void *arg); static void mptable_walk_table(mptable_entry_handler *handler, void *arg); static int search_for_sig(u_int32_t target, int count); @@ -281,6 +294,11 @@ mptable_probe(void) __func__); return (ENXIO); } + if (mpct->extended_table_length != 0 && + mpct->extended_table_length + mpct->base_table_length + + (uintptr_t)mpfps->pap < 1024 * 1024) + mpet = (ext_entry_ptr)((char *)mpct + + mpct->base_table_length); if (mpct->signature[0] != 'P' || mpct->signature[1] != 'C' || mpct->signature[2] != 'M' || mpct->signature[3] != 'P') { printf("%s: MP Config Table has bad signature: %c%c%c%c\n", @@ -393,7 +411,7 @@ SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST, mptable_register, NULL); /* - * Call the handler routine for each entry in the MP config table. + * Call the handler routine for each entry in the MP config base table. */ static void mptable_walk_table(mptable_entry_handler *handler, void *arg) @@ -419,6 +437,25 @@ mptable_walk_table(mptable_entry_handler *handler, void *arg) } } +/* + * Call the handler routine for each entry in the MP config extended + * table. + */ +static void +mptable_walk_extended_table(mptable_extended_entry_handler *handler, void *arg) +{ + ext_entry_ptr end, entry; + + if (mpet == NULL) + return; + entry = mpet; + end = (ext_entry_ptr)((char *)mpet + mpct->extended_table_length); + while (entry < end) { + handler(entry, arg); + entry = (ext_entry_ptr)((char *)entry + entry->length); + } +} + static void mptable_probe_cpus_handler(u_char *entry, void *arg) { @@ -1053,3 +1090,133 @@ mptable_pci_route_interrupt(device_t pcib, device_t dev, int pin) 'A' + pin, args.vector); return (args.vector); } + +#ifdef NEW_PCIB +struct host_res_args { + struct mptable_hostb_softc *sc; + device_t dev; + u_char bus; +}; + +/* + * Initialize a Host-PCI bridge so it can restrict resource allocation + * requests to the resources it actually decodes according to MP + * config table extended entries. + */ +static void +mptable_host_res_handler(ext_entry_ptr entry, void *arg) +{ + struct host_res_args *args; + cbasm_entry_ptr cbasm; + sas_entry_ptr sas; + const char *name; + uint64_t start, end; + int error, *flagp, flags, type; + + args = arg; + switch (entry->type) { + case MPCT_EXTENTRY_SAS: + sas = (sas_entry_ptr)entry; + if (sas->bus_id != args->bus) + break; + switch (sas->address_type) { + case SASENTRY_TYPE_IO: + type = SYS_RES_IOPORT; + flags = 0; + break; + case SASENTRY_TYPE_MEMORY: + type = SYS_RES_MEMORY; + flags = 0; + break; + case SASENTRY_TYPE_PREFETCH: + type = SYS_RES_MEMORY; + flags = RF_PREFETCHABLE; + break; + default: + printf( + "MPTable: Unknown systems address space type for bus %u: %d\n", + sas->bus_id, sas->address_type); + return; + } + start = sas->address_base; + end = sas->address_base + sas->address_length - 1; +#ifdef __i386__ + if (start > ULONG_MAX) { + device_printf(args->dev, + "Ignoring %d range above 4GB (%#jx-%#jx)\n", + type, (uintmax_t)start, (uintmax_t)end); + break; + } + if (end > ULONG_MAX) { + device_printf(args->dev, + "Truncating end of %d range above 4GB (%#jx-%#jx)\n", + type, (uintmax_t)start, (uintmax_t)end); + end = ULONG_MAX; + } +#endif + error = pcib_host_res_decodes(&args->sc->sc_host_res, type, + start, end, flags); + if (error) + panic("Failed to manage %d range (%#jx-%#jx): %d", + type, (uintmax_t)start, (uintmax_t)end, error); + break; + case MPCT_EXTENTRY_CBASM: + cbasm = (cbasm_entry_ptr)entry; + if (cbasm->bus_id != args->bus) + break; + switch (cbasm->predefined_range) { + case CBASMENTRY_RANGE_ISA_IO: + flagp = &args->sc->sc_decodes_isa_io; + name = "ISA I/O"; + break; + case CBASMENTRY_RANGE_VGA_IO: + flagp = &args->sc->sc_decodes_vga_io; + name = "VGA I/O"; + break; + default: + printf( + "MPTable: Unknown compatiblity address space range for bus %u: %d\n", + cbasm->bus_id, cbasm->predefined_range); + return; + } + if (*flagp != 0) + printf( + "MPTable: Duplicate compatibility %s range for bus %u\n", + name, cbasm->bus_id); + switch (cbasm->address_mod) { + case CBASMENTRY_ADDRESS_MOD_ADD: + *flagp = 1; + if (bootverbose) + device_printf(args->dev, "decoding %s ports\n", + name); + break; + case CBASMENTRY_ADDRESS_MOD_SUBTRACT: + *flagp = -1; + if (bootverbose) + device_printf(args->dev, + "not decoding %s ports\n", name); + break; + default: + printf( + "MPTable: Unknown compatibility address space modifier: %u\n", + cbasm->address_mod); + break; + } + break; + } +} + +void +mptable_pci_host_res_init(device_t pcib) +{ + struct host_res_args args; + + KASSERT(pci0 != -1, ("do not know how to map PCI bus IDs")); + args.bus = pci_get_bus(pcib) + pci0; + args.dev = pcib; + args.sc = device_get_softc(pcib); + if (pcib_host_res_init(pcib, &args.sc->sc_host_res) != 0) + panic("failed to init hostb resources"); + mptable_walk_extended_table(mptable_host_res_handler, &args); +} +#endif diff --git a/sys/x86/x86/mptable_pci.c b/sys/x86/x86/mptable_pci.c index 48eec7f6563e..f0b90a80c3ac 100644 --- a/sys/x86/x86/mptable_pci.c +++ b/sys/x86/x86/mptable_pci.c @@ -69,6 +69,9 @@ static int mptable_hostb_attach(device_t dev) { +#ifdef NEW_PCIB + mptable_pci_host_res_init(dev); +#endif device_add_child(dev, "pci", pcib_get_bus(dev)); return (bus_generic_attach(dev)); } @@ -104,6 +107,80 @@ mptable_hostb_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data)); } +#ifdef NEW_PCIB +static int +mptable_is_isa_range(u_long start, u_long end) +{ + + if (end >= 0x10000) + return (0); + if ((start & 0xfc00) != (end & 0xfc00)) + return (0); + start &= ~0xfc00; + end &= ~0xfc00; + return (start >= 0x100 && end <= 0x3ff); +} + +static int +mptable_is_vga_range(u_long start, u_long end) +{ + if (end >= 0x10000) + return (0); + if ((start & 0xfc00) != (end & 0xfc00)) + return (0); + start &= ~0xfc00; + end &= ~0xfc00; + return (pci_is_vga_ioport_range(start, end)); +} + +static struct resource * +mptable_hostb_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct mptable_hostb_softc *sc; + + sc = device_get_softc(dev); + if (type == SYS_RES_IOPORT && start + count - 1 == end) { + if (mptable_is_isa_range(start, end)) { + switch (sc->sc_decodes_isa_io) { + case -1: + return (NULL); + case 1: + return (bus_generic_alloc_resource(dev, child, + type, rid, start, end, count, flags)); + default: + break; + } + } + if (mptable_is_vga_range(start, end)) { + switch (sc->sc_decodes_vga_io) { + case -1: + return (NULL); + case 1: + return (bus_generic_alloc_resource(dev, child, + type, rid, start, end, count, flags)); + default: + break; + } + } + } + start = hostb_alloc_start(type, start, end, count); + return (pcib_host_res_alloc(&sc->sc_host_res, child, type, rid, start, + end, count, flags)); +} + +static int +mptable_hostb_adjust_resource(device_t dev, device_t child, int type, + struct resource *r, u_long start, u_long end) +{ + struct mptable_hostb_softc *sc; + + sc = device_get_softc(dev); + return (pcib_host_res_adjust(&sc->sc_host_res, child, type, r, start, + end)); +} +#endif + static device_method_t mptable_hostb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mptable_hostb_probe), @@ -116,8 +193,13 @@ static device_method_t mptable_hostb_methods[] = { DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, legacy_pcib_read_ivar), DEVMETHOD(bus_write_ivar, legacy_pcib_write_ivar), +#ifdef NEW_PCIB + DEVMETHOD(bus_alloc_resource, mptable_hostb_alloc_resource), + DEVMETHOD(bus_adjust_resource, mptable_hostb_adjust_resource), +#else DEVMETHOD(bus_alloc_resource, legacy_pcib_alloc_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), +#endif DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), @@ -140,7 +222,8 @@ static device_method_t mptable_hostb_methods[] = { static devclass_t hostb_devclass; -DEFINE_CLASS_0(pcib, mptable_hostb_driver, mptable_hostb_methods, 1); +DEFINE_CLASS_0(pcib, mptable_hostb_driver, mptable_hostb_methods, + sizeof(struct mptable_hostb_softc)); DRIVER_MODULE(mptable_pcib, legacy, mptable_hostb_driver, hostb_devclass, 0, 0); /* PCI to PCI bridge driver. */