git: cb4a83cff01c - main - gicv3: If the LPI is already allocated, remember it

From: Warner Losh <imp_at_FreeBSD.org>
Date: Wed, 28 Feb 2024 14:10:48 UTC
The branch main has been updated by imp:

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

commit cb4a83cff01cdf871c78115c942c5b34412914d2
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2024-02-28 14:09:17 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2024-02-28 14:09:43 +0000

    gicv3: If the LPI is already allocated, remember it
    
    If the LPI Configuration Tabel has been pre-allocated by the boot
    loader, then we have to remember PROPBASER and use it rather than
    allocating memory for it ourselves. Linux provides us with a reserved
    table that contains all the gicv3 allocations, so make sure what we read
    from PROPBASER matches something in that table. Normally, bare metal
    boot loaders leave the gic in a reset state. However, Linux brings it up
    fully so it can do I/O to boot the next kernel via kexec. Since the
    gicv3 PENDBASER can't be reset while running due to undefined behavior,
    we must reuse what's there for both PENDBASER and PROPBASER.
    
    With this commit, the workaround is complete. Details are at
    https://lkml.iu.edu/hypermail/linux/kernel/1809.2/06246.html
    and pointers in the thread.
    
    Sponsored by:           Netflix
    Reviewed by:            andrew
    Differential Revision:  https://reviews.freebsd.org/D44038
---
 sys/arm64/arm64/gicv3_its.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/sys/arm64/arm64/gicv3_its.c b/sys/arm64/arm64/gicv3_its.c
index 71620eec8fce..9fcf13fe433b 100644
--- a/sys/arm64/arm64/gicv3_its.c
+++ b/sys/arm64/arm64/gicv3_its.c
@@ -685,6 +685,8 @@ gicv3_its_conftable_init(struct gicv3_its_softc *sc)
 	int extra_flags = 0;
 	device_t gicv3;
 	uint32_t ctlr;
+	vm_paddr_t conf_pa;
+	vm_offset_t conf_va;
 
 	/*
 	 * The PROPBASER is a singleton in our parent. We only set it up the
@@ -699,8 +701,32 @@ gicv3_its_conftable_init(struct gicv3_its_softc *sc)
 	gicv3 = device_get_parent(sc->dev);
 	ctlr = gic_r_read_4(gicv3, GICR_CTLR);
 	if ((ctlr & GICR_CTLR_LPI_ENABLE) != 0) {
-		extra_flags = ITS_FLAGS_LPI_PREALLOC;
-		panic("gicv3 already enabled, can't reprogram.");
+		conf_pa = gic_r_read_8(gicv3, GICR_PROPBASER);
+		conf_pa &= GICR_PROPBASER_PA_MASK;
+		/*
+		 * If there was a pre-existing PROPBASER, then we need to honor
+		 * it because implemenetation defined behavior in gicv3 makes it
+		 * impossible to quiesce to change it out. We will only see a
+		 * pre-existing one when we've been kexec'd from a Linux kernel,
+		 * or from a LinuxBoot environment.
+		 *
+		 * Linux provides us with a MEMRESERVE table that we put into
+		 * the excluded physmem area. If PROPBASER isn't in this tabke,
+		 * the system cannot run due to random memory corruption,
+		 * so we panic for this case.
+		 */
+		if (!physmem_excluded(conf_pa, LPI_CONFTAB_SIZE))
+			panic("gicv3 PROPBASER needs to reuse %#lx, but not reserved\n",
+			    conf_pa);
+		conf_va = PHYS_TO_DMAP(conf_pa);
+		if (!pmap_klookup(conf_va, NULL))
+			panic("Can't mapped prior LPI mapping into VA\n");
+		conf_table = (void *)conf_va;
+		extra_flags = ITS_FLAGS_LPI_PREALLOC | ITS_FLAGS_LPI_CONF_FLUSH;
+		if (bootverbose)
+			device_printf(sc->dev,
+			    "LPI enabled, conf table using pa %#lx va %lx\n",
+			    conf_pa, conf_va);
 	} else {
 
 		/*