git: 7d2dd08d013d - main - gicv3: Add checks for the device ID

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Wed, 23 Aug 2023 16:39:02 UTC
The branch main has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=7d2dd08d013d79dc3f2a22747b079628a571cea2

commit 7d2dd08d013d79dc3f2a22747b079628a571cea2
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2023-08-22 14:37:19 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2023-08-23 16:38:20 +0000

    gicv3: Add checks for the device ID
    
    Add checks that the device ID is supported by the hardware and is
    within the range allocated when the driver attaches.
    
    Reviewed by:    gallatin, imp
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D41554
---
 sys/arm64/arm64/gicv3_its.c | 48 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/sys/arm64/arm64/gicv3_its.c b/sys/arm64/arm64/gicv3_its.c
index cc743e72b032..cb655eaca412 100644
--- a/sys/arm64/arm64/gicv3_its.c
+++ b/sys/arm64/arm64/gicv3_its.c
@@ -221,7 +221,10 @@ struct its_cmd {
 /* An ITS private table */
 struct its_ptable {
 	vm_offset_t	ptab_vaddr;
-	unsigned long	ptab_size;
+	/* Size of the L1 table */
+	size_t		ptab_l1_size;
+	/* Number of L1 entries */
+	int		ptab_l1_nidents;
 };
 
 /* ITS collection description. */
@@ -246,6 +249,8 @@ struct gicv3_its_softc {
 	cpuset_t	sc_cpus;
 	struct domainset *sc_ds;
 	u_int		gic_irq_cpu;
+	int		sc_devbits;
+	int		sc_dev_table_idx;
 
 	struct its_ptable sc_its_ptab[GITS_BASER_NUM];
 	struct its_col *sc_its_cols[MAXCPU];	/* Per-CPU collections */
@@ -476,7 +481,8 @@ gicv3_its_table_init(device_t dev, struct gicv3_its_softc *sc)
 	vm_offset_t table;
 	vm_paddr_t paddr;
 	uint64_t cache, reg, share, tmp, type;
-	size_t esize, its_tbl_size, nidents, nitspages, npages;
+	size_t its_tbl_size, nitspages, npages;
+	size_t l1_esize, l1_nidents;
 	int i, page_size;
 	int devbits;
 
@@ -505,6 +511,7 @@ gicv3_its_table_init(device_t dev, struct gicv3_its_softc *sc)
 		devbits = GITS_TYPER_DEVB(gic_its_read_8(sc, GITS_TYPER));
 		cache = GITS_BASER_CACHE_WAWB;
 	}
+	sc->sc_devbits = devbits;
 	share = GITS_BASER_SHARE_IS;
 
 	for (i = 0; i < GITS_BASER_NUM; i++) {
@@ -515,7 +522,7 @@ gicv3_its_table_init(device_t dev, struct gicv3_its_softc *sc)
 			continue;
 
 		/* The table entry size */
-		esize = GITS_BASER_ESIZE(reg);
+		l1_esize = GITS_BASER_ESIZE(reg);
 
 		/* Find the tables page size */
 		page_size = gicv3_its_table_page_size(sc, i);
@@ -527,8 +534,13 @@ gicv3_its_table_init(device_t dev, struct gicv3_its_softc *sc)
 
 		switch(type) {
 		case GITS_BASER_TYPE_DEV:
-			nidents = (1 << devbits);
-			its_tbl_size = esize * nidents;
+			if (sc->sc_dev_table_idx != -1)
+				device_printf(dev,
+				    "Warning: Multiple device tables found\n");
+
+			sc->sc_dev_table_idx = i;
+			l1_nidents = (1 << devbits);
+			its_tbl_size = l1_esize * l1_nidents;
 			its_tbl_size = roundup2(its_tbl_size, page_size);
 			break;
 		case GITS_BASER_TYPE_VP:
@@ -550,7 +562,8 @@ gicv3_its_table_init(device_t dev, struct gicv3_its_softc *sc)
 		    (1ul << 48) - 1, PAGE_SIZE_64K, 0);
 
 		sc->sc_its_ptab[i].ptab_vaddr = table;
-		sc->sc_its_ptab[i].ptab_size = npages * PAGE_SIZE;
+		sc->sc_its_ptab[i].ptab_l1_size = its_tbl_size;
+		sc->sc_its_ptab[i].ptab_l1_nidents = l1_nidents;
 
 		paddr = vtophys(table);
 
@@ -874,6 +887,7 @@ gicv3_its_attach(device_t dev)
 
 	sc = device_get_softc(dev);
 
+	sc->sc_dev_table_idx = -1;
 	sc->sc_irq_length = gicv3_get_nirqs(dev);
 	sc->sc_irq_base = GIC_FIRST_LPI;
 	sc->sc_irq_base += device_get_unit(dev) * sc->sc_irq_length;
@@ -1200,6 +1214,23 @@ its_device_find(device_t dev, device_t child)
 	return (its_dev);
 }
 
+static bool
+its_device_alloc(struct gicv3_its_softc *sc, int devid)
+{
+	struct its_ptable *ptable;
+
+	/* No device table */
+	if (sc->sc_dev_table_idx < 0) {
+		if (devid < (1 << sc->sc_devbits))
+			return (true);
+		return (false);
+	}
+
+	ptable = &sc->sc_its_ptab[sc->sc_dev_table_idx];
+	/* Check the devid is within the table limit */
+	return (devid < ptable->ptab_l1_nidents);
+}
+
 static struct its_dev *
 its_device_get(device_t dev, device_t child, u_int nvecs)
 {
@@ -1225,6 +1256,11 @@ its_device_get(device_t dev, device_t child, u_int nvecs)
 	its_dev->lpis.lpi_num = nvecs;
 	its_dev->lpis.lpi_free = nvecs;
 
+	if (!its_device_alloc(sc, its_dev->devid)) {
+		free(its_dev, M_GICV3_ITS);
+		return (NULL);
+	}
+
 	if (vmem_alloc(sc->sc_irq_alloc, nvecs, M_FIRSTFIT | M_NOWAIT,
 	    &irq_base) != 0) {
 		free(its_dev, M_GICV3_ITS);