git: 839374bbfe8b - main - Teach the GICv3 driver to translate memory ranges

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Fri, 23 Sep 2022 14:47:11 UTC
The branch main has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=839374bbfe8bdbc766156bf0129bf54958dc2d8b

commit 839374bbfe8bdbc766156bf0129bf54958dc2d8b
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2022-09-22 12:09:02 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2022-09-23 14:28:45 +0000

    Teach the GICv3 driver to translate memory ranges
    
    As with the GICv1/2 driver teach the GICv3 driver to translate memory
    ranges of children. This allows us to create a common
    bus_alloc_resource implementation for bot hACPI and FDT attachments.
    
    Sponsored by:   The FreeBSD Foundation
---
 sys/arm/arm/gic.h             |   6 ---
 sys/arm/arm/gic_common.h      |   6 +++
 sys/arm64/arm64/gic_v3.c      |  57 ++++++++++++++++++++++
 sys/arm64/arm64/gic_v3_acpi.c |  33 -------------
 sys/arm64/arm64/gic_v3_fdt.c  | 111 +++++++++++++++++++++++-------------------
 sys/arm64/arm64/gic_v3_var.h  |   3 ++
 6 files changed, 126 insertions(+), 90 deletions(-)

diff --git a/sys/arm/arm/gic.h b/sys/arm/arm/gic.h
index ce0c8a6187e1..5db11a16a4e2 100644
--- a/sys/arm/arm/gic.h
+++ b/sys/arm/arm/gic.h
@@ -39,12 +39,6 @@
 #ifndef _ARM_GIC_H_
 #define _ARM_GIC_H_
 
-struct arm_gic_range {
-	uint64_t bus;
-	uint64_t host;
-	uint64_t size;
-};
-
 struct arm_gic_softc {
 	device_t		gic_dev;
 	void *			gic_intrhand;
diff --git a/sys/arm/arm/gic_common.h b/sys/arm/arm/gic_common.h
index 42ec44bb7fab..9487bcb2be8d 100644
--- a/sys/arm/arm/gic_common.h
+++ b/sys/arm/arm/gic_common.h
@@ -31,6 +31,12 @@
 #ifndef _GIC_COMMON_H_
 #define _GIC_COMMON_H_
 
+struct arm_gic_range {
+	uint64_t bus;
+	uint64_t host;
+	uint64_t size;
+};
+
 #define	GIC_IVAR_HW_REV		500
 #define	GIC_IVAR_BUS		501
 #define	GIC_IVAR_VGIC		502
diff --git a/sys/arm64/arm64/gic_v3.c b/sys/arm64/arm64/gic_v3.c
index 759d50f0941e..c26158e4035c 100644
--- a/sys/arm64/arm64/gic_v3.c
+++ b/sys/arm64/arm64/gic_v3.c
@@ -81,6 +81,7 @@ static bus_print_child_t gic_v3_print_child;
 static bus_get_domain_t gic_v3_get_domain;
 static bus_read_ivar_t gic_v3_read_ivar;
 static bus_write_ivar_t gic_v3_write_ivar;
+static bus_alloc_resource_t gic_v3_alloc_resource;
 
 static pic_disable_intr_t gic_v3_disable_intr;
 static pic_enable_intr_t gic_v3_enable_intr;
@@ -124,6 +125,8 @@ static device_method_t gic_v3_methods[] = {
 	DEVMETHOD(bus_get_domain,	gic_v3_get_domain),
 	DEVMETHOD(bus_read_ivar,	gic_v3_read_ivar),
 	DEVMETHOD(bus_write_ivar,	gic_v3_write_ivar),
+	DEVMETHOD(bus_alloc_resource,	gic_v3_alloc_resource),
+	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
 
 	/* Interrupt controller interface */
 	DEVMETHOD(pic_disable_intr,	gic_v3_disable_intr),
@@ -435,6 +438,7 @@ gic_v3_detach(device_t dev)
 	for (i = 0; i <= mp_maxid; i++)
 		free(sc->gic_redists.pcpu[i], M_GIC_V3);
 
+	free(sc->ranges, M_GIC_V3);
 	free(sc->gic_res, M_GIC_V3);
 	free(sc->gic_redists.regions, M_GIC_V3);
 
@@ -524,6 +528,59 @@ gic_v3_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
 	return (ENOENT);
 }
 
+static struct resource *
+gic_v3_alloc_resource(device_t bus, device_t child, int type, int *rid,
+    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+	struct gic_v3_softc *sc;
+	struct resource_list_entry *rle;
+	struct resource_list *rl;
+	int j;
+
+	/* We only allocate memory */
+	if (type != SYS_RES_MEMORY)
+		return (NULL);
+
+	sc = device_get_softc(bus);
+
+	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
+		rl = BUS_GET_RESOURCE_LIST(bus, child);
+		if (rl == NULL)
+			return (NULL);
+
+		/* Find defaults for this rid */
+		rle = resource_list_find(rl, type, *rid);
+		if (rle == NULL)
+			return (NULL);
+
+		start = rle->start;
+		end = rle->end;
+		count = rle->count;
+	}
+
+	/* Remap through ranges property */
+	for (j = 0; j < sc->nranges; j++) {
+		if (start >= sc->ranges[j].bus && end <
+		    sc->ranges[j].bus + sc->ranges[j].size) {
+			start -= sc->ranges[j].bus;
+			start += sc->ranges[j].host;
+			end -= sc->ranges[j].bus;
+			end += sc->ranges[j].host;
+			break;
+		}
+	}
+	if (j == sc->nranges && sc->nranges != 0) {
+		if (bootverbose)
+			device_printf(bus, "Could not map resource "
+			    "%#jx-%#jx\n", (uintmax_t)start, (uintmax_t)end);
+
+		return (NULL);
+	}
+
+	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+	    count, flags));
+}
+
 int
 arm_gic_v3_intr(void *arg)
 {
diff --git a/sys/arm64/arm64/gic_v3_acpi.c b/sys/arm64/arm64/gic_v3_acpi.c
index f24662750da7..3d3cd3ba9ccd 100644
--- a/sys/arm64/arm64/gic_v3_acpi.c
+++ b/sys/arm64/arm64/gic_v3_acpi.c
@@ -59,7 +59,6 @@ struct gic_v3_acpi_devinfo {
 static device_identify_t gic_v3_acpi_identify;
 static device_probe_t gic_v3_acpi_probe;
 static device_attach_t gic_v3_acpi_attach;
-static bus_alloc_resource_t gic_v3_acpi_bus_alloc_res;
 static bus_get_resource_list_t gic_v3_acpi_get_resource_list;
 
 static void gic_v3_acpi_bus_attach(device_t);
@@ -71,8 +70,6 @@ static device_method_t gic_v3_acpi_methods[] = {
 	DEVMETHOD(device_attach,		gic_v3_acpi_attach),
 
 	/* Bus interface */
-	DEVMETHOD(bus_alloc_resource,		gic_v3_acpi_bus_alloc_res),
-	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
 	DEVMETHOD(bus_get_resource_list,	gic_v3_acpi_get_resource_list),
 
 	/* End */
@@ -445,36 +442,6 @@ gic_v3_acpi_bus_attach(device_t dev)
 	bus_generic_attach(dev);
 }
 
-static struct resource *
-gic_v3_acpi_bus_alloc_res(device_t bus, device_t child, int type, int *rid,
-    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
-{
-	struct resource_list_entry *rle;
-	struct resource_list *rl;
-
-	/* We only allocate memory */
-	if (type != SYS_RES_MEMORY)
-		return (NULL);
-
-	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
-		rl = BUS_GET_RESOURCE_LIST(bus, child);
-		if (rl == NULL)
-			return (NULL);
-
-		/* Find defaults for this rid */
-		rle = resource_list_find(rl, type, *rid);
-		if (rle == NULL)
-			return (NULL);
-
-		start = rle->start;
-		end = rle->end;
-		count = rle->count;
-	}
-
-	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
-	    count, flags));
-}
-
 static struct resource_list *
 gic_v3_acpi_get_resource_list(device_t bus, device_t child)
 {
diff --git a/sys/arm64/arm64/gic_v3_fdt.c b/sys/arm64/arm64/gic_v3_fdt.c
index dc1445340943..2efdba68eae2 100644
--- a/sys/arm64/arm64/gic_v3_fdt.c
+++ b/sys/arm64/arm64/gic_v3_fdt.c
@@ -54,8 +54,6 @@ __FBSDID("$FreeBSD$");
 static int gic_v3_fdt_probe(device_t);
 static int gic_v3_fdt_attach(device_t);
 
-static struct resource *gic_v3_ofw_bus_alloc_res(device_t, device_t, int, int *,
-    rman_res_t, rman_res_t, rman_res_t, u_int);
 static const struct ofw_bus_devinfo *gic_v3_ofw_get_devinfo(device_t, device_t);
 static bus_get_resource_list_t gic_v3_fdt_get_resource_list;
 
@@ -65,8 +63,6 @@ static device_method_t gic_v3_fdt_methods[] = {
 	DEVMETHOD(device_attach,	gic_v3_fdt_attach),
 
 	/* Bus interface */
-	DEVMETHOD(bus_alloc_resource,		gic_v3_ofw_bus_alloc_res),
-	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
 	DEVMETHOD(bus_get_resource_list,	gic_v3_fdt_get_resource_list),
 
 	/* ofw_bus interface */
@@ -219,51 +215,64 @@ gic_v3_ofw_get_devinfo(device_t bus __unused, device_t child)
 	return (&di->di_dinfo);
 }
 
-static struct resource *
-gic_v3_ofw_bus_alloc_res(device_t bus, device_t child, int type, int *rid,
-    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+/* Helper functions */
+static int
+gic_v3_ofw_fill_ranges(phandle_t parent, struct gic_v3_softc *sc,
+    pcell_t *addr_cellsp, pcell_t *size_cellsp)
 {
-	struct resource_list_entry *rle;
-	struct resource_list *rl;
-	int ranges_len;
-
-	/* We only allocate memory */
-	if (type != SYS_RES_MEMORY)
-		return (NULL);
-
-	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
-		rl = BUS_GET_RESOURCE_LIST(bus, child);
-		if (rl == NULL)
-			return (NULL);
-
-		/* Find defaults for this rid */
-		rle = resource_list_find(rl, type, *rid);
-		if (rle == NULL)
-			return (NULL);
-
-		start = rle->start;
-		end = rle->end;
-		count = rle->count;
-	}
-	/*
-	 * XXX: No ranges remap!
-	 *	Absolute address is expected.
-	 */
-	if (ofw_bus_has_prop(bus, "ranges")) {
-		ranges_len = OF_getproplen(ofw_bus_get_node(bus), "ranges");
-		if (ranges_len != 0) {
-			if (bootverbose) {
-				device_printf(child,
-				    "Ranges remap not supported\n");
-			}
-			return (NULL);
+	pcell_t addr_cells, host_cells, size_cells;
+	cell_t *base_ranges;
+	ssize_t nbase_ranges;
+	int i, j, k;
+
+	host_cells = 1;
+	OF_getencprop(OF_parent(parent), "#address-cells", &host_cells,
+	    sizeof(host_cells));
+	addr_cells = 2;
+	OF_getencprop(parent, "#address-cells", &addr_cells,
+	    sizeof(addr_cells));
+	size_cells = 2;
+	OF_getencprop(parent, "#size-cells", &size_cells,
+	    sizeof(size_cells));
+
+	*addr_cellsp = addr_cells;
+	*size_cellsp = size_cells;
+
+	nbase_ranges = OF_getproplen(parent, "ranges");
+	if (nbase_ranges < 0)
+		return (EINVAL);
+
+	sc->nranges = nbase_ranges / sizeof(cell_t) /
+	    (addr_cells + host_cells + size_cells);
+	if (sc->nranges == 0)
+		return (0);
+
+	sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), M_GIC_V3,
+	    M_WAITOK);
+	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+	OF_getencprop(parent, "ranges", base_ranges, nbase_ranges);
+
+	for (i = 0, j = 0; i < sc->nranges; i++) {
+		sc->ranges[i].bus = 0;
+		for (k = 0; k < addr_cells; k++) {
+			sc->ranges[i].bus <<= 32;
+			sc->ranges[i].bus |= base_ranges[j++];
+		}
+		sc->ranges[i].host = 0;
+		for (k = 0; k < host_cells; k++) {
+			sc->ranges[i].host <<= 32;
+			sc->ranges[i].host |= base_ranges[j++];
+		}
+		sc->ranges[i].size = 0;
+		for (k = 0; k < size_cells; k++) {
+			sc->ranges[i].size <<= 32;
+			sc->ranges[i].size |= base_ranges[j++];
 		}
 	}
-	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
-	    count, flags));
-}
 
-/* Helper functions */
+	free(base_ranges, M_DEVBUF);
+	return (0);
+}
 
 /*
  * Bus capability support for GICv3.
@@ -278,16 +287,16 @@ gic_v3_ofw_bus_attach(device_t dev)
 	device_t child;
 	phandle_t parent, node;
 	pcell_t addr_cells, size_cells;
+	int rv;
 
 	sc = device_get_softc(dev);
 	parent = ofw_bus_get_node(dev);
 	if (parent > 0) {
-		addr_cells = 2;
-		OF_getencprop(parent, "#address-cells", &addr_cells,
-		    sizeof(addr_cells));
-		size_cells = 2;
-		OF_getencprop(parent, "#size-cells", &size_cells,
-		    sizeof(size_cells));
+		rv = gic_v3_ofw_fill_ranges(parent, sc, &addr_cells,
+		    &size_cells);
+		if (rv != 0)
+			return (rv);
+
 		/* Iterate through all GIC subordinates */
 		for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
 			/*
diff --git a/sys/arm64/arm64/gic_v3_var.h b/sys/arm64/arm64/gic_v3_var.h
index b66fe6c57bb2..61c3ff0b61ae 100644
--- a/sys/arm64/arm64/gic_v3_var.h
+++ b/sys/arm64/arm64/gic_v3_var.h
@@ -84,6 +84,9 @@ struct gic_v3_softc {
 	device_t		*gic_children;
 	struct intr_pic		*gic_pic;
 	struct gic_v3_irqsrc	*gic_irqs;
+
+	int			nranges;
+	struct arm_gic_range *	ranges;
 };
 
 struct gic_v3_devinfo {