summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2011-07-15 21:08:58 +0000
committerjhb <jhb@FreeBSD.org>2011-07-15 21:08:58 +0000
commitb75d5a0ef9a633d11ad36f4e2e0cc915b287d36c (patch)
treecb42b98ebf988021855125779898a415b8209443 /sys/x86
parentd3eb43cf7d9826d90122c3bc76f9284edf7eae8d (diff)
downloadFreeBSD-src-b75d5a0ef9a633d11ad36f4e2e0cc915b287d36c.zip
FreeBSD-src-b75d5a0ef9a633d11ad36f4e2e0cc915b287d36c.tar.gz
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)
Diffstat (limited to 'sys/x86')
-rw-r--r--sys/x86/include/mptable.h62
-rw-r--r--sys/x86/x86/mptable.c173
-rw-r--r--sys/x86/x86/mptable_pci.c85
3 files changed, 315 insertions, 5 deletions
diff --git a/sys/x86/include/mptable.h b/sys/x86/include/mptable.h
index 451d9ce..08fc927 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 4b240a6..8dcd9ad 100644
--- a/sys/x86/x86/mptable.c
+++ b/sys/x86/x86/mptable.c
@@ -32,22 +32,31 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
+#include <sys/limits.h>
#include <sys/malloc.h>
+#ifdef NEW_PCIB
+#include <sys/rman.h>
+#endif
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
+#include <dev/pci/pcivar.h>
+#ifdef NEW_PCIB
+#include <dev/pci/pcib_private.h>
+#endif
#include <x86/apicreg.h>
#include <x86/mptable.h>
#include <machine/frame.h>
#include <machine/intr_machdep.h>
#include <machine/apicvar.h>
#include <machine/md_var.h>
+#ifdef NEW_PCIB
+#include <machine/resource.h>
+#endif
#include <machine/specialreg.h>
-#include <dev/pci/pcivar.h>
-
/* 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 @@ found:
__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 48eec7f..f0b90a8 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. */
OpenPOWER on IntegriCloud