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)
This commit is contained in:
parent
de8d750faa
commit
34ff71eecd
@ -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_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
|
||||
|
@ -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.
|
||||
|
@ -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 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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 @@ 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
|
||||
|
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user