git: d476e51525ff - stable/14 - gicv3: If the LPI is already allocated, remember it

From: Warner Losh <imp_at_FreeBSD.org>
Date: Tue, 16 Apr 2024 21:42:52 UTC
The branch stable/14 has been updated by imp:

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

commit d476e51525ff9e5c489feec14f64aa8c1d38c5b5
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2024-02-28 14:09:17 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2024-04-16 21:28:41 +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
    
    (cherry picked from commit cb4a83cff01cdf871c78115c942c5b34412914d2)
---
 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 49d9c08a4ffd..02c063fe57b5 100644
--- a/sys/arm64/arm64/gicv3_its.c
+++ b/sys/arm64/arm64/gicv3_its.c
@@ -686,6 +686,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
@@ -700,8 +702,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 {
 
 		/*