aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2024-02-09 18:27:45 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2024-02-09 18:27:45 +0000
commitb377ff8110e3489eb6e6b920b51a2384dfc4eb0b (patch)
tree79aa10f759cfd201515afabe96521fcc6f431a1c
parent36efc64a6bc6318eefd9baf88cbf511e91db80c8 (diff)
downloadsrc-b377ff8110e3489eb6e6b920b51a2384dfc4eb0b.tar.gz
src-b377ff8110e3489eb6e6b920b51a2384dfc4eb0b.zip
pcib: Refine handling of resources allocated from bridge windows
Fix a long-standing layering violation in the original NEW_PCIB code by not passing suballocated resources up to the parent bus for activation and mapping. Instead, handle activation and mapping of sub-allocated resources in this driver. When mapping resources, request a mapping from a suitable sub-region of the resource allocated from the parent bus for the associated bridge window. Note that this does require passing RF_ACTIVE (with RF_UNMAPPED) when allocating bridge window resources from the parent. Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D43690
-rw-r--r--sys/dev/pci/pci_pci.c130
1 files changed, 126 insertions, 4 deletions
diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c
index 5286d4e82e53..02fa8cf1fb9e 100644
--- a/sys/dev/pci/pci_pci.c
+++ b/sys/dev/pci/pci_pci.c
@@ -66,6 +66,10 @@ static bus_alloc_resource_t pcib_alloc_resource;
#ifdef NEW_PCIB
static bus_adjust_resource_t pcib_adjust_resource;
static bus_release_resource_t pcib_release_resource;
+static bus_activate_resource_t pcib_activate_resource;
+static bus_deactivate_resource_t pcib_deactivate_resource;
+static bus_map_resource_t pcib_map_resource;
+static bus_unmap_resource_t pcib_unmap_resource;
#endif
static int pcib_reset_child(device_t dev, device_t child, int flags);
@@ -108,12 +112,16 @@ static device_method_t pcib_methods[] = {
#ifdef NEW_PCIB
DEVMETHOD(bus_adjust_resource, pcib_adjust_resource),
DEVMETHOD(bus_release_resource, pcib_release_resource),
+ DEVMETHOD(bus_activate_resource, pcib_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, pcib_deactivate_resource),
+ DEVMETHOD(bus_map_resource, pcib_map_resource),
+ DEVMETHOD(bus_unmap_resource, pcib_unmap_resource),
#else
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
-#endif
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+#endif
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
DEVMETHOD(bus_reset_child, pcib_reset_child),
@@ -381,7 +389,7 @@ alloc_ranges(rman_res_t start, rman_res_t end, void *arg)
device_printf(as->sc->dev,
"allocating non-ISA range %#jx-%#jx\n", start, end);
as->res[as->count] = bus_alloc_resource(as->sc->dev, SYS_RES_IOPORT,
- &rid, start, end, end - start + 1, 0);
+ &rid, start, end, end - start + 1, RF_ACTIVE | RF_UNMAPPED);
if (as->res[as->count] == NULL)
as->error = ENXIO;
else
@@ -454,7 +462,7 @@ pcib_alloc_window(struct pcib_softc *sc, struct pcib_window *w, int type,
else {
rid = w->reg;
res = bus_alloc_resource(sc->dev, type, &rid, w->base, w->limit,
- w->limit - w->base + 1, flags);
+ w->limit - w->base + 1, flags | RF_ACTIVE | RF_UNMAPPED);
if (res != NULL)
pcib_add_window_resources(w, &res, 1);
}
@@ -2001,7 +2009,7 @@ pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type,
count = roundup2(count, (rman_res_t)1 << w->step);
rid = w->reg;
res = bus_alloc_resource(sc->dev, type, &rid, start, end, count,
- flags & ~RF_ACTIVE);
+ flags | RF_ACTIVE | RF_UNMAPPED);
if (res == NULL)
return (ENOSPC);
pcib_add_window_resources(w, &res, 1);
@@ -2452,6 +2460,120 @@ pcib_release_resource(device_t dev, device_t child, int type, int rid,
}
return (bus_generic_release_resource(dev, child, type, rid, r));
}
+
+static int
+pcib_activate_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *r)
+{
+ struct pcib_softc *sc = device_get_softc(dev);
+ struct resource_map map;
+ int error;
+
+ if (!pcib_is_resource_managed(sc, type, r))
+ return (bus_generic_activate_resource(dev, child, type, rid,
+ r));
+
+ error = rman_activate_resource(r);
+ if (error != 0)
+ return (error);
+
+ if ((rman_get_flags(r) & RF_UNMAPPED) == 0 &&
+ (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT)) {
+ error = BUS_MAP_RESOURCE(dev, child, type, r, NULL, &map);
+ if (error != 0) {
+ rman_deactivate_resource(r);
+ return (error);
+ }
+
+ rman_set_mapping(r, &map);
+ }
+ return (0);
+}
+
+static int
+pcib_deactivate_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *r)
+{
+ struct pcib_softc *sc = device_get_softc(dev);
+ struct resource_map map;
+ int error;
+
+ if (!pcib_is_resource_managed(sc, type, r))
+ return (bus_generic_deactivate_resource(dev, child, type, rid,
+ r));
+
+ error = rman_deactivate_resource(r);
+ if (error != 0)
+ return (error);
+
+ if ((rman_get_flags(r) & RF_UNMAPPED) == 0 &&
+ (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT)) {
+ rman_get_mapping(r, &map);
+ BUS_UNMAP_RESOURCE(dev, child, type, r, &map);
+ }
+ return (0);
+}
+
+static struct resource *
+pcib_find_parent_resource(struct pcib_window *w, struct resource *r)
+{
+ for (int i = 0; i < w->count; i++) {
+ if (rman_get_start(w->res[i]) <= rman_get_start(r) &&
+ rman_get_end(w->res[i]) >= rman_get_end(r))
+ return (w->res[i]);
+ }
+ return (NULL);
+}
+
+static int
+pcib_map_resource(device_t dev, device_t child, int type, struct resource *r,
+ struct resource_map_request *argsp, struct resource_map *map)
+{
+ struct pcib_softc *sc = device_get_softc(dev);
+ struct resource_map_request args;
+ struct pcib_window *w;
+ struct resource *pres;
+ rman_res_t length, start;
+ int error;
+
+ w = pcib_get_resource_window(sc, type, r);
+ if (w == NULL)
+ return (bus_generic_map_resource(dev, child, type, r, argsp,
+ map));
+
+ /* Resources must be active to be mapped. */
+ if (!(rman_get_flags(r) & RF_ACTIVE))
+ return (ENXIO);
+
+ resource_init_map_request(&args);
+ error = resource_validate_map_request(r, argsp, &args, &start, &length);
+ if (error)
+ return (error);
+
+ pres = pcib_find_parent_resource(w, r);
+ if (pres == NULL)
+ return (ENOENT);
+
+ args.offset = start - rman_get_start(pres);
+ args.length = length;
+ return (bus_generic_map_resource(dev, child, type, pres, &args, map));
+}
+
+static int
+pcib_unmap_resource(device_t dev, device_t child, int type, struct resource *r,
+ struct resource_map *map)
+{
+ struct pcib_softc *sc = device_get_softc(dev);
+ struct pcib_window *w;
+
+ w = pcib_get_resource_window(sc, type, r);
+ if (w != NULL) {
+ r = pcib_find_parent_resource(w, r);
+ if (r == NULL)
+ return (ENOENT);
+ }
+ return (bus_generic_unmap_resource(dev, child, type, r, map));
+}
#else
/*
* We have to trap resource allocation requests and ensure that the bridge