Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 15 Jul 2011 21:08:58 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r224069 - in head/sys: dev/acpica dev/pci sys x86/include x86/x86
Message-ID:  <201107152108.p6FL8wON095998@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Fri Jul 15 21:08:58 2011
New Revision: 224069
URL: http://svn.freebsd.org/changeset/base/224069

Log:
  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)

Modified:
  head/sys/dev/acpica/acpi_pcib_acpi.c
  head/sys/dev/pci/pci_pci.c
  head/sys/dev/pci/pci_subr.c
  head/sys/dev/pci/pcib_private.h
  head/sys/sys/bus.h
  head/sys/x86/include/mptable.h
  head/sys/x86/x86/mptable.c
  head/sys/x86/x86/mptable_pci.c

Modified: head/sys/dev/acpica/acpi_pcib_acpi.c
==============================================================================
--- head/sys/dev/acpica/acpi_pcib_acpi.c	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/dev/acpica/acpi_pcib_acpi.c	Fri Jul 15 21:08:58 2011	(r224069)
@@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/kernel.h>
+#include <sys/limits.h>
 #include <sys/malloc.h>
 #include <sys/module.h>
 #include <sys/rman.h>
@@ -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_a
 			    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_me
     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

Modified: head/sys/dev/pci/pci_pci.c
==============================================================================
--- head/sys/dev/pci/pci_pci.c	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/dev/pci/pci_pci.c	Fri Jul 15 21:08:58 2011	(r224069)
@@ -767,18 +767,6 @@ pcib_write_ivar(device_t dev, device_t c
 }
 
 #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.

Modified: head/sys/dev/pci/pci_subr.c
==============================================================================
--- head/sys/dev/pci/pci_subr.c	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/dev/pci/pci_subr.c	Fri Jul 15 21:08:58 2011	(r224069)
@@ -33,9 +33,10 @@ __FBSDID("$FreeBSD$");
  * provide PCI domains.
  */
 
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/rman.h>
+#include <sys/systm.h>
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
@@ -128,3 +129,157 @@ host_pcib_get_busno(pci_read_config_fn r
 
 	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 */

Modified: head/sys/dev/pci/pcib_private.h
==============================================================================
--- head/sys/dev/pci/pcib_private.h	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/dev/pci/pcib_private.h	Fri Jul 15 21:08:58 2011	(r224069)
@@ -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);

Modified: head/sys/sys/bus.h
==============================================================================
--- head/sys/sys/bus.h	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/sys/bus.h	Fri Jul 15 21:08:58 2011	(r224069)
@@ -247,6 +247,7 @@ STAILQ_HEAD(resource_list, resource_list
 
 #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);

Modified: head/sys/x86/include/mptable.h
==============================================================================
--- head/sys/x86/include/mptable.h	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/x86/include/mptable.h	Fri Jul 15 21:08:58 2011	(r224069)
@@ -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

Modified: head/sys/x86/x86/mptable.c
==============================================================================
--- head/sys/x86/x86/mptable.c	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/x86/x86/mptable.c	Fri Jul 15 21:08:58 2011	(r224069)
@@ -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
 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_TUNABLE
     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
 	}
 }
 
+/*
+ * 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 pci
 		    '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

Modified: head/sys/x86/x86/mptable_pci.c
==============================================================================
--- head/sys/x86/x86/mptable_pci.c	Fri Jul 15 19:02:44 2011	(r224068)
+++ head/sys/x86/x86/mptable_pci.c	Fri Jul 15 21:08:58 2011	(r224069)
@@ -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, dev
 	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_met
 	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_met
 
 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. */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201107152108.p6FL8wON095998>