Properly account for prefetchable memory when a request is being made.
We allow the request to go through if it matches either a prefetchable or a non-prefetchable part of the bridge. We do not check to make sure it is the right kind of memory because most drivers to not yet properly set RF_PREFETCHABLE (only cardbus seems to do so, and I'm not entirely sure it does it right). RF_PREFETCHABLE was invented for cardbus, so hasn't been properly documented yet. This is still overridable by hw.pci.allow_unsupported_io_ranges, but the need for that is greatly reduced, especially for the nvida driver. Approved by: re Reviewed by: jhb and many testers Submitted by: Matt Emmerton (although this has been reworked somewhat)
This commit is contained in:
parent
903f444bed
commit
58312ce69b
@ -38,6 +38,8 @@
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <machine/resource.h>
|
||||
@ -252,7 +254,8 @@ pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
|
||||
/*
|
||||
* Is this a decoded ISA I/O port address? Note, we need to do the mask that
|
||||
* we do below because of the ISA alias addresses. I'm not 100% sure that
|
||||
* this is correct.
|
||||
* this is correct. Maybe the bridge needs to be subtractive decode for
|
||||
* this to work?
|
||||
*/
|
||||
static int
|
||||
pcib_is_isa_io(u_long start)
|
||||
@ -273,6 +276,33 @@ pcib_is_isa_mem(u_long start)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the prefetch window open (eg, can we allocate memory in it?)
|
||||
*/
|
||||
static int
|
||||
pcib_is_prefetch_open(struct pcib_softc *sc)
|
||||
{
|
||||
return (sc->pmembase > 0 && sc->pmembase < sc->pmemlimit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the nonprefetch window open (eg, can we allocate memory in it?)
|
||||
*/
|
||||
static int
|
||||
pcib_is_nonprefetch_open(struct pcib_softc *sc)
|
||||
{
|
||||
return (sc->membase > 0 && sc->membase < sc->memlimit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the io window open (eg, can we allocate ports in it?)
|
||||
*/
|
||||
static int
|
||||
pcib_is_io_open(struct pcib_softc *sc)
|
||||
{
|
||||
return (sc->iobase > 0 && sc->iobase < sc->iolimit);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to trap resource allocation requests and ensure that the bridge
|
||||
* is set up to, or capable of handling them.
|
||||
@ -282,6 +312,7 @@ pcib_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 pcib_softc *sc = device_get_softc(dev);
|
||||
int ok;
|
||||
|
||||
/*
|
||||
* If this is a "default" allocation against this rid, we can't work
|
||||
@ -294,19 +325,21 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
|
||||
} else {
|
||||
/*
|
||||
* Fail the allocation for this range if it's not supported.
|
||||
*
|
||||
* XXX we should probably just fix up the bridge decode and soldier on.
|
||||
*/
|
||||
switch (type) {
|
||||
case SYS_RES_IOPORT:
|
||||
ok = 1;
|
||||
if (!pcib_is_isa_io(start)) {
|
||||
ok = 0;
|
||||
if (pcib_is_io_open(sc))
|
||||
ok = (start >= sc->iobase && end <= sc->iolimit);
|
||||
if (!pci_allow_unsupported_io_range) {
|
||||
if (start < sc->iobase)
|
||||
start = sc->iobase;
|
||||
if (end > sc->iolimit)
|
||||
end = sc->iolimit;
|
||||
if (end < start)
|
||||
start = 0;
|
||||
if (!ok) {
|
||||
if (start < sc->iobase)
|
||||
start = sc->iobase;
|
||||
if (end > sc->iolimit)
|
||||
end = sc->iolimit;
|
||||
}
|
||||
} else {
|
||||
if (start < sc->iobase)
|
||||
printf("start (%lx) < sc->iobase (%x)\n", start,
|
||||
@ -318,12 +351,16 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
|
||||
printf("end (%lx) < start (%lx)\n", end, start);
|
||||
}
|
||||
}
|
||||
if (!pcib_is_isa_io(start) &&
|
||||
((start < sc->iobase) || (end > sc->iolimit))) {
|
||||
device_printf(dev, "device %s%d requested unsupported I/O range 0x%lx-0x%lx"
|
||||
" (decoding 0x%x-0x%x)\n",
|
||||
device_get_name(child), device_get_unit(child), start, end,
|
||||
sc->iobase, sc->iolimit);
|
||||
if (end < start) {
|
||||
start = 0;
|
||||
end = 0;
|
||||
ok = 0;
|
||||
}
|
||||
if (!ok) {
|
||||
device_printf(dev, "device %s%d requested unsupported I/O "
|
||||
"range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n",
|
||||
device_get_name(child), device_get_unit(child), start, end,
|
||||
sc->iobase, sc->iolimit);
|
||||
return (NULL);
|
||||
}
|
||||
if (bootverbose)
|
||||
@ -331,48 +368,76 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
|
||||
device_get_name(child), device_get_unit(child), start, end);
|
||||
break;
|
||||
|
||||
/*
|
||||
* XXX will have to decide whether the device making the request is asking
|
||||
* for prefetchable memory or not. If it's coming from another bridge
|
||||
* down the line, do we assume not, or ask the bridge to pass in another
|
||||
* flag as the request bubbles up?
|
||||
*/
|
||||
case SYS_RES_MEMORY:
|
||||
ok = 1;
|
||||
if (!pcib_is_isa_mem(start)) {
|
||||
ok = 0;
|
||||
if (pcib_is_nonprefetch_open(sc))
|
||||
ok = ok || (start >= sc->membase && end <= sc->memlimit);
|
||||
if (pcib_is_prefetch_open(sc))
|
||||
ok = ok || (start >= sc->pmembase && end <= sc->pmemlimit);
|
||||
if (!pci_allow_unsupported_io_range) {
|
||||
if (start < sc->membase && end >= sc->membase)
|
||||
start = sc->membase;
|
||||
if (end > sc->memlimit)
|
||||
end = sc->memlimit;
|
||||
if (end < start)
|
||||
start = 0;
|
||||
} else {
|
||||
if (start < sc->membase && end > sc->membase)
|
||||
printf("start (%lx) < sc->membase (%x)\n",
|
||||
start, sc->membase);
|
||||
if (end > sc->memlimit)
|
||||
printf("end (%lx) > sc->memlimit (%x)\n",
|
||||
end, sc->memlimit);
|
||||
if (!ok) {
|
||||
ok = 1;
|
||||
if (flags & RF_PREFETCHABLE) {
|
||||
if (pcib_is_prefetch_open(sc)) {
|
||||
if (start < sc->pmembase)
|
||||
start = sc->pmembase;
|
||||
if (end > sc->pmemlimit)
|
||||
end = sc->pmemlimit;
|
||||
} else {
|
||||
ok = 0;
|
||||
}
|
||||
} else { /* non-prefetchable */
|
||||
if (pcib_is_nonprefetch_open(sc)) {
|
||||
if (start < sc->membase)
|
||||
start = sc->membase;
|
||||
if (end > sc->memlimit)
|
||||
end = sc->memlimit;
|
||||
} else {
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!ok) {
|
||||
ok = 1; /* pci_allow_unsupported_ranges -> always ok */
|
||||
if (pcib_is_nonprefetch_open(sc)) {
|
||||
if (start < sc->membase)
|
||||
printf("start (%lx) < sc->membase (%x)\n",
|
||||
start, sc->membase);
|
||||
if (end > sc->memlimit)
|
||||
printf("end (%lx) > sc->memlimit (%x)\n",
|
||||
end, sc->memlimit);
|
||||
}
|
||||
if (pcib_is_prefetch_open(sc)) {
|
||||
if (start < sc->pmembase)
|
||||
printf("start (%lx) < sc->pmembase (%x)\n",
|
||||
start, sc->pmembase);
|
||||
if (end > sc->pmemlimit)
|
||||
printf("end (%lx) > sc->pmemlimit (%x)\n",
|
||||
end, sc->memlimit);
|
||||
}
|
||||
if (end < start)
|
||||
printf("end (%lx) < start (%lx)\n", end, start);
|
||||
}
|
||||
}
|
||||
if (!pcib_is_isa_mem(start) &&
|
||||
(((start < sc->membase) || (end > sc->memlimit)) &&
|
||||
((start < sc->pmembase) || (end > sc->pmemlimit)))) {
|
||||
if (bootverbose)
|
||||
device_printf(dev,
|
||||
"device %s%d requested unsupported memory range "
|
||||
"0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n",
|
||||
device_get_name(child), device_get_unit(child), start,
|
||||
end, sc->membase, sc->memlimit, sc->pmembase,
|
||||
sc->pmemlimit);
|
||||
if (!pci_allow_unsupported_io_range)
|
||||
return (NULL);
|
||||
if (end < start) {
|
||||
start = 0;
|
||||
end = 0;
|
||||
ok = 0;
|
||||
}
|
||||
if (!ok && bootverbose)
|
||||
device_printf(dev,
|
||||
"device %s%d requested unsupported memory range "
|
||||
"0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n",
|
||||
device_get_name(child), device_get_unit(child), start,
|
||||
end, sc->membase, sc->memlimit, sc->pmembase,
|
||||
sc->pmemlimit);
|
||||
if (!ok)
|
||||
return (NULL);
|
||||
if (bootverbose)
|
||||
device_printf(sc->dev, "device %s%d requested decoded memory range 0x%lx-0x%lx\n",
|
||||
device_get_name(child), device_get_unit(child), start, end);
|
||||
device_get_name(child), device_get_unit(child), start, end);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
Loading…
Reference in New Issue
Block a user