xref: /freebsd/sys/arm64/vmm/io/vgic_v3.c (revision 47e07394)
147e07394SAndrew Turner /*-
247e07394SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
347e07394SAndrew Turner  *
447e07394SAndrew Turner  * Copyright (C) 2018 Alexandru Elisei <alexandru.elisei@gmail.com>
547e07394SAndrew Turner  * Copyright (C) 2020-2022 Andrew Turner
647e07394SAndrew Turner  * Copyright (C) 2023 Arm Ltd
747e07394SAndrew Turner  *
847e07394SAndrew Turner  * Redistribution and use in source and binary forms, with or without
947e07394SAndrew Turner  * modification, are permitted provided that the following conditions
1047e07394SAndrew Turner  * are met:
1147e07394SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
1247e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
1347e07394SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
1447e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
1547e07394SAndrew Turner  *    documentation and/or other materials provided with the distribution.
1647e07394SAndrew Turner  *
1747e07394SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1847e07394SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1947e07394SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2047e07394SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
2147e07394SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2247e07394SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2347e07394SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2447e07394SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2547e07394SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2647e07394SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2747e07394SAndrew Turner  * SUCH DAMAGE.
2847e07394SAndrew Turner  */
2947e07394SAndrew Turner 
3047e07394SAndrew Turner #include <sys/cdefs.h>
3147e07394SAndrew Turner 
3247e07394SAndrew Turner #include <sys/types.h>
3347e07394SAndrew Turner #include <sys/errno.h>
3447e07394SAndrew Turner #include <sys/systm.h>
3547e07394SAndrew Turner #include <sys/bitstring.h>
3647e07394SAndrew Turner #include <sys/bus.h>
3747e07394SAndrew Turner #include <sys/kernel.h>
3847e07394SAndrew Turner #include <sys/lock.h>
3947e07394SAndrew Turner #include <sys/malloc.h>
4047e07394SAndrew Turner #include <sys/module.h>
4147e07394SAndrew Turner #include <sys/mutex.h>
4247e07394SAndrew Turner #include <sys/rman.h>
4347e07394SAndrew Turner #include <sys/smp.h>
4447e07394SAndrew Turner 
4547e07394SAndrew Turner #include <vm/vm.h>
4647e07394SAndrew Turner #include <vm/pmap.h>
4747e07394SAndrew Turner 
4847e07394SAndrew Turner #include <dev/ofw/openfirm.h>
4947e07394SAndrew Turner 
5047e07394SAndrew Turner #include <machine/armreg.h>
5147e07394SAndrew Turner #include <machine/atomic.h>
5247e07394SAndrew Turner #include <machine/bus.h>
5347e07394SAndrew Turner #include <machine/cpufunc.h>
5447e07394SAndrew Turner #include <machine/cpu.h>
5547e07394SAndrew Turner #include <machine/machdep.h>
5647e07394SAndrew Turner #include <machine/param.h>
5747e07394SAndrew Turner #include <machine/pmap.h>
5847e07394SAndrew Turner #include <machine/vmparam.h>
5947e07394SAndrew Turner #include <machine/intr.h>
6047e07394SAndrew Turner #include <machine/vmm.h>
6147e07394SAndrew Turner #include <machine/vmm_dev.h>
6247e07394SAndrew Turner #include <machine/vmm_instruction_emul.h>
6347e07394SAndrew Turner 
6447e07394SAndrew Turner #include <arm/arm/gic_common.h>
6547e07394SAndrew Turner #include <arm64/arm64/gic_v3_reg.h>
6647e07394SAndrew Turner #include <arm64/arm64/gic_v3_var.h>
6747e07394SAndrew Turner 
6847e07394SAndrew Turner #include <arm64/vmm/hyp.h>
6947e07394SAndrew Turner #include <arm64/vmm/mmu.h>
7047e07394SAndrew Turner #include <arm64/vmm/arm64.h>
7147e07394SAndrew Turner 
7247e07394SAndrew Turner #include "vgic.h"
7347e07394SAndrew Turner #include "vgic_v3.h"
7447e07394SAndrew Turner #include "vgic_v3_reg.h"
7547e07394SAndrew Turner 
7647e07394SAndrew Turner #include "vgic_if.h"
7747e07394SAndrew Turner 
7847e07394SAndrew Turner #define VGIC_SGI_NUM		(GIC_LAST_SGI - GIC_FIRST_SGI + 1)
7947e07394SAndrew Turner #define VGIC_PPI_NUM		(GIC_LAST_PPI - GIC_FIRST_PPI + 1)
8047e07394SAndrew Turner #define VGIC_SPI_NUM		(GIC_LAST_SPI - GIC_FIRST_SPI + 1)
8147e07394SAndrew Turner #define VGIC_PRV_I_NUM		(VGIC_SGI_NUM + VGIC_PPI_NUM)
8247e07394SAndrew Turner #define VGIC_SHR_I_NUM		(VGIC_SPI_NUM)
8347e07394SAndrew Turner 
8447e07394SAndrew Turner MALLOC_DEFINE(M_VGIC_V3, "ARM VMM VGIC V3", "ARM VMM VGIC V3");
8547e07394SAndrew Turner 
8647e07394SAndrew Turner /* TODO: Move to softc */
8747e07394SAndrew Turner struct vgic_v3_virt_features {
8847e07394SAndrew Turner 	uint8_t min_prio;
8947e07394SAndrew Turner 	size_t ich_lr_num;
9047e07394SAndrew Turner 	size_t ich_apr_num;
9147e07394SAndrew Turner };
9247e07394SAndrew Turner 
9347e07394SAndrew Turner struct vgic_v3_irq {
9447e07394SAndrew Turner 	/* List of IRQs that are active or pending */
9547e07394SAndrew Turner 	TAILQ_ENTRY(vgic_v3_irq) act_pend_list;
9647e07394SAndrew Turner 	struct mtx irq_spinmtx;
9747e07394SAndrew Turner 	uint64_t mpidr;
9847e07394SAndrew Turner 	int target_vcpu;
9947e07394SAndrew Turner 	uint32_t irq;
10047e07394SAndrew Turner 	bool active;
10147e07394SAndrew Turner 	bool pending;
10247e07394SAndrew Turner 	bool enabled;
10347e07394SAndrew Turner 	bool level;
10447e07394SAndrew Turner 	bool on_aplist;
10547e07394SAndrew Turner 	uint8_t priority;
10647e07394SAndrew Turner 	uint8_t config;
10747e07394SAndrew Turner #define	VGIC_CONFIG_MASK	0x2
10847e07394SAndrew Turner #define	VGIC_CONFIG_LEVEL	0x0
10947e07394SAndrew Turner #define	VGIC_CONFIG_EDGE	0x2
11047e07394SAndrew Turner };
11147e07394SAndrew Turner 
11247e07394SAndrew Turner /* Global data not needed by EL2 */
11347e07394SAndrew Turner struct vgic_v3 {
11447e07394SAndrew Turner 	struct mtx 	dist_mtx;
11547e07394SAndrew Turner 	uint64_t 	dist_start;
11647e07394SAndrew Turner 	size_t   	dist_end;
11747e07394SAndrew Turner 
11847e07394SAndrew Turner 	uint64_t 	redist_start;
11947e07394SAndrew Turner 	size_t 		redist_end;
12047e07394SAndrew Turner 
12147e07394SAndrew Turner 	uint32_t 	gicd_ctlr;	/* Distributor Control Register */
12247e07394SAndrew Turner 
12347e07394SAndrew Turner 	struct vgic_v3_irq *irqs;
12447e07394SAndrew Turner };
12547e07394SAndrew Turner 
12647e07394SAndrew Turner /* Per-CPU data not needed by EL2 */
12747e07394SAndrew Turner struct vgic_v3_cpu {
12847e07394SAndrew Turner 	/*
12947e07394SAndrew Turner 	 * We need a mutex for accessing the list registers because they are
13047e07394SAndrew Turner 	 * modified asynchronously by the virtual timer.
13147e07394SAndrew Turner 	 *
13247e07394SAndrew Turner 	 * Note that the mutex *MUST* be a spin mutex because an interrupt can
13347e07394SAndrew Turner 	 * be injected by a callout callback function, thereby modifying the
13447e07394SAndrew Turner 	 * list registers from a context where sleeping is forbidden.
13547e07394SAndrew Turner 	 */
13647e07394SAndrew Turner 	struct mtx	lr_mtx;
13747e07394SAndrew Turner 
13847e07394SAndrew Turner 	struct vgic_v3_irq private_irqs[VGIC_PRV_I_NUM];
13947e07394SAndrew Turner 	TAILQ_HEAD(, vgic_v3_irq) irq_act_pend;
14047e07394SAndrew Turner 	u_int		ich_lr_used;
14147e07394SAndrew Turner };
14247e07394SAndrew Turner 
14347e07394SAndrew Turner /* How many IRQs we support (SGIs + PPIs + SPIs). Not including LPIs */
14447e07394SAndrew Turner #define	VGIC_NIRQS	1023
14547e07394SAndrew Turner /* Pretend to be an Arm design */
14647e07394SAndrew Turner #define	VGIC_IIDR	0x43b
14747e07394SAndrew Turner 
14847e07394SAndrew Turner static vgic_inject_irq_t vgic_v3_inject_irq;
14947e07394SAndrew Turner static vgic_inject_msi_t vgic_v3_inject_msi;
15047e07394SAndrew Turner 
15147e07394SAndrew Turner static int vgic_v3_max_cpu_count(device_t dev, struct hyp *hyp);
15247e07394SAndrew Turner 
15347e07394SAndrew Turner #define	INJECT_IRQ(hyp, vcpuid, irqid, level)			\
15447e07394SAndrew Turner     vgic_v3_inject_irq(NULL, (hyp), (vcpuid), (irqid), (level))
15547e07394SAndrew Turner 
15647e07394SAndrew Turner typedef void (register_read)(struct hypctx *, u_int, uint64_t *, void *);
15747e07394SAndrew Turner typedef void (register_write)(struct hypctx *, u_int, u_int, u_int,
15847e07394SAndrew Turner     uint64_t, void *);
15947e07394SAndrew Turner 
16047e07394SAndrew Turner #define	VGIC_8_BIT	(1 << 0)
16147e07394SAndrew Turner /* (1 << 1) is reserved for 16 bit accesses */
16247e07394SAndrew Turner #define	VGIC_32_BIT	(1 << 2)
16347e07394SAndrew Turner #define	VGIC_64_BIT	(1 << 3)
16447e07394SAndrew Turner 
16547e07394SAndrew Turner struct vgic_register {
16647e07394SAndrew Turner 	u_int start;	/* Start within a memory region */
16747e07394SAndrew Turner 	u_int end;
16847e07394SAndrew Turner 	u_int size;
16947e07394SAndrew Turner 	u_int flags;
17047e07394SAndrew Turner 	register_read *read;
17147e07394SAndrew Turner 	register_write *write;
17247e07394SAndrew Turner };
17347e07394SAndrew Turner 
17447e07394SAndrew Turner #define	VGIC_REGISTER_RANGE(reg_start, reg_end, reg_size, reg_flags, readf, \
17547e07394SAndrew Turner     writef)								\
17647e07394SAndrew Turner {									\
17747e07394SAndrew Turner 	.start = (reg_start),						\
17847e07394SAndrew Turner 	.end = (reg_end),						\
17947e07394SAndrew Turner 	.size = (reg_size),						\
18047e07394SAndrew Turner 	.flags = (reg_flags),						\
18147e07394SAndrew Turner 	.read = (readf),						\
18247e07394SAndrew Turner 	.write = (writef),						\
18347e07394SAndrew Turner }
18447e07394SAndrew Turner 
18547e07394SAndrew Turner #define	VGIC_REGISTER_RANGE_RAZ_WI(reg_start, reg_end, reg_size, reg_flags) \
18647e07394SAndrew Turner 	VGIC_REGISTER_RANGE(reg_start, reg_end, reg_size, reg_flags,	\
18747e07394SAndrew Turner 	    gic_zero_read, gic_ignore_write)
18847e07394SAndrew Turner 
18947e07394SAndrew Turner #define	VGIC_REGISTER(start_addr, reg_size, reg_flags, readf, writef)	\
19047e07394SAndrew Turner 	VGIC_REGISTER_RANGE(start_addr, (start_addr) + (reg_size),	\
19147e07394SAndrew Turner 	    reg_size, reg_flags, readf, writef)
19247e07394SAndrew Turner 
19347e07394SAndrew Turner #define	VGIC_REGISTER_RAZ_WI(start_addr, reg_size, reg_flags)		\
19447e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(start_addr,				\
19547e07394SAndrew Turner 	    (start_addr) + (reg_size), reg_size, reg_flags)
19647e07394SAndrew Turner 
19747e07394SAndrew Turner static register_read gic_pidr2_read;
19847e07394SAndrew Turner static register_read gic_zero_read;
19947e07394SAndrew Turner static register_write gic_ignore_write;
20047e07394SAndrew Turner 
20147e07394SAndrew Turner /* GICD_CTLR */
20247e07394SAndrew Turner static register_read dist_ctlr_read;
20347e07394SAndrew Turner static register_write dist_ctlr_write;
20447e07394SAndrew Turner /* GICD_TYPER */
20547e07394SAndrew Turner static register_read dist_typer_read;
20647e07394SAndrew Turner /* GICD_IIDR */
20747e07394SAndrew Turner static register_read dist_iidr_read;
20847e07394SAndrew Turner /* GICD_STATUSR - RAZ/WI as we don't report errors (yet) */
20947e07394SAndrew Turner /* GICD_SETSPI_NSR & GICD_CLRSPI_NSR */
21047e07394SAndrew Turner static register_write dist_setclrspi_nsr_write;
21147e07394SAndrew Turner /* GICD_SETSPI_SR - RAZ/WI */
21247e07394SAndrew Turner /* GICD_CLRSPI_SR - RAZ/WI */
21347e07394SAndrew Turner /* GICD_IGROUPR - RAZ/WI as GICD_CTLR.ARE == 1 */
21447e07394SAndrew Turner /* GICD_ISENABLER */
21547e07394SAndrew Turner static register_read dist_isenabler_read;
21647e07394SAndrew Turner static register_write dist_isenabler_write;
21747e07394SAndrew Turner /* GICD_ICENABLER */
21847e07394SAndrew Turner static register_read dist_icenabler_read;
21947e07394SAndrew Turner static register_write dist_icenabler_write;
22047e07394SAndrew Turner /* GICD_ISPENDR */
22147e07394SAndrew Turner static register_read dist_ispendr_read;
22247e07394SAndrew Turner static register_write dist_ispendr_write;
22347e07394SAndrew Turner /* GICD_ICPENDR */
22447e07394SAndrew Turner static register_read dist_icpendr_read;
22547e07394SAndrew Turner static register_write dist_icpendr_write;
22647e07394SAndrew Turner /* GICD_ISACTIVER */
22747e07394SAndrew Turner static register_read dist_isactiver_read;
22847e07394SAndrew Turner static register_write dist_isactiver_write;
22947e07394SAndrew Turner /* GICD_ICACTIVER */
23047e07394SAndrew Turner static register_read dist_icactiver_read;
23147e07394SAndrew Turner static register_write dist_icactiver_write;
23247e07394SAndrew Turner /* GICD_IPRIORITYR */
23347e07394SAndrew Turner static register_read dist_ipriorityr_read;
23447e07394SAndrew Turner static register_write dist_ipriorityr_write;
23547e07394SAndrew Turner /* GICD_ITARGETSR - RAZ/WI as GICD_CTLR.ARE == 1 */
23647e07394SAndrew Turner /* GICD_ICFGR */
23747e07394SAndrew Turner static register_read dist_icfgr_read;
23847e07394SAndrew Turner static register_write dist_icfgr_write;
23947e07394SAndrew Turner /* GICD_IGRPMODR - RAZ/WI from non-secure mode */
24047e07394SAndrew Turner /* GICD_NSACR - RAZ/WI from non-secure mode */
24147e07394SAndrew Turner /* GICD_SGIR - RAZ/WI as GICD_CTLR.ARE == 1 */
24247e07394SAndrew Turner /* GICD_CPENDSGIR - RAZ/WI as GICD_CTLR.ARE == 1 */
24347e07394SAndrew Turner /* GICD_SPENDSGIR - RAZ/WI as GICD_CTLR.ARE == 1 */
24447e07394SAndrew Turner /* GICD_IROUTER */
24547e07394SAndrew Turner static register_read dist_irouter_read;
24647e07394SAndrew Turner static register_write dist_irouter_write;
24747e07394SAndrew Turner 
24847e07394SAndrew Turner static struct vgic_register dist_registers[] = {
24947e07394SAndrew Turner 	VGIC_REGISTER(GICD_CTLR, 4, VGIC_32_BIT, dist_ctlr_read,
25047e07394SAndrew Turner 	    dist_ctlr_write),
25147e07394SAndrew Turner 	VGIC_REGISTER(GICD_TYPER, 4, VGIC_32_BIT, dist_typer_read,
25247e07394SAndrew Turner 	    gic_ignore_write),
25347e07394SAndrew Turner 	VGIC_REGISTER(GICD_IIDR, 4, VGIC_32_BIT, dist_iidr_read,
25447e07394SAndrew Turner 	    gic_ignore_write),
25547e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_STATUSR, 4, VGIC_32_BIT),
25647e07394SAndrew Turner 	VGIC_REGISTER(GICD_SETSPI_NSR, 4, VGIC_32_BIT, gic_zero_read,
25747e07394SAndrew Turner 	    dist_setclrspi_nsr_write),
25847e07394SAndrew Turner 	VGIC_REGISTER(GICD_CLRSPI_NSR, 4, VGIC_32_BIT, gic_zero_read,
25947e07394SAndrew Turner 	    dist_setclrspi_nsr_write),
26047e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_SETSPI_SR, 4, VGIC_32_BIT),
26147e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_CLRSPI_SR, 4, VGIC_32_BIT),
26247e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_IGROUPR(0), GICD_IGROUPR(1024), 4,
26347e07394SAndrew Turner 	    VGIC_32_BIT),
26447e07394SAndrew Turner 
26547e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_ISENABLER(0), 4, VGIC_32_BIT),
26647e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ISENABLER(32), GICD_ISENABLER(1024), 4,
26747e07394SAndrew Turner 	    VGIC_32_BIT, dist_isenabler_read, dist_isenabler_write),
26847e07394SAndrew Turner 
26947e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_ICENABLER(0), 4, VGIC_32_BIT),
27047e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ICENABLER(32), GICD_ICENABLER(1024), 4,
27147e07394SAndrew Turner 	    VGIC_32_BIT, dist_icenabler_read, dist_icenabler_write),
27247e07394SAndrew Turner 
27347e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_ISPENDR(0), 4, VGIC_32_BIT),
27447e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ISPENDR(32), GICD_ISPENDR(1024), 4,
27547e07394SAndrew Turner 	    VGIC_32_BIT, dist_ispendr_read, dist_ispendr_write),
27647e07394SAndrew Turner 
27747e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_ICPENDR(0), 4, VGIC_32_BIT),
27847e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ICPENDR(32), GICD_ICPENDR(1024), 4,
27947e07394SAndrew Turner 	    VGIC_32_BIT, dist_icpendr_read, dist_icpendr_write),
28047e07394SAndrew Turner 
28147e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_ISACTIVER(0), 4, VGIC_32_BIT),
28247e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ISACTIVER(32), GICD_ISACTIVER(1024), 4,
28347e07394SAndrew Turner 	    VGIC_32_BIT, dist_isactiver_read, dist_isactiver_write),
28447e07394SAndrew Turner 
28547e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_ICACTIVER(0), 4, VGIC_32_BIT),
28647e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ICACTIVER(32), GICD_ICACTIVER(1024), 4,
28747e07394SAndrew Turner 	    VGIC_32_BIT, dist_icactiver_read, dist_icactiver_write),
28847e07394SAndrew Turner 
28947e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_IPRIORITYR(0), GICD_IPRIORITYR(32), 4,
29047e07394SAndrew Turner 	    VGIC_32_BIT | VGIC_8_BIT),
29147e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_IPRIORITYR(32), GICD_IPRIORITYR(1024), 4,
29247e07394SAndrew Turner 	    VGIC_32_BIT | VGIC_8_BIT, dist_ipriorityr_read,
29347e07394SAndrew Turner 	    dist_ipriorityr_write),
29447e07394SAndrew Turner 
29547e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_ITARGETSR(0), GICD_ITARGETSR(1024), 4,
29647e07394SAndrew Turner 	    VGIC_32_BIT | VGIC_8_BIT),
29747e07394SAndrew Turner 
29847e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_ICFGR(0), GICD_ICFGR(32), 4,
29947e07394SAndrew Turner 	    VGIC_32_BIT),
30047e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_ICFGR(32), GICD_ICFGR(1024), 4,
30147e07394SAndrew Turner 	    VGIC_32_BIT, dist_icfgr_read, dist_icfgr_write),
30247e07394SAndrew Turner /*
30347e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_IGRPMODR(0), GICD_IGRPMODR(1024), 4,
30447e07394SAndrew Turner 	    VGIC_32_BIT, dist_igrpmodr_read, dist_igrpmodr_write),
30547e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_NSACR(0), GICD_NSACR(1024), 4,
30647e07394SAndrew Turner 	    VGIC_32_BIT, dist_nsacr_read, dist_nsacr_write),
30747e07394SAndrew Turner */
30847e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICD_SGIR, 4, VGIC_32_BIT),
30947e07394SAndrew Turner /*
31047e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_CPENDSGIR(0), GICD_CPENDSGIR(1024), 4,
31147e07394SAndrew Turner 	    VGIC_32_BIT | VGIC_8_BIT, dist_cpendsgir_read,
31247e07394SAndrew Turner 	    dist_cpendsgir_write),
31347e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_SPENDSGIR(0), GICD_SPENDSGIR(1024), 4,
31447e07394SAndrew Turner 	    VGIC_32_BIT | VGIC_8_BIT, dist_spendsgir_read,
31547e07394SAndrew Turner 	    dist_spendsgir_write),
31647e07394SAndrew Turner */
31747e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICD_IROUTER(32), GICD_IROUTER(1024), 8,
31847e07394SAndrew Turner 	    VGIC_64_BIT | VGIC_32_BIT, dist_irouter_read, dist_irouter_write),
31947e07394SAndrew Turner 
32047e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR4, GICD_PIDR2, 4, VGIC_32_BIT),
32147e07394SAndrew Turner 	VGIC_REGISTER(GICD_PIDR2, 4, VGIC_32_BIT, gic_pidr2_read,
32247e07394SAndrew Turner 	    gic_ignore_write),
32347e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR2 + 4, GICD_SIZE, 4, VGIC_32_BIT),
32447e07394SAndrew Turner };
32547e07394SAndrew Turner 
32647e07394SAndrew Turner /* GICR_CTLR - Ignore writes as no bits can be set */
32747e07394SAndrew Turner static register_read redist_ctlr_read;
32847e07394SAndrew Turner /* GICR_IIDR */
32947e07394SAndrew Turner static register_read redist_iidr_read;
33047e07394SAndrew Turner /* GICR_TYPER */
33147e07394SAndrew Turner static register_read redist_typer_read;
33247e07394SAndrew Turner /* GICR_STATUSR - RAZ/WI as we don't report errors (yet) */
33347e07394SAndrew Turner /* GICR_WAKER - RAZ/WI from non-secure mode */
33447e07394SAndrew Turner /* GICR_SETLPIR - RAZ/WI as no LPIs are supported */
33547e07394SAndrew Turner /* GICR_CLRLPIR - RAZ/WI as no LPIs are supported */
33647e07394SAndrew Turner /* GICR_PROPBASER - RAZ/WI as no LPIs are supported */
33747e07394SAndrew Turner /* GICR_PENDBASER - RAZ/WI as no LPIs are supported */
33847e07394SAndrew Turner /* GICR_INVLPIR - RAZ/WI as no LPIs are supported */
33947e07394SAndrew Turner /* GICR_INVALLR - RAZ/WI as no LPIs are supported */
34047e07394SAndrew Turner /* GICR_SYNCR - RAZ/WI as no LPIs are supported */
34147e07394SAndrew Turner 
34247e07394SAndrew Turner static struct vgic_register redist_rd_registers[] = {
34347e07394SAndrew Turner 	VGIC_REGISTER(GICR_CTLR, 4, VGIC_32_BIT, redist_ctlr_read,
34447e07394SAndrew Turner 	    gic_ignore_write),
34547e07394SAndrew Turner 	VGIC_REGISTER(GICR_IIDR, 4, VGIC_32_BIT, redist_iidr_read,
34647e07394SAndrew Turner 	    gic_ignore_write),
34747e07394SAndrew Turner 	VGIC_REGISTER(GICR_TYPER, 8, VGIC_64_BIT | VGIC_32_BIT,
34847e07394SAndrew Turner 	    redist_typer_read, gic_ignore_write),
34947e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_STATUSR, 4, VGIC_32_BIT),
35047e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_WAKER, 4, VGIC_32_BIT),
35147e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_SETLPIR, 8, VGIC_64_BIT | VGIC_32_BIT),
35247e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_CLRLPIR, 8, VGIC_64_BIT | VGIC_32_BIT),
35347e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_PROPBASER, 8, VGIC_64_BIT | VGIC_32_BIT),
35447e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_PENDBASER, 8, VGIC_64_BIT | VGIC_32_BIT),
35547e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_INVLPIR, 8, VGIC_64_BIT | VGIC_32_BIT),
35647e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_INVALLR, 8, VGIC_64_BIT | VGIC_32_BIT),
35747e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_SYNCR, 4, VGIC_32_BIT),
35847e07394SAndrew Turner 
35947e07394SAndrew Turner 	/* These are identical to the dist registers */
36047e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR4, GICD_PIDR2, 4, VGIC_32_BIT),
36147e07394SAndrew Turner 	VGIC_REGISTER(GICD_PIDR2, 4, VGIC_32_BIT, gic_pidr2_read,
36247e07394SAndrew Turner 	    gic_ignore_write),
36347e07394SAndrew Turner 	VGIC_REGISTER_RANGE_RAZ_WI(GICD_PIDR2 + 4, GICD_SIZE, 4,
36447e07394SAndrew Turner 	    VGIC_32_BIT),
36547e07394SAndrew Turner };
36647e07394SAndrew Turner 
36747e07394SAndrew Turner /* GICR_IGROUPR0 - RAZ/WI from non-secure mode */
36847e07394SAndrew Turner /* GICR_ISENABLER0 */
36947e07394SAndrew Turner static register_read redist_ienabler0_read;
37047e07394SAndrew Turner static register_write redist_isenabler0_write;
37147e07394SAndrew Turner /* GICR_ICENABLER0 */
37247e07394SAndrew Turner static register_write redist_icenabler0_write;
37347e07394SAndrew Turner /* GICR_ISPENDR0 */
37447e07394SAndrew Turner static register_read redist_ipendr0_read;
37547e07394SAndrew Turner static register_write redist_ispendr0_write;
37647e07394SAndrew Turner /* GICR_ICPENDR0 */
37747e07394SAndrew Turner static register_write redist_icpendr0_write;
37847e07394SAndrew Turner /* GICR_ISACTIVER0 */
37947e07394SAndrew Turner static register_read redist_iactiver0_read;
38047e07394SAndrew Turner static register_write redist_isactiver0_write;
38147e07394SAndrew Turner /* GICR_ICACTIVER0 */
38247e07394SAndrew Turner static register_write redist_icactiver0_write;
38347e07394SAndrew Turner /* GICR_IPRIORITYR */
38447e07394SAndrew Turner static register_read redist_ipriorityr_read;
38547e07394SAndrew Turner static register_write redist_ipriorityr_write;
38647e07394SAndrew Turner /* GICR_ICFGR0 - RAZ/WI from non-secure mode */
38747e07394SAndrew Turner /* GICR_ICFGR1 */
38847e07394SAndrew Turner static register_read redist_icfgr1_read;
38947e07394SAndrew Turner static register_write redist_icfgr1_write;
39047e07394SAndrew Turner /* GICR_IGRPMODR0 - RAZ/WI from non-secure mode */
39147e07394SAndrew Turner /* GICR_NSCAR - RAZ/WI from non-secure mode */
39247e07394SAndrew Turner 
39347e07394SAndrew Turner static struct vgic_register redist_sgi_registers[] = {
39447e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_IGROUPR0, 4, VGIC_32_BIT),
39547e07394SAndrew Turner 	VGIC_REGISTER(GICR_ISENABLER0, 4, VGIC_32_BIT, redist_ienabler0_read,
39647e07394SAndrew Turner 	    redist_isenabler0_write),
39747e07394SAndrew Turner 	VGIC_REGISTER(GICR_ICENABLER0, 4, VGIC_32_BIT, redist_ienabler0_read,
39847e07394SAndrew Turner 	    redist_icenabler0_write),
39947e07394SAndrew Turner 	VGIC_REGISTER(GICR_ISPENDR0, 4, VGIC_32_BIT, redist_ipendr0_read,
40047e07394SAndrew Turner 	    redist_ispendr0_write),
40147e07394SAndrew Turner 	VGIC_REGISTER(GICR_ICPENDR0, 4, VGIC_32_BIT, redist_ipendr0_read,
40247e07394SAndrew Turner 	    redist_icpendr0_write),
40347e07394SAndrew Turner 	VGIC_REGISTER(GICR_ISACTIVER0, 4, VGIC_32_BIT, redist_iactiver0_read,
40447e07394SAndrew Turner 	    redist_isactiver0_write),
40547e07394SAndrew Turner 	VGIC_REGISTER(GICR_ICACTIVER0, 4, VGIC_32_BIT, redist_iactiver0_read,
40647e07394SAndrew Turner 	    redist_icactiver0_write),
40747e07394SAndrew Turner 	VGIC_REGISTER_RANGE(GICR_IPRIORITYR(0), GICR_IPRIORITYR(32), 4,
40847e07394SAndrew Turner 	    VGIC_32_BIT | VGIC_8_BIT, redist_ipriorityr_read,
40947e07394SAndrew Turner 	    redist_ipriorityr_write),
41047e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_ICFGR0, 4, VGIC_32_BIT),
41147e07394SAndrew Turner 	VGIC_REGISTER(GICR_ICFGR1, 4, VGIC_32_BIT, redist_icfgr1_read,
41247e07394SAndrew Turner 	    redist_icfgr1_write),
41347e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_IGRPMODR0, 4, VGIC_32_BIT),
41447e07394SAndrew Turner 	VGIC_REGISTER_RAZ_WI(GICR_NSACR, 4, VGIC_32_BIT),
41547e07394SAndrew Turner };
41647e07394SAndrew Turner 
41747e07394SAndrew Turner static struct vgic_v3_virt_features virt_features;
41847e07394SAndrew Turner 
41947e07394SAndrew Turner static struct vgic_v3_irq *vgic_v3_get_irq(struct hyp *, int, uint32_t);
42047e07394SAndrew Turner static void vgic_v3_release_irq(struct vgic_v3_irq *);
42147e07394SAndrew Turner 
42247e07394SAndrew Turner /* TODO: Move to a common file */
42347e07394SAndrew Turner static int
mpidr_to_vcpu(struct hyp * hyp,uint64_t mpidr)42447e07394SAndrew Turner mpidr_to_vcpu(struct hyp *hyp, uint64_t mpidr)
42547e07394SAndrew Turner {
42647e07394SAndrew Turner 	struct vm *vm;
42747e07394SAndrew Turner 	struct hypctx *hypctx;
42847e07394SAndrew Turner 
42947e07394SAndrew Turner 	vm = hyp->vm;
43047e07394SAndrew Turner 	for (int i = 0; i < vm_get_maxcpus(vm); i++) {
43147e07394SAndrew Turner 		hypctx = hyp->ctx[i];
43247e07394SAndrew Turner 		if (hypctx != NULL && (hypctx->vmpidr_el2 & GICD_AFF) == mpidr)
43347e07394SAndrew Turner 			return (i);
43447e07394SAndrew Turner 	}
43547e07394SAndrew Turner 	return (-1);
43647e07394SAndrew Turner }
43747e07394SAndrew Turner 
43847e07394SAndrew Turner static void
vgic_v3_vminit(device_t dev,struct hyp * hyp)43947e07394SAndrew Turner vgic_v3_vminit(device_t dev, struct hyp *hyp)
44047e07394SAndrew Turner {
44147e07394SAndrew Turner 	struct vgic_v3 *vgic;
44247e07394SAndrew Turner 
44347e07394SAndrew Turner 	hyp->vgic = malloc(sizeof(*hyp->vgic), M_VGIC_V3,
44447e07394SAndrew Turner 	    M_WAITOK | M_ZERO);
44547e07394SAndrew Turner 	vgic = hyp->vgic;
44647e07394SAndrew Turner 
44747e07394SAndrew Turner 	/*
44847e07394SAndrew Turner 	 * Configure the Distributor control register. The register resets to an
44947e07394SAndrew Turner 	 * architecturally UNKNOWN value, so we reset to 0 to disable all
45047e07394SAndrew Turner 	 * functionality controlled by the register.
45147e07394SAndrew Turner 	 *
45247e07394SAndrew Turner 	 * The exception is GICD_CTLR.DS, which is RA0/WI when the Distributor
45347e07394SAndrew Turner 	 * supports one security state (ARM GIC Architecture Specification for
45447e07394SAndrew Turner 	 * GICv3 and GICv4, p. 4-464)
45547e07394SAndrew Turner 	 */
45647e07394SAndrew Turner 	vgic->gicd_ctlr = 0;
45747e07394SAndrew Turner 
45847e07394SAndrew Turner 	mtx_init(&vgic->dist_mtx, "VGICv3 Distributor lock", NULL,
45947e07394SAndrew Turner 	    MTX_SPIN);
46047e07394SAndrew Turner }
46147e07394SAndrew Turner 
46247e07394SAndrew Turner static void
vgic_v3_cpuinit(device_t dev,struct hypctx * hypctx)46347e07394SAndrew Turner vgic_v3_cpuinit(device_t dev, struct hypctx *hypctx)
46447e07394SAndrew Turner {
46547e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
46647e07394SAndrew Turner 	struct vgic_v3_irq *irq;
46747e07394SAndrew Turner 	int i, irqid;
46847e07394SAndrew Turner 
46947e07394SAndrew Turner 	hypctx->vgic_cpu = malloc(sizeof(*hypctx->vgic_cpu),
47047e07394SAndrew Turner 	    M_VGIC_V3, M_WAITOK | M_ZERO);
47147e07394SAndrew Turner 	vgic_cpu = hypctx->vgic_cpu;
47247e07394SAndrew Turner 
47347e07394SAndrew Turner 	mtx_init(&vgic_cpu->lr_mtx, "VGICv3 ICH_LR_EL2 lock", NULL, MTX_SPIN);
47447e07394SAndrew Turner 
47547e07394SAndrew Turner 	/* Set the SGI and PPI state */
47647e07394SAndrew Turner 	for (irqid = 0; irqid < VGIC_PRV_I_NUM; irqid++) {
47747e07394SAndrew Turner 		irq = &vgic_cpu->private_irqs[irqid];
47847e07394SAndrew Turner 
47947e07394SAndrew Turner 		mtx_init(&irq->irq_spinmtx, "VGIC IRQ spinlock", NULL,
48047e07394SAndrew Turner 		    MTX_SPIN);
48147e07394SAndrew Turner 		irq->irq = irqid;
48247e07394SAndrew Turner 		irq->mpidr = hypctx->vmpidr_el2 & GICD_AFF;
48347e07394SAndrew Turner 		irq->target_vcpu = vcpu_vcpuid(hypctx->vcpu);
48447e07394SAndrew Turner 		MPASS(irq->target_vcpu >= 0);
48547e07394SAndrew Turner 
48647e07394SAndrew Turner 		if (irqid < VGIC_SGI_NUM) {
48747e07394SAndrew Turner 			/* SGIs */
48847e07394SAndrew Turner 			irq->enabled = true;
48947e07394SAndrew Turner 			irq->config = VGIC_CONFIG_EDGE;
49047e07394SAndrew Turner 		} else {
49147e07394SAndrew Turner 			/* PPIs */
49247e07394SAndrew Turner 			irq->config = VGIC_CONFIG_LEVEL;
49347e07394SAndrew Turner 		}
49447e07394SAndrew Turner 		irq->priority = 0;
49547e07394SAndrew Turner 	}
49647e07394SAndrew Turner 
49747e07394SAndrew Turner 	/*
49847e07394SAndrew Turner 	 * Configure the Interrupt Controller Hyp Control Register.
49947e07394SAndrew Turner 	 *
50047e07394SAndrew Turner 	 * ICH_HCR_EL2_En: enable virtual CPU interface.
50147e07394SAndrew Turner 	 *
50247e07394SAndrew Turner 	 * Maintenance interrupts are disabled.
50347e07394SAndrew Turner 	 */
50447e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_hcr_el2 = ICH_HCR_EL2_En;
50547e07394SAndrew Turner 
50647e07394SAndrew Turner 	/*
50747e07394SAndrew Turner 	 * Configure the Interrupt Controller Virtual Machine Control Register.
50847e07394SAndrew Turner 	 *
50947e07394SAndrew Turner 	 * ICH_VMCR_EL2_VPMR: lowest priority mask for the VCPU interface
51047e07394SAndrew Turner 	 * ICH_VMCR_EL2_VBPR1_NO_PREEMPTION: disable interrupt preemption for
51147e07394SAndrew Turner 	 * Group 1 interrupts
51247e07394SAndrew Turner 	 * ICH_VMCR_EL2_VBPR0_NO_PREEMPTION: disable interrupt preemption for
51347e07394SAndrew Turner 	 * Group 0 interrupts
51447e07394SAndrew Turner 	 * ~ICH_VMCR_EL2_VEOIM: writes to EOI registers perform priority drop
51547e07394SAndrew Turner 	 * and interrupt deactivation.
51647e07394SAndrew Turner 	 * ICH_VMCR_EL2_VENG0: virtual Group 0 interrupts enabled.
51747e07394SAndrew Turner 	 * ICH_VMCR_EL2_VENG1: virtual Group 1 interrupts enabled.
51847e07394SAndrew Turner 	 */
51947e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_vmcr_el2 =
52047e07394SAndrew Turner 	    (virt_features.min_prio << ICH_VMCR_EL2_VPMR_SHIFT) |
52147e07394SAndrew Turner 	    ICH_VMCR_EL2_VBPR1_NO_PREEMPTION | ICH_VMCR_EL2_VBPR0_NO_PREEMPTION;
52247e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_vmcr_el2 &= ~ICH_VMCR_EL2_VEOIM;
52347e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_vmcr_el2 |= ICH_VMCR_EL2_VENG0 |
52447e07394SAndrew Turner 	    ICH_VMCR_EL2_VENG1;
52547e07394SAndrew Turner 
52647e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_lr_num = virt_features.ich_lr_num;
52747e07394SAndrew Turner 	for (i = 0; i < hypctx->vgic_v3_regs.ich_lr_num; i++)
52847e07394SAndrew Turner 		hypctx->vgic_v3_regs.ich_lr_el2[i] = 0UL;
52947e07394SAndrew Turner 	vgic_cpu->ich_lr_used = 0;
53047e07394SAndrew Turner 	TAILQ_INIT(&vgic_cpu->irq_act_pend);
53147e07394SAndrew Turner 
53247e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_apr_num = virt_features.ich_apr_num;
53347e07394SAndrew Turner }
53447e07394SAndrew Turner 
53547e07394SAndrew Turner static void
vgic_v3_cpucleanup(device_t dev,struct hypctx * hypctx)53647e07394SAndrew Turner vgic_v3_cpucleanup(device_t dev, struct hypctx *hypctx)
53747e07394SAndrew Turner {
53847e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
53947e07394SAndrew Turner 	struct vgic_v3_irq *irq;
54047e07394SAndrew Turner 	int irqid;
54147e07394SAndrew Turner 
54247e07394SAndrew Turner 	vgic_cpu = hypctx->vgic_cpu;
54347e07394SAndrew Turner 	for (irqid = 0; irqid < VGIC_PRV_I_NUM; irqid++) {
54447e07394SAndrew Turner 		irq = &vgic_cpu->private_irqs[irqid];
54547e07394SAndrew Turner 		mtx_destroy(&irq->irq_spinmtx);
54647e07394SAndrew Turner 	}
54747e07394SAndrew Turner 
54847e07394SAndrew Turner 	mtx_destroy(&vgic_cpu->lr_mtx);
54947e07394SAndrew Turner 	free(hypctx->vgic_cpu, M_VGIC_V3);
55047e07394SAndrew Turner }
55147e07394SAndrew Turner 
55247e07394SAndrew Turner static void
vgic_v3_vmcleanup(device_t dev,struct hyp * hyp)55347e07394SAndrew Turner vgic_v3_vmcleanup(device_t dev, struct hyp *hyp)
55447e07394SAndrew Turner {
55547e07394SAndrew Turner 	mtx_destroy(&hyp->vgic->dist_mtx);
55647e07394SAndrew Turner 	free(hyp->vgic, M_VGIC_V3);
55747e07394SAndrew Turner }
55847e07394SAndrew Turner 
55947e07394SAndrew Turner static int
vgic_v3_max_cpu_count(device_t dev,struct hyp * hyp)56047e07394SAndrew Turner vgic_v3_max_cpu_count(device_t dev, struct hyp *hyp)
56147e07394SAndrew Turner {
56247e07394SAndrew Turner 	struct vgic_v3 *vgic;
56347e07394SAndrew Turner 	size_t count;
56447e07394SAndrew Turner 	int16_t max_count;
56547e07394SAndrew Turner 
56647e07394SAndrew Turner 	vgic = hyp->vgic;
56747e07394SAndrew Turner 	max_count = vm_get_maxcpus(hyp->vm);
56847e07394SAndrew Turner 
56947e07394SAndrew Turner 	/* No registers, assume the maximum CPUs */
57047e07394SAndrew Turner 	if (vgic->redist_start == 0 && vgic->redist_end == 0)
57147e07394SAndrew Turner 		return (max_count);
57247e07394SAndrew Turner 
57347e07394SAndrew Turner 	count = (vgic->redist_end - vgic->redist_start) /
57447e07394SAndrew Turner 	    (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
57547e07394SAndrew Turner 
57647e07394SAndrew Turner 	/*
57747e07394SAndrew Turner 	 * max_count is smaller than INT_MAX so will also limit count
57847e07394SAndrew Turner 	 * to a positive integer value.
57947e07394SAndrew Turner 	 */
58047e07394SAndrew Turner 	if (count > max_count)
58147e07394SAndrew Turner 		return (max_count);
58247e07394SAndrew Turner 
58347e07394SAndrew Turner 	return (count);
58447e07394SAndrew Turner }
58547e07394SAndrew Turner 
58647e07394SAndrew Turner static bool
vgic_v3_irq_pending(struct vgic_v3_irq * irq)58747e07394SAndrew Turner vgic_v3_irq_pending(struct vgic_v3_irq *irq)
58847e07394SAndrew Turner {
58947e07394SAndrew Turner 	if ((irq->config & VGIC_CONFIG_MASK) == VGIC_CONFIG_LEVEL) {
59047e07394SAndrew Turner 		return (irq->pending || irq->level);
59147e07394SAndrew Turner 	} else {
59247e07394SAndrew Turner 		return (irq->pending);
59347e07394SAndrew Turner 	}
59447e07394SAndrew Turner }
59547e07394SAndrew Turner 
59647e07394SAndrew Turner static bool
vgic_v3_queue_irq(struct hyp * hyp,struct vgic_v3_cpu * vgic_cpu,int vcpuid,struct vgic_v3_irq * irq)59747e07394SAndrew Turner vgic_v3_queue_irq(struct hyp *hyp, struct vgic_v3_cpu *vgic_cpu,
59847e07394SAndrew Turner     int vcpuid, struct vgic_v3_irq *irq)
59947e07394SAndrew Turner {
60047e07394SAndrew Turner 	MPASS(vcpuid >= 0);
60147e07394SAndrew Turner 	MPASS(vcpuid < vm_get_maxcpus(hyp->vm));
60247e07394SAndrew Turner 
60347e07394SAndrew Turner 	mtx_assert(&vgic_cpu->lr_mtx, MA_OWNED);
60447e07394SAndrew Turner 	mtx_assert(&irq->irq_spinmtx, MA_OWNED);
60547e07394SAndrew Turner 
60647e07394SAndrew Turner 	/* No need to queue the IRQ */
60747e07394SAndrew Turner 	if (!irq->level && !irq->pending)
60847e07394SAndrew Turner 		return (false);
60947e07394SAndrew Turner 
61047e07394SAndrew Turner 	if (!irq->on_aplist) {
61147e07394SAndrew Turner 		irq->on_aplist = true;
61247e07394SAndrew Turner 		TAILQ_INSERT_TAIL(&vgic_cpu->irq_act_pend, irq, act_pend_list);
61347e07394SAndrew Turner 	}
61447e07394SAndrew Turner 	return (true);
61547e07394SAndrew Turner }
61647e07394SAndrew Turner 
61747e07394SAndrew Turner static uint64_t
gic_reg_value_64(uint64_t field,uint64_t val,u_int offset,u_int size)61847e07394SAndrew Turner gic_reg_value_64(uint64_t field, uint64_t val, u_int offset, u_int size)
61947e07394SAndrew Turner {
62047e07394SAndrew Turner 	uint32_t mask;
62147e07394SAndrew Turner 
62247e07394SAndrew Turner 	if (offset != 0 || size != 8) {
62347e07394SAndrew Turner 		mask = ((1ul << (size * 8)) - 1) << (offset * 8);
62447e07394SAndrew Turner 		/* Shift the new bits to the correct place */
62547e07394SAndrew Turner 		val <<= (offset * 8);
62647e07394SAndrew Turner 		/* Keep only the interesting bits */
62747e07394SAndrew Turner 		val &= mask;
62847e07394SAndrew Turner 		/* Add the bits we are keeping from the old value */
62947e07394SAndrew Turner 		val |= field & ~mask;
63047e07394SAndrew Turner 	}
63147e07394SAndrew Turner 
63247e07394SAndrew Turner 	return (val);
63347e07394SAndrew Turner }
63447e07394SAndrew Turner 
63547e07394SAndrew Turner static void
gic_pidr2_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)63647e07394SAndrew Turner gic_pidr2_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
63747e07394SAndrew Turner     void *arg)
63847e07394SAndrew Turner {
63947e07394SAndrew Turner 	*rval = GICR_PIDR2_ARCH_GICv3 << GICR_PIDR2_ARCH_SHIFT;
64047e07394SAndrew Turner }
64147e07394SAndrew Turner 
64247e07394SAndrew Turner /* Common read-only/write-ignored helpers */
64347e07394SAndrew Turner static void
gic_zero_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)64447e07394SAndrew Turner gic_zero_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
64547e07394SAndrew Turner     void *arg)
64647e07394SAndrew Turner {
64747e07394SAndrew Turner 	*rval = 0;
64847e07394SAndrew Turner }
64947e07394SAndrew Turner 
65047e07394SAndrew Turner static void
gic_ignore_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)65147e07394SAndrew Turner gic_ignore_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
65247e07394SAndrew Turner     uint64_t wval, void *arg)
65347e07394SAndrew Turner {
65447e07394SAndrew Turner 	/* Nothing to do */
65547e07394SAndrew Turner }
65647e07394SAndrew Turner 
65747e07394SAndrew Turner static uint64_t
read_enabler(struct hypctx * hypctx,int n)65847e07394SAndrew Turner read_enabler(struct hypctx *hypctx, int n)
65947e07394SAndrew Turner {
66047e07394SAndrew Turner 	struct vgic_v3_irq *irq;
66147e07394SAndrew Turner 	uint64_t ret;
66247e07394SAndrew Turner 	uint32_t irq_base;
66347e07394SAndrew Turner 	int i;
66447e07394SAndrew Turner 
66547e07394SAndrew Turner 	ret = 0;
66647e07394SAndrew Turner 	irq_base = n * 32;
66747e07394SAndrew Turner 	for (i = 0; i < 32; i++) {
66847e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
66947e07394SAndrew Turner 		    irq_base + i);
67047e07394SAndrew Turner 		if (irq == NULL)
67147e07394SAndrew Turner 			continue;
67247e07394SAndrew Turner 
67347e07394SAndrew Turner 		if (!irq->enabled)
67447e07394SAndrew Turner 			ret |= 1u << i;
67547e07394SAndrew Turner 		vgic_v3_release_irq(irq);
67647e07394SAndrew Turner 	}
67747e07394SAndrew Turner 
67847e07394SAndrew Turner 	return (ret);
67947e07394SAndrew Turner }
68047e07394SAndrew Turner 
68147e07394SAndrew Turner static void
write_enabler(struct hypctx * hypctx,int n,bool set,uint64_t val)68247e07394SAndrew Turner write_enabler(struct hypctx *hypctx,int n, bool set, uint64_t val)
68347e07394SAndrew Turner {
68447e07394SAndrew Turner 	struct vgic_v3_irq *irq;
68547e07394SAndrew Turner 	uint32_t irq_base;
68647e07394SAndrew Turner 	int i;
68747e07394SAndrew Turner 
68847e07394SAndrew Turner 	irq_base = n * 32;
68947e07394SAndrew Turner 	for (i = 0; i < 32; i++) {
69047e07394SAndrew Turner 		/* We only change interrupts when the appropriate bit is set */
69147e07394SAndrew Turner 		if ((val & (1u << i)) == 0)
69247e07394SAndrew Turner 			continue;
69347e07394SAndrew Turner 
69447e07394SAndrew Turner 		/* Find the interrupt this bit represents */
69547e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
69647e07394SAndrew Turner 		    irq_base + i);
69747e07394SAndrew Turner 		if (irq == NULL)
69847e07394SAndrew Turner 			continue;
69947e07394SAndrew Turner 
70047e07394SAndrew Turner 		irq->enabled = set;
70147e07394SAndrew Turner 		vgic_v3_release_irq(irq);
70247e07394SAndrew Turner 	}
70347e07394SAndrew Turner }
70447e07394SAndrew Turner 
70547e07394SAndrew Turner static uint64_t
read_pendr(struct hypctx * hypctx,int n)70647e07394SAndrew Turner read_pendr(struct hypctx *hypctx, int n)
70747e07394SAndrew Turner {
70847e07394SAndrew Turner 	struct vgic_v3_irq *irq;
70947e07394SAndrew Turner 	uint64_t ret;
71047e07394SAndrew Turner 	uint32_t irq_base;
71147e07394SAndrew Turner 	int i;
71247e07394SAndrew Turner 
71347e07394SAndrew Turner 	ret = 0;
71447e07394SAndrew Turner 	irq_base = n * 32;
71547e07394SAndrew Turner 	for (i = 0; i < 32; i++) {
71647e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
71747e07394SAndrew Turner 		    irq_base + i);
71847e07394SAndrew Turner 		if (irq == NULL)
71947e07394SAndrew Turner 			continue;
72047e07394SAndrew Turner 
72147e07394SAndrew Turner 		if (vgic_v3_irq_pending(irq))
72247e07394SAndrew Turner 			ret |= 1u << i;
72347e07394SAndrew Turner 		vgic_v3_release_irq(irq);
72447e07394SAndrew Turner 	}
72547e07394SAndrew Turner 
72647e07394SAndrew Turner 	return (ret);
72747e07394SAndrew Turner }
72847e07394SAndrew Turner 
72947e07394SAndrew Turner static uint64_t
write_pendr(struct hypctx * hypctx,int n,bool set,uint64_t val)73047e07394SAndrew Turner write_pendr(struct hypctx *hypctx, int n, bool set, uint64_t val)
73147e07394SAndrew Turner {
73247e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
73347e07394SAndrew Turner 	struct vgic_v3_irq *irq;
73447e07394SAndrew Turner 	struct hyp *hyp;
73547e07394SAndrew Turner 	struct hypctx *target_hypctx;
73647e07394SAndrew Turner 	uint64_t ret;
73747e07394SAndrew Turner 	uint32_t irq_base;
73847e07394SAndrew Turner 	int target_vcpu, i;
73947e07394SAndrew Turner 	bool notify;
74047e07394SAndrew Turner 
74147e07394SAndrew Turner 	hyp = hypctx->hyp;
74247e07394SAndrew Turner 	ret = 0;
74347e07394SAndrew Turner 	irq_base = n * 32;
74447e07394SAndrew Turner 	for (i = 0; i < 32; i++) {
74547e07394SAndrew Turner 		/* We only change interrupts when the appropriate bit is set */
74647e07394SAndrew Turner 		if ((val & (1u << i)) == 0)
74747e07394SAndrew Turner 			continue;
74847e07394SAndrew Turner 
74947e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
75047e07394SAndrew Turner 		    irq_base + i);
75147e07394SAndrew Turner 		if (irq == NULL)
75247e07394SAndrew Turner 			continue;
75347e07394SAndrew Turner 
75447e07394SAndrew Turner 		notify = false;
75547e07394SAndrew Turner 		target_vcpu = irq->target_vcpu;
75647e07394SAndrew Turner 		if (target_vcpu < 0)
75747e07394SAndrew Turner 			goto next_irq;
75847e07394SAndrew Turner 		target_hypctx = hyp->ctx[target_vcpu];
75947e07394SAndrew Turner 		if (target_hypctx == NULL)
76047e07394SAndrew Turner 			goto next_irq;
76147e07394SAndrew Turner 		vgic_cpu = target_hypctx->vgic_cpu;
76247e07394SAndrew Turner 
76347e07394SAndrew Turner 		if (!set) {
76447e07394SAndrew Turner 			/* pending -> not pending */
76547e07394SAndrew Turner 			irq->pending = false;
76647e07394SAndrew Turner 		} else {
76747e07394SAndrew Turner 			irq->pending = true;
76847e07394SAndrew Turner 			mtx_lock_spin(&vgic_cpu->lr_mtx);
76947e07394SAndrew Turner 			notify = vgic_v3_queue_irq(hyp, vgic_cpu, target_vcpu,
77047e07394SAndrew Turner 			    irq);
77147e07394SAndrew Turner 			mtx_unlock_spin(&vgic_cpu->lr_mtx);
77247e07394SAndrew Turner 		}
77347e07394SAndrew Turner next_irq:
77447e07394SAndrew Turner 		vgic_v3_release_irq(irq);
77547e07394SAndrew Turner 
77647e07394SAndrew Turner 		if (notify)
77747e07394SAndrew Turner 			vcpu_notify_event(vm_vcpu(hyp->vm, target_vcpu));
77847e07394SAndrew Turner 	}
77947e07394SAndrew Turner 
78047e07394SAndrew Turner 	return (ret);
78147e07394SAndrew Turner }
78247e07394SAndrew Turner 
78347e07394SAndrew Turner static uint64_t
read_activer(struct hypctx * hypctx,int n)78447e07394SAndrew Turner read_activer(struct hypctx *hypctx, int n)
78547e07394SAndrew Turner {
78647e07394SAndrew Turner 	struct vgic_v3_irq *irq;
78747e07394SAndrew Turner 	uint64_t ret;
78847e07394SAndrew Turner 	uint32_t irq_base;
78947e07394SAndrew Turner 	int i;
79047e07394SAndrew Turner 
79147e07394SAndrew Turner 	ret = 0;
79247e07394SAndrew Turner 	irq_base = n * 32;
79347e07394SAndrew Turner 	for (i = 0; i < 32; i++) {
79447e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
79547e07394SAndrew Turner 		    irq_base + i);
79647e07394SAndrew Turner 		if (irq == NULL)
79747e07394SAndrew Turner 			continue;
79847e07394SAndrew Turner 
79947e07394SAndrew Turner 		if (irq->active)
80047e07394SAndrew Turner 			ret |= 1u << i;
80147e07394SAndrew Turner 		vgic_v3_release_irq(irq);
80247e07394SAndrew Turner 	}
80347e07394SAndrew Turner 
80447e07394SAndrew Turner 	return (ret);
80547e07394SAndrew Turner }
80647e07394SAndrew Turner 
80747e07394SAndrew Turner static void
write_activer(struct hypctx * hypctx,u_int n,bool set,uint64_t val)80847e07394SAndrew Turner write_activer(struct hypctx *hypctx, u_int n, bool set, uint64_t val)
80947e07394SAndrew Turner {
81047e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
81147e07394SAndrew Turner 	struct vgic_v3_irq *irq;
81247e07394SAndrew Turner 	struct hyp *hyp;
81347e07394SAndrew Turner 	struct hypctx *target_hypctx;
81447e07394SAndrew Turner 	uint32_t irq_base;
81547e07394SAndrew Turner 	int target_vcpu, i;
81647e07394SAndrew Turner 	bool notify;
81747e07394SAndrew Turner 
81847e07394SAndrew Turner 	hyp = hypctx->hyp;
81947e07394SAndrew Turner 	irq_base = n * 32;
82047e07394SAndrew Turner 	for (i = 0; i < 32; i++) {
82147e07394SAndrew Turner 		/* We only change interrupts when the appropriate bit is set */
82247e07394SAndrew Turner 		if ((val & (1u << i)) == 0)
82347e07394SAndrew Turner 			continue;
82447e07394SAndrew Turner 
82547e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
82647e07394SAndrew Turner 		    irq_base + i);
82747e07394SAndrew Turner 		if (irq == NULL)
82847e07394SAndrew Turner 			continue;
82947e07394SAndrew Turner 
83047e07394SAndrew Turner 		notify = false;
83147e07394SAndrew Turner 		target_vcpu = irq->target_vcpu;
83247e07394SAndrew Turner 		if (target_vcpu < 0)
83347e07394SAndrew Turner 			goto next_irq;
83447e07394SAndrew Turner 		target_hypctx = hyp->ctx[target_vcpu];
83547e07394SAndrew Turner 		if (target_hypctx == NULL)
83647e07394SAndrew Turner 			goto next_irq;
83747e07394SAndrew Turner 		vgic_cpu = target_hypctx->vgic_cpu;
83847e07394SAndrew Turner 
83947e07394SAndrew Turner 		if (!set) {
84047e07394SAndrew Turner 			/* active -> not active */
84147e07394SAndrew Turner 			irq->active = false;
84247e07394SAndrew Turner 		} else {
84347e07394SAndrew Turner 			/* not active -> active */
84447e07394SAndrew Turner 			irq->active = true;
84547e07394SAndrew Turner 			mtx_lock_spin(&vgic_cpu->lr_mtx);
84647e07394SAndrew Turner 			notify = vgic_v3_queue_irq(hyp, vgic_cpu, target_vcpu,
84747e07394SAndrew Turner 			    irq);
84847e07394SAndrew Turner 			mtx_unlock_spin(&vgic_cpu->lr_mtx);
84947e07394SAndrew Turner 		}
85047e07394SAndrew Turner next_irq:
85147e07394SAndrew Turner 		vgic_v3_release_irq(irq);
85247e07394SAndrew Turner 
85347e07394SAndrew Turner 		if (notify)
85447e07394SAndrew Turner 			vcpu_notify_event(vm_vcpu(hyp->vm, target_vcpu));
85547e07394SAndrew Turner 	}
85647e07394SAndrew Turner }
85747e07394SAndrew Turner 
85847e07394SAndrew Turner static uint64_t
read_priorityr(struct hypctx * hypctx,int n)85947e07394SAndrew Turner read_priorityr(struct hypctx *hypctx, int n)
86047e07394SAndrew Turner {
86147e07394SAndrew Turner 	struct vgic_v3_irq *irq;
86247e07394SAndrew Turner 	uint64_t ret;
86347e07394SAndrew Turner 	uint32_t irq_base;
86447e07394SAndrew Turner 	int i;
86547e07394SAndrew Turner 
86647e07394SAndrew Turner 	ret = 0;
86747e07394SAndrew Turner 	irq_base = n * 4;
86847e07394SAndrew Turner 	for (i = 0; i < 4; i++) {
86947e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
87047e07394SAndrew Turner 		    irq_base + i);
87147e07394SAndrew Turner 		if (irq == NULL)
87247e07394SAndrew Turner 			continue;
87347e07394SAndrew Turner 
87447e07394SAndrew Turner 		ret |= ((uint64_t)irq->priority) << (i * 8);
87547e07394SAndrew Turner 		vgic_v3_release_irq(irq);
87647e07394SAndrew Turner 	}
87747e07394SAndrew Turner 
87847e07394SAndrew Turner 	return (ret);
87947e07394SAndrew Turner }
88047e07394SAndrew Turner 
88147e07394SAndrew Turner static void
write_priorityr(struct hypctx * hypctx,u_int irq_base,u_int size,uint64_t val)88247e07394SAndrew Turner write_priorityr(struct hypctx *hypctx, u_int irq_base, u_int size, uint64_t val)
88347e07394SAndrew Turner {
88447e07394SAndrew Turner 	struct vgic_v3_irq *irq;
88547e07394SAndrew Turner 	int i;
88647e07394SAndrew Turner 
88747e07394SAndrew Turner 	for (i = 0; i < size; i++) {
88847e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
88947e07394SAndrew Turner 		    irq_base + i);
89047e07394SAndrew Turner 		if (irq == NULL)
89147e07394SAndrew Turner 			continue;
89247e07394SAndrew Turner 
89347e07394SAndrew Turner 		/* Set the priority. We support 32 priority steps (5 bits) */
89447e07394SAndrew Turner 		irq->priority = (val >> (i * 8)) & 0xf8;
89547e07394SAndrew Turner 		vgic_v3_release_irq(irq);
89647e07394SAndrew Turner 	}
89747e07394SAndrew Turner }
89847e07394SAndrew Turner 
89947e07394SAndrew Turner static uint64_t
read_config(struct hypctx * hypctx,int n)90047e07394SAndrew Turner read_config(struct hypctx *hypctx, int n)
90147e07394SAndrew Turner {
90247e07394SAndrew Turner 	struct vgic_v3_irq *irq;
90347e07394SAndrew Turner 	uint64_t ret;
90447e07394SAndrew Turner 	uint32_t irq_base;
90547e07394SAndrew Turner 	int i;
90647e07394SAndrew Turner 
90747e07394SAndrew Turner 	ret = 0;
90847e07394SAndrew Turner 	irq_base = n * 16;
90947e07394SAndrew Turner 	for (i = 0; i < 16; i++) {
91047e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
91147e07394SAndrew Turner 		    irq_base + i);
91247e07394SAndrew Turner 		if (irq == NULL)
91347e07394SAndrew Turner 			continue;
91447e07394SAndrew Turner 
91547e07394SAndrew Turner 		ret |= ((uint64_t)irq->config) << (i * 2);
91647e07394SAndrew Turner 		vgic_v3_release_irq(irq);
91747e07394SAndrew Turner 	}
91847e07394SAndrew Turner 
91947e07394SAndrew Turner 	return (ret);
92047e07394SAndrew Turner }
92147e07394SAndrew Turner 
92247e07394SAndrew Turner static void
write_config(struct hypctx * hypctx,int n,uint64_t val)92347e07394SAndrew Turner write_config(struct hypctx *hypctx, int n, uint64_t val)
92447e07394SAndrew Turner {
92547e07394SAndrew Turner 	struct vgic_v3_irq *irq;
92647e07394SAndrew Turner 	uint32_t irq_base;
92747e07394SAndrew Turner 	int i;
92847e07394SAndrew Turner 
92947e07394SAndrew Turner 	irq_base = n * 16;
93047e07394SAndrew Turner 	for (i = 0; i < 16; i++) {
93147e07394SAndrew Turner 		/*
93247e07394SAndrew Turner 		 * The config can't be changed for SGIs and PPIs. SGIs have
93347e07394SAndrew Turner 		 * an edge-triggered behaviour, and the register is
93447e07394SAndrew Turner 		 * implementation defined to be read-only for PPIs.
93547e07394SAndrew Turner 		 */
93647e07394SAndrew Turner 		if (irq_base + i < VGIC_PRV_I_NUM)
93747e07394SAndrew Turner 			continue;
93847e07394SAndrew Turner 
93947e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
94047e07394SAndrew Turner 		    irq_base + i);
94147e07394SAndrew Turner 		if (irq == NULL)
94247e07394SAndrew Turner 			continue;
94347e07394SAndrew Turner 
94447e07394SAndrew Turner 		/* Bit 0 is RES0 */
94547e07394SAndrew Turner 		irq->config = (val >> (i * 2)) & VGIC_CONFIG_MASK;
94647e07394SAndrew Turner 		vgic_v3_release_irq(irq);
94747e07394SAndrew Turner 	}
94847e07394SAndrew Turner }
94947e07394SAndrew Turner 
95047e07394SAndrew Turner static uint64_t
read_route(struct hypctx * hypctx,int n)95147e07394SAndrew Turner read_route(struct hypctx *hypctx, int n)
95247e07394SAndrew Turner {
95347e07394SAndrew Turner 	struct vgic_v3_irq *irq;
95447e07394SAndrew Turner 	uint64_t mpidr;
95547e07394SAndrew Turner 
95647e07394SAndrew Turner 	irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu), n);
95747e07394SAndrew Turner 	if (irq == NULL)
95847e07394SAndrew Turner 		return (0);
95947e07394SAndrew Turner 
96047e07394SAndrew Turner 	mpidr = irq->mpidr;
96147e07394SAndrew Turner 	vgic_v3_release_irq(irq);
96247e07394SAndrew Turner 
96347e07394SAndrew Turner 	return (mpidr);
96447e07394SAndrew Turner }
96547e07394SAndrew Turner 
96647e07394SAndrew Turner static void
write_route(struct hypctx * hypctx,int n,uint64_t val,u_int offset,u_int size)96747e07394SAndrew Turner write_route(struct hypctx *hypctx, int n, uint64_t val, u_int offset,
96847e07394SAndrew Turner     u_int size)
96947e07394SAndrew Turner {
97047e07394SAndrew Turner 	struct vgic_v3_irq *irq;
97147e07394SAndrew Turner 
97247e07394SAndrew Turner 	irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu), n);
97347e07394SAndrew Turner 	if (irq == NULL)
97447e07394SAndrew Turner 		return;
97547e07394SAndrew Turner 
97647e07394SAndrew Turner 	irq->mpidr = gic_reg_value_64(irq->mpidr, val, offset, size) & GICD_AFF;
97747e07394SAndrew Turner 	irq->target_vcpu = mpidr_to_vcpu(hypctx->hyp, irq->mpidr);
97847e07394SAndrew Turner 	/*
97947e07394SAndrew Turner 	 * If the interrupt is pending we can either use the old mpidr, or
98047e07394SAndrew Turner 	 * the new mpidr. To simplify this code we use the old value so we
98147e07394SAndrew Turner 	 * don't need to move the interrupt until the next time it is
98247e07394SAndrew Turner 	 * moved to the pending state.
98347e07394SAndrew Turner 	 */
98447e07394SAndrew Turner 	vgic_v3_release_irq(irq);
98547e07394SAndrew Turner }
98647e07394SAndrew Turner 
98747e07394SAndrew Turner /*
98847e07394SAndrew Turner  * Distributor register handlers.
98947e07394SAndrew Turner  */
99047e07394SAndrew Turner /* GICD_CTLR */
99147e07394SAndrew Turner static void
dist_ctlr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)99247e07394SAndrew Turner dist_ctlr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
99347e07394SAndrew Turner     void *arg)
99447e07394SAndrew Turner {
99547e07394SAndrew Turner 	struct hyp *hyp;
99647e07394SAndrew Turner 	struct vgic_v3 *vgic;
99747e07394SAndrew Turner 
99847e07394SAndrew Turner 	hyp = hypctx->hyp;
99947e07394SAndrew Turner 	vgic = hyp->vgic;
100047e07394SAndrew Turner 
100147e07394SAndrew Turner 	mtx_lock_spin(&vgic->dist_mtx);
100247e07394SAndrew Turner 	*rval = vgic->gicd_ctlr;
100347e07394SAndrew Turner 	mtx_unlock_spin(&vgic->dist_mtx);
100447e07394SAndrew Turner 
100547e07394SAndrew Turner 	/* Writes are never pending */
100647e07394SAndrew Turner 	*rval &= ~GICD_CTLR_RWP;
100747e07394SAndrew Turner }
100847e07394SAndrew Turner 
100947e07394SAndrew Turner static void
dist_ctlr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)101047e07394SAndrew Turner dist_ctlr_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
101147e07394SAndrew Turner     uint64_t wval, void *arg)
101247e07394SAndrew Turner {
101347e07394SAndrew Turner 	struct vgic_v3 *vgic;
101447e07394SAndrew Turner 
101547e07394SAndrew Turner 	MPASS(offset == 0);
101647e07394SAndrew Turner 	MPASS(size == 4);
101747e07394SAndrew Turner 	vgic = hypctx->hyp->vgic;
101847e07394SAndrew Turner 
101947e07394SAndrew Turner 	/*
102047e07394SAndrew Turner 	 * GICv2 backwards compatibility is not implemented so
102147e07394SAndrew Turner 	 * ARE_NS is RAO/WI. This means EnableGrp1 is RES0.
102247e07394SAndrew Turner 	 *
102347e07394SAndrew Turner 	 * EnableGrp1A is supported, and RWP is read-only.
102447e07394SAndrew Turner 	 *
102547e07394SAndrew Turner 	 * All other bits are RES0 from non-secure mode as we
102647e07394SAndrew Turner 	 * implement as if we are in a system with two security
102747e07394SAndrew Turner 	 * states.
102847e07394SAndrew Turner 	 */
102947e07394SAndrew Turner 	wval &= GICD_CTLR_G1A;
103047e07394SAndrew Turner 	wval |= GICD_CTLR_ARE_NS;
103147e07394SAndrew Turner 	mtx_lock_spin(&vgic->dist_mtx);
103247e07394SAndrew Turner 	vgic->gicd_ctlr = wval;
103347e07394SAndrew Turner 	/* TODO: Wake any vcpus that have interrupts pending */
103447e07394SAndrew Turner 	mtx_unlock_spin(&vgic->dist_mtx);
103547e07394SAndrew Turner }
103647e07394SAndrew Turner 
103747e07394SAndrew Turner /* GICD_TYPER */
103847e07394SAndrew Turner static void
dist_typer_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)103947e07394SAndrew Turner dist_typer_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
104047e07394SAndrew Turner     void *arg)
104147e07394SAndrew Turner {
104247e07394SAndrew Turner 	uint32_t typer;
104347e07394SAndrew Turner 
104447e07394SAndrew Turner 	typer = (10 - 1) << GICD_TYPER_IDBITS_SHIFT;
104547e07394SAndrew Turner 	typer |= GICD_TYPER_MBIS;
104647e07394SAndrew Turner 	/* ITLinesNumber: */
104747e07394SAndrew Turner 	typer |= howmany(VGIC_NIRQS + 1, 32) - 1;
104847e07394SAndrew Turner 
104947e07394SAndrew Turner 	*rval = typer;
105047e07394SAndrew Turner }
105147e07394SAndrew Turner 
105247e07394SAndrew Turner /* GICD_IIDR */
105347e07394SAndrew Turner static void
dist_iidr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)105447e07394SAndrew Turner dist_iidr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
105547e07394SAndrew Turner {
105647e07394SAndrew Turner 	*rval = VGIC_IIDR;
105747e07394SAndrew Turner }
105847e07394SAndrew Turner 
105947e07394SAndrew Turner /* GICD_SETSPI_NSR & GICD_CLRSPI_NSR */
106047e07394SAndrew Turner static void
dist_setclrspi_nsr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)106147e07394SAndrew Turner dist_setclrspi_nsr_write(struct hypctx *hypctx, u_int reg, u_int offset,
106247e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
106347e07394SAndrew Turner {
106447e07394SAndrew Turner 	uint32_t irqid;
106547e07394SAndrew Turner 
106647e07394SAndrew Turner 	MPASS(offset == 0);
106747e07394SAndrew Turner 	MPASS(size == 4);
106847e07394SAndrew Turner 	irqid = wval & GICD_SPI_INTID_MASK;
106947e07394SAndrew Turner 	INJECT_IRQ(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu), irqid,
107047e07394SAndrew Turner 	    reg == GICD_SETSPI_NSR);
107147e07394SAndrew Turner }
107247e07394SAndrew Turner 
107347e07394SAndrew Turner /* GICD_ISENABLER */
107447e07394SAndrew Turner static void
dist_isenabler_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)107547e07394SAndrew Turner dist_isenabler_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
107647e07394SAndrew Turner {
107747e07394SAndrew Turner 	int n;
107847e07394SAndrew Turner 
107947e07394SAndrew Turner 	n = (reg - GICD_ISENABLER(0)) / 4;
108047e07394SAndrew Turner 	/* GICD_ISENABLER0 is RAZ/WI so handled separately */
108147e07394SAndrew Turner 	MPASS(n > 0);
108247e07394SAndrew Turner 	*rval = read_enabler(hypctx, n);
108347e07394SAndrew Turner }
108447e07394SAndrew Turner 
108547e07394SAndrew Turner static void
dist_isenabler_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)108647e07394SAndrew Turner dist_isenabler_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
108747e07394SAndrew Turner     uint64_t wval, void *arg)
108847e07394SAndrew Turner {
108947e07394SAndrew Turner 	int n;
109047e07394SAndrew Turner 
109147e07394SAndrew Turner 	MPASS(offset == 0);
109247e07394SAndrew Turner 	MPASS(size == 4);
109347e07394SAndrew Turner 	n = (reg - GICD_ISENABLER(0)) / 4;
109447e07394SAndrew Turner 	/* GICD_ISENABLER0 is RAZ/WI so handled separately */
109547e07394SAndrew Turner 	MPASS(n > 0);
109647e07394SAndrew Turner 	write_enabler(hypctx, n, true, wval);
109747e07394SAndrew Turner }
109847e07394SAndrew Turner 
109947e07394SAndrew Turner /* GICD_ICENABLER */
110047e07394SAndrew Turner static void
dist_icenabler_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)110147e07394SAndrew Turner dist_icenabler_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
110247e07394SAndrew Turner {
110347e07394SAndrew Turner 	int n;
110447e07394SAndrew Turner 
110547e07394SAndrew Turner 	n = (reg - GICD_ICENABLER(0)) / 4;
110647e07394SAndrew Turner 	/* GICD_ICENABLER0 is RAZ/WI so handled separately */
110747e07394SAndrew Turner 	MPASS(n > 0);
110847e07394SAndrew Turner 	*rval = read_enabler(hypctx, n);
110947e07394SAndrew Turner }
111047e07394SAndrew Turner 
111147e07394SAndrew Turner static void
dist_icenabler_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)111247e07394SAndrew Turner dist_icenabler_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
111347e07394SAndrew Turner     uint64_t wval, void *arg)
111447e07394SAndrew Turner {
111547e07394SAndrew Turner 	int n;
111647e07394SAndrew Turner 
111747e07394SAndrew Turner 	MPASS(offset == 0);
111847e07394SAndrew Turner 	MPASS(size == 4);
111947e07394SAndrew Turner 	n = (reg - GICD_ISENABLER(0)) / 4;
112047e07394SAndrew Turner 	/* GICD_ICENABLER0 is RAZ/WI so handled separately */
112147e07394SAndrew Turner 	MPASS(n > 0);
112247e07394SAndrew Turner 	write_enabler(hypctx, n, false, wval);
112347e07394SAndrew Turner }
112447e07394SAndrew Turner 
112547e07394SAndrew Turner /* GICD_ISPENDR */
112647e07394SAndrew Turner static void
dist_ispendr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)112747e07394SAndrew Turner dist_ispendr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
112847e07394SAndrew Turner {
112947e07394SAndrew Turner 	int n;
113047e07394SAndrew Turner 
113147e07394SAndrew Turner 	n = (reg - GICD_ISPENDR(0)) / 4;
113247e07394SAndrew Turner 	/* GICD_ISPENDR0 is RAZ/WI so handled separately */
113347e07394SAndrew Turner 	MPASS(n > 0);
113447e07394SAndrew Turner 	*rval = read_pendr(hypctx, n);
113547e07394SAndrew Turner }
113647e07394SAndrew Turner 
113747e07394SAndrew Turner static void
dist_ispendr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)113847e07394SAndrew Turner dist_ispendr_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
113947e07394SAndrew Turner     uint64_t wval, void *arg)
114047e07394SAndrew Turner {
114147e07394SAndrew Turner 	int n;
114247e07394SAndrew Turner 
114347e07394SAndrew Turner 	MPASS(offset == 0);
114447e07394SAndrew Turner 	MPASS(size == 4);
114547e07394SAndrew Turner 	n = (reg - GICD_ISPENDR(0)) / 4;
114647e07394SAndrew Turner 	/* GICD_ISPENDR0 is RAZ/WI so handled separately */
114747e07394SAndrew Turner 	MPASS(n > 0);
114847e07394SAndrew Turner 	write_pendr(hypctx, n, true, wval);
114947e07394SAndrew Turner }
115047e07394SAndrew Turner 
115147e07394SAndrew Turner /* GICD_ICPENDR */
115247e07394SAndrew Turner static void
dist_icpendr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)115347e07394SAndrew Turner dist_icpendr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
115447e07394SAndrew Turner {
115547e07394SAndrew Turner 	int n;
115647e07394SAndrew Turner 
115747e07394SAndrew Turner 	n = (reg - GICD_ICPENDR(0)) / 4;
115847e07394SAndrew Turner 	/* GICD_ICPENDR0 is RAZ/WI so handled separately */
115947e07394SAndrew Turner 	MPASS(n > 0);
116047e07394SAndrew Turner 	*rval = read_pendr(hypctx, n);
116147e07394SAndrew Turner }
116247e07394SAndrew Turner 
116347e07394SAndrew Turner static void
dist_icpendr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)116447e07394SAndrew Turner dist_icpendr_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
116547e07394SAndrew Turner     uint64_t wval, void *arg)
116647e07394SAndrew Turner {
116747e07394SAndrew Turner 	int n;
116847e07394SAndrew Turner 
116947e07394SAndrew Turner 	MPASS(offset == 0);
117047e07394SAndrew Turner 	MPASS(size == 4);
117147e07394SAndrew Turner 	n = (reg - GICD_ICPENDR(0)) / 4;
117247e07394SAndrew Turner 	/* GICD_ICPENDR0 is RAZ/WI so handled separately */
117347e07394SAndrew Turner 	MPASS(n > 0);
117447e07394SAndrew Turner 	write_pendr(hypctx, n, false, wval);
117547e07394SAndrew Turner }
117647e07394SAndrew Turner 
117747e07394SAndrew Turner /* GICD_ISACTIVER */
117847e07394SAndrew Turner /* Affinity routing is enabled so isactiver0 is RAZ/WI */
117947e07394SAndrew Turner static void
dist_isactiver_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)118047e07394SAndrew Turner dist_isactiver_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
118147e07394SAndrew Turner {
118247e07394SAndrew Turner 	int n;
118347e07394SAndrew Turner 
118447e07394SAndrew Turner 	n = (reg - GICD_ISACTIVER(0)) / 4;
118547e07394SAndrew Turner 	/* GICD_ISACTIVER0 is RAZ/WI so handled separately */
118647e07394SAndrew Turner 	MPASS(n > 0);
118747e07394SAndrew Turner 	*rval = read_activer(hypctx, n);
118847e07394SAndrew Turner }
118947e07394SAndrew Turner 
119047e07394SAndrew Turner static void
dist_isactiver_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)119147e07394SAndrew Turner dist_isactiver_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
119247e07394SAndrew Turner     uint64_t wval, void *arg)
119347e07394SAndrew Turner {
119447e07394SAndrew Turner 	int n;
119547e07394SAndrew Turner 
119647e07394SAndrew Turner 	MPASS(offset == 0);
119747e07394SAndrew Turner 	MPASS(size == 4);
119847e07394SAndrew Turner 	n = (reg - GICD_ISACTIVER(0)) / 4;
119947e07394SAndrew Turner 	/* GICD_ISACTIVE0 is RAZ/WI so handled separately */
120047e07394SAndrew Turner 	MPASS(n > 0);
120147e07394SAndrew Turner 	write_activer(hypctx, n, true, wval);
120247e07394SAndrew Turner }
120347e07394SAndrew Turner 
120447e07394SAndrew Turner /* GICD_ICACTIVER */
120547e07394SAndrew Turner static void
dist_icactiver_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)120647e07394SAndrew Turner dist_icactiver_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
120747e07394SAndrew Turner     void *arg)
120847e07394SAndrew Turner {
120947e07394SAndrew Turner 	int n;
121047e07394SAndrew Turner 
121147e07394SAndrew Turner 	n = (reg - GICD_ICACTIVER(0)) / 4;
121247e07394SAndrew Turner 	/* GICD_ICACTIVE0 is RAZ/WI so handled separately */
121347e07394SAndrew Turner 	MPASS(n > 0);
121447e07394SAndrew Turner 	*rval = read_activer(hypctx, n);
121547e07394SAndrew Turner }
121647e07394SAndrew Turner 
121747e07394SAndrew Turner static void
dist_icactiver_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)121847e07394SAndrew Turner dist_icactiver_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
121947e07394SAndrew Turner     uint64_t wval, void *arg)
122047e07394SAndrew Turner {
122147e07394SAndrew Turner 	int n;
122247e07394SAndrew Turner 
122347e07394SAndrew Turner 	MPASS(offset == 0);
122447e07394SAndrew Turner 	MPASS(size == 4);
122547e07394SAndrew Turner 	n = (reg - GICD_ICACTIVER(0)) / 4;
122647e07394SAndrew Turner 	/* GICD_ICACTIVE0 is RAZ/WI so handled separately */
122747e07394SAndrew Turner 	MPASS(n > 0);
122847e07394SAndrew Turner 	write_activer(hypctx, n, false, wval);
122947e07394SAndrew Turner }
123047e07394SAndrew Turner 
123147e07394SAndrew Turner /* GICD_IPRIORITYR */
123247e07394SAndrew Turner /* Affinity routing is enabled so ipriorityr0-7 is RAZ/WI */
123347e07394SAndrew Turner static void
dist_ipriorityr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)123447e07394SAndrew Turner dist_ipriorityr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
123547e07394SAndrew Turner     void *arg)
123647e07394SAndrew Turner {
123747e07394SAndrew Turner 	int n;
123847e07394SAndrew Turner 
123947e07394SAndrew Turner 	n = (reg - GICD_IPRIORITYR(0)) / 4;
124047e07394SAndrew Turner 	/* GICD_IPRIORITY0-7 is RAZ/WI so handled separately */
124147e07394SAndrew Turner 	MPASS(n > 7);
124247e07394SAndrew Turner 	*rval = read_priorityr(hypctx, n);
124347e07394SAndrew Turner }
124447e07394SAndrew Turner 
124547e07394SAndrew Turner static void
dist_ipriorityr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)124647e07394SAndrew Turner dist_ipriorityr_write(struct hypctx *hypctx, u_int reg, u_int offset,
124747e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
124847e07394SAndrew Turner {
124947e07394SAndrew Turner 	u_int irq_base;
125047e07394SAndrew Turner 
125147e07394SAndrew Turner 	irq_base = (reg - GICD_IPRIORITYR(0)) + offset;
125247e07394SAndrew Turner 	/* GICD_IPRIORITY0-7 is RAZ/WI so handled separately */
125347e07394SAndrew Turner 	MPASS(irq_base > 31);
125447e07394SAndrew Turner 	write_priorityr(hypctx, irq_base, size, wval);
125547e07394SAndrew Turner }
125647e07394SAndrew Turner 
125747e07394SAndrew Turner /* GICD_ICFGR */
125847e07394SAndrew Turner static void
dist_icfgr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)125947e07394SAndrew Turner dist_icfgr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
126047e07394SAndrew Turner {
126147e07394SAndrew Turner 	int n;
126247e07394SAndrew Turner 
126347e07394SAndrew Turner 	n = (reg - GICD_ICFGR(0)) / 4;
126447e07394SAndrew Turner 	/* GICD_ICFGR0-1 are RAZ/WI so handled separately */
126547e07394SAndrew Turner 	MPASS(n > 1);
126647e07394SAndrew Turner 	*rval = read_config(hypctx, n);
126747e07394SAndrew Turner }
126847e07394SAndrew Turner 
126947e07394SAndrew Turner static void
dist_icfgr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)127047e07394SAndrew Turner dist_icfgr_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
127147e07394SAndrew Turner     uint64_t wval, void *arg)
127247e07394SAndrew Turner {
127347e07394SAndrew Turner 	int n;
127447e07394SAndrew Turner 
127547e07394SAndrew Turner 	MPASS(offset == 0);
127647e07394SAndrew Turner 	MPASS(size == 4);
127747e07394SAndrew Turner 	n = (reg - GICD_ICFGR(0)) / 4;
127847e07394SAndrew Turner 	/* GICD_ICFGR0-1 are RAZ/WI so handled separately */
127947e07394SAndrew Turner 	MPASS(n > 1);
128047e07394SAndrew Turner 	write_config(hypctx, n, wval);
128147e07394SAndrew Turner }
128247e07394SAndrew Turner 
128347e07394SAndrew Turner /* GICD_IROUTER */
128447e07394SAndrew Turner static void
dist_irouter_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)128547e07394SAndrew Turner dist_irouter_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
128647e07394SAndrew Turner {
128747e07394SAndrew Turner 	int n;
128847e07394SAndrew Turner 
128947e07394SAndrew Turner 	n = (reg - GICD_IROUTER(0)) / 8;
129047e07394SAndrew Turner 	/* GICD_IROUTER0-31 don't exist */
129147e07394SAndrew Turner 	MPASS(n > 31);
129247e07394SAndrew Turner 	*rval = read_route(hypctx, n);
129347e07394SAndrew Turner }
129447e07394SAndrew Turner 
129547e07394SAndrew Turner static void
dist_irouter_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)129647e07394SAndrew Turner dist_irouter_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
129747e07394SAndrew Turner     uint64_t wval, void *arg)
129847e07394SAndrew Turner {
129947e07394SAndrew Turner 	int n;
130047e07394SAndrew Turner 
130147e07394SAndrew Turner 	n = (reg - GICD_IROUTER(0)) / 8;
130247e07394SAndrew Turner 	/* GICD_IROUTER0-31 don't exist */
130347e07394SAndrew Turner 	MPASS(n > 31);
130447e07394SAndrew Turner 	write_route(hypctx, n, wval, offset, size);
130547e07394SAndrew Turner }
130647e07394SAndrew Turner 
130747e07394SAndrew Turner static bool
vgic_register_read(struct hypctx * hypctx,struct vgic_register * reg_list,u_int reg_list_size,u_int reg,u_int size,uint64_t * rval,void * arg)130847e07394SAndrew Turner vgic_register_read(struct hypctx *hypctx, struct vgic_register *reg_list,
130947e07394SAndrew Turner     u_int reg_list_size, u_int reg, u_int size, uint64_t *rval, void *arg)
131047e07394SAndrew Turner {
131147e07394SAndrew Turner 	u_int i, offset;
131247e07394SAndrew Turner 
131347e07394SAndrew Turner 	for (i = 0; i < reg_list_size; i++) {
131447e07394SAndrew Turner 		if (reg_list[i].start <= reg && reg_list[i].end >= reg + size) {
131547e07394SAndrew Turner 			offset = reg & (reg_list[i].size - 1);
131647e07394SAndrew Turner 			reg -= offset;
131747e07394SAndrew Turner 			if ((reg_list[i].flags & size) != 0) {
131847e07394SAndrew Turner 				reg_list[i].read(hypctx, reg, rval, NULL);
131947e07394SAndrew Turner 
132047e07394SAndrew Turner 				/* Move the bits into the correct place */
132147e07394SAndrew Turner 				*rval >>= (offset * 8);
132247e07394SAndrew Turner 				if (size < 8) {
132347e07394SAndrew Turner 					*rval &= (1ul << (size * 8)) - 1;
132447e07394SAndrew Turner 				}
132547e07394SAndrew Turner 			} else {
132647e07394SAndrew Turner 				/*
132747e07394SAndrew Turner 				 * The access is an invalid size. Section
132847e07394SAndrew Turner 				 * 12.1.3 "GIC memory-mapped register access"
132947e07394SAndrew Turner 				 * of the GICv3 and GICv4 spec issue H
133047e07394SAndrew Turner 				 * (IHI0069) lists the options. For a read
133147e07394SAndrew Turner 				 * the controller returns unknown data, in
133247e07394SAndrew Turner 				 * this case it is zero.
133347e07394SAndrew Turner 				 */
133447e07394SAndrew Turner 				*rval = 0;
133547e07394SAndrew Turner 			}
133647e07394SAndrew Turner 			return (true);
133747e07394SAndrew Turner 		}
133847e07394SAndrew Turner 	}
133947e07394SAndrew Turner 	return (false);
134047e07394SAndrew Turner }
134147e07394SAndrew Turner 
134247e07394SAndrew Turner static bool
vgic_register_write(struct hypctx * hypctx,struct vgic_register * reg_list,u_int reg_list_size,u_int reg,u_int size,uint64_t wval,void * arg)134347e07394SAndrew Turner vgic_register_write(struct hypctx *hypctx, struct vgic_register *reg_list,
134447e07394SAndrew Turner     u_int reg_list_size, u_int reg, u_int size, uint64_t wval, void *arg)
134547e07394SAndrew Turner {
134647e07394SAndrew Turner 	u_int i, offset;
134747e07394SAndrew Turner 
134847e07394SAndrew Turner 	for (i = 0; i < reg_list_size; i++) {
134947e07394SAndrew Turner 		if (reg_list[i].start <= reg && reg_list[i].end >= reg + size) {
135047e07394SAndrew Turner 			offset = reg & (reg_list[i].size - 1);
135147e07394SAndrew Turner 			reg -= offset;
135247e07394SAndrew Turner 			if ((reg_list[i].flags & size) != 0) {
135347e07394SAndrew Turner 				reg_list[i].write(hypctx, reg, offset,
135447e07394SAndrew Turner 				    size, wval, NULL);
135547e07394SAndrew Turner 			} else {
135647e07394SAndrew Turner 				/*
135747e07394SAndrew Turner 				 * See the comment in vgic_register_read.
135847e07394SAndrew Turner 				 * For writes the controller ignores the
135947e07394SAndrew Turner 				 * operation.
136047e07394SAndrew Turner 				 */
136147e07394SAndrew Turner 			}
136247e07394SAndrew Turner 			return (true);
136347e07394SAndrew Turner 		}
136447e07394SAndrew Turner 	}
136547e07394SAndrew Turner 	return (false);
136647e07394SAndrew Turner }
136747e07394SAndrew Turner 
136847e07394SAndrew Turner static int
dist_read(struct vcpu * vcpu,uint64_t fault_ipa,uint64_t * rval,int size,void * arg)136947e07394SAndrew Turner dist_read(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t *rval,
137047e07394SAndrew Turner     int size, void *arg)
137147e07394SAndrew Turner {
137247e07394SAndrew Turner 	struct hyp *hyp;
137347e07394SAndrew Turner 	struct hypctx *hypctx;
137447e07394SAndrew Turner 	struct vgic_v3 *vgic;
137547e07394SAndrew Turner 	uint64_t reg;
137647e07394SAndrew Turner 
137747e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
137847e07394SAndrew Turner 	hyp = hypctx->hyp;
137947e07394SAndrew Turner 	vgic = hyp->vgic;
138047e07394SAndrew Turner 
138147e07394SAndrew Turner 	/* Check the register is one of ours and is the correct size */
138247e07394SAndrew Turner 	if (fault_ipa < vgic->dist_start || fault_ipa + size > vgic->dist_end) {
138347e07394SAndrew Turner 		return (EINVAL);
138447e07394SAndrew Turner 	}
138547e07394SAndrew Turner 
138647e07394SAndrew Turner 	reg = fault_ipa - vgic->dist_start;
138747e07394SAndrew Turner 	/*
138847e07394SAndrew Turner 	 * As described in vgic_register_read an access with an invalid
138947e07394SAndrew Turner 	 * alignment is read with an unknown value
139047e07394SAndrew Turner 	 */
139147e07394SAndrew Turner 	if ((reg & (size - 1)) != 0) {
139247e07394SAndrew Turner 		*rval = 0;
139347e07394SAndrew Turner 		return (0);
139447e07394SAndrew Turner 	}
139547e07394SAndrew Turner 
139647e07394SAndrew Turner 	if (vgic_register_read(hypctx, dist_registers, nitems(dist_registers),
139747e07394SAndrew Turner 	    reg, size, rval, NULL))
139847e07394SAndrew Turner 		return (0);
139947e07394SAndrew Turner 
140047e07394SAndrew Turner 	/* Reserved register addresses are RES0 so we can hardware it to 0 */
140147e07394SAndrew Turner 	*rval = 0;
140247e07394SAndrew Turner 
140347e07394SAndrew Turner 	return (0);
140447e07394SAndrew Turner }
140547e07394SAndrew Turner 
140647e07394SAndrew Turner static int
dist_write(struct vcpu * vcpu,uint64_t fault_ipa,uint64_t wval,int size,void * arg)140747e07394SAndrew Turner dist_write(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t wval,
140847e07394SAndrew Turner     int size, void *arg)
140947e07394SAndrew Turner {
141047e07394SAndrew Turner 	struct hyp *hyp;
141147e07394SAndrew Turner 	struct hypctx *hypctx;
141247e07394SAndrew Turner 	struct vgic_v3 *vgic;
141347e07394SAndrew Turner 	uint64_t reg;
141447e07394SAndrew Turner 
141547e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
141647e07394SAndrew Turner 	hyp = hypctx->hyp;
141747e07394SAndrew Turner 	vgic = hyp->vgic;
141847e07394SAndrew Turner 
141947e07394SAndrew Turner 	/* Check the register is one of ours and is the correct size */
142047e07394SAndrew Turner 	if (fault_ipa < vgic->dist_start || fault_ipa + size > vgic->dist_end) {
142147e07394SAndrew Turner 		return (EINVAL);
142247e07394SAndrew Turner 	}
142347e07394SAndrew Turner 
142447e07394SAndrew Turner 	reg = fault_ipa - vgic->dist_start;
142547e07394SAndrew Turner 	/*
142647e07394SAndrew Turner 	 * As described in vgic_register_read an access with an invalid
142747e07394SAndrew Turner 	 * alignment is write ignored.
142847e07394SAndrew Turner 	 */
142947e07394SAndrew Turner 	if ((reg & (size - 1)) != 0)
143047e07394SAndrew Turner 		return (0);
143147e07394SAndrew Turner 
143247e07394SAndrew Turner 	if (vgic_register_write(hypctx, dist_registers, nitems(dist_registers),
143347e07394SAndrew Turner 	    reg, size, wval, NULL))
143447e07394SAndrew Turner 		return (0);
143547e07394SAndrew Turner 
143647e07394SAndrew Turner 	/* Reserved register addresses are RES0 so we can ignore the write */
143747e07394SAndrew Turner 	return (0);
143847e07394SAndrew Turner }
143947e07394SAndrew Turner 
144047e07394SAndrew Turner /*
144147e07394SAndrew Turner  * Redistributor register handlers.
144247e07394SAndrew Turner  *
144347e07394SAndrew Turner  * RD_base:
144447e07394SAndrew Turner  */
144547e07394SAndrew Turner /* GICR_CTLR */
144647e07394SAndrew Turner static void
redist_ctlr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)144747e07394SAndrew Turner redist_ctlr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
144847e07394SAndrew Turner {
144947e07394SAndrew Turner 	/* LPIs not supported */
145047e07394SAndrew Turner 	*rval = 0;
145147e07394SAndrew Turner }
145247e07394SAndrew Turner 
145347e07394SAndrew Turner /* GICR_IIDR */
145447e07394SAndrew Turner static void
redist_iidr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)145547e07394SAndrew Turner redist_iidr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
145647e07394SAndrew Turner {
145747e07394SAndrew Turner 	*rval = VGIC_IIDR;
145847e07394SAndrew Turner }
145947e07394SAndrew Turner 
146047e07394SAndrew Turner /* GICR_TYPER */
146147e07394SAndrew Turner static void
redist_typer_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)146247e07394SAndrew Turner redist_typer_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
146347e07394SAndrew Turner {
146447e07394SAndrew Turner 	uint64_t aff, gicr_typer, vmpidr_el2;
146547e07394SAndrew Turner 	bool last_vcpu;
146647e07394SAndrew Turner 
146747e07394SAndrew Turner 	last_vcpu = false;
146847e07394SAndrew Turner 	if (vcpu_vcpuid(hypctx->vcpu) == (vgic_max_cpu_count(hypctx->hyp) - 1))
146947e07394SAndrew Turner 		last_vcpu = true;
147047e07394SAndrew Turner 
147147e07394SAndrew Turner 	vmpidr_el2 = hypctx->vmpidr_el2;
147247e07394SAndrew Turner 	MPASS(vmpidr_el2 != 0);
147347e07394SAndrew Turner 	/*
147447e07394SAndrew Turner 	 * Get affinity for the current CPU. The guest CPU affinity is taken
147547e07394SAndrew Turner 	 * from VMPIDR_EL2. The Redistributor corresponding to this CPU is
147647e07394SAndrew Turner 	 * the Redistributor with the same affinity from GICR_TYPER.
147747e07394SAndrew Turner 	 */
147847e07394SAndrew Turner 	aff = (CPU_AFF3(vmpidr_el2) << 24) | (CPU_AFF2(vmpidr_el2) << 16) |
147947e07394SAndrew Turner 	    (CPU_AFF1(vmpidr_el2) << 8) | CPU_AFF0(vmpidr_el2);
148047e07394SAndrew Turner 
148147e07394SAndrew Turner 	/* Set up GICR_TYPER. */
148247e07394SAndrew Turner 	gicr_typer = aff << GICR_TYPER_AFF_SHIFT;
148347e07394SAndrew Turner 	/* Set the vcpu as the processsor ID */
148447e07394SAndrew Turner 	gicr_typer |=
148547e07394SAndrew Turner 	    (uint64_t)vcpu_vcpuid(hypctx->vcpu) << GICR_TYPER_CPUNUM_SHIFT;
148647e07394SAndrew Turner 
148747e07394SAndrew Turner 	if (last_vcpu)
148847e07394SAndrew Turner 		/* Mark the last Redistributor */
148947e07394SAndrew Turner 		gicr_typer |= GICR_TYPER_LAST;
149047e07394SAndrew Turner 
149147e07394SAndrew Turner 	*rval = gicr_typer;
149247e07394SAndrew Turner }
149347e07394SAndrew Turner 
149447e07394SAndrew Turner /*
149547e07394SAndrew Turner  * SGI_base:
149647e07394SAndrew Turner  */
149747e07394SAndrew Turner /* GICR_ISENABLER0 */
149847e07394SAndrew Turner static void
redist_ienabler0_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)149947e07394SAndrew Turner redist_ienabler0_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
150047e07394SAndrew Turner     void *arg)
150147e07394SAndrew Turner {
150247e07394SAndrew Turner 	*rval = read_enabler(hypctx, 0);
150347e07394SAndrew Turner }
150447e07394SAndrew Turner 
150547e07394SAndrew Turner static void
redist_isenabler0_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)150647e07394SAndrew Turner redist_isenabler0_write(struct hypctx *hypctx, u_int reg, u_int offset,
150747e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
150847e07394SAndrew Turner {
150947e07394SAndrew Turner 	MPASS(offset == 0);
151047e07394SAndrew Turner 	MPASS(size == 4);
151147e07394SAndrew Turner 	write_enabler(hypctx, 0, true, wval);
151247e07394SAndrew Turner }
151347e07394SAndrew Turner 
151447e07394SAndrew Turner /* GICR_ICENABLER0 */
151547e07394SAndrew Turner static void
redist_icenabler0_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)151647e07394SAndrew Turner redist_icenabler0_write(struct hypctx *hypctx, u_int reg, u_int offset,
151747e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
151847e07394SAndrew Turner {
151947e07394SAndrew Turner 	MPASS(offset == 0);
152047e07394SAndrew Turner 	MPASS(size == 4);
152147e07394SAndrew Turner 	write_enabler(hypctx, 0, false, wval);
152247e07394SAndrew Turner }
152347e07394SAndrew Turner 
152447e07394SAndrew Turner /* GICR_ISPENDR0 */
152547e07394SAndrew Turner static void
redist_ipendr0_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)152647e07394SAndrew Turner redist_ipendr0_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
152747e07394SAndrew Turner     void *arg)
152847e07394SAndrew Turner {
152947e07394SAndrew Turner 	*rval = read_pendr(hypctx, 0);
153047e07394SAndrew Turner }
153147e07394SAndrew Turner 
153247e07394SAndrew Turner static void
redist_ispendr0_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)153347e07394SAndrew Turner redist_ispendr0_write(struct hypctx *hypctx, u_int reg, u_int offset,
153447e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
153547e07394SAndrew Turner {
153647e07394SAndrew Turner 	MPASS(offset == 0);
153747e07394SAndrew Turner 	MPASS(size == 4);
153847e07394SAndrew Turner 	write_pendr(hypctx, 0, true, wval);
153947e07394SAndrew Turner }
154047e07394SAndrew Turner 
154147e07394SAndrew Turner /* GICR_ICPENDR0 */
154247e07394SAndrew Turner static void
redist_icpendr0_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)154347e07394SAndrew Turner redist_icpendr0_write(struct hypctx *hypctx, u_int reg, u_int offset,
154447e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
154547e07394SAndrew Turner {
154647e07394SAndrew Turner 	MPASS(offset == 0);
154747e07394SAndrew Turner 	MPASS(size == 4);
154847e07394SAndrew Turner 	write_pendr(hypctx, 0, false, wval);
154947e07394SAndrew Turner }
155047e07394SAndrew Turner 
155147e07394SAndrew Turner /* GICR_ISACTIVER0 */
155247e07394SAndrew Turner static void
redist_iactiver0_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)155347e07394SAndrew Turner redist_iactiver0_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
155447e07394SAndrew Turner     void *arg)
155547e07394SAndrew Turner {
155647e07394SAndrew Turner 	*rval = read_activer(hypctx, 0);
155747e07394SAndrew Turner }
155847e07394SAndrew Turner 
155947e07394SAndrew Turner static void
redist_isactiver0_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)156047e07394SAndrew Turner redist_isactiver0_write(struct hypctx *hypctx, u_int reg, u_int offset,
156147e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
156247e07394SAndrew Turner {
156347e07394SAndrew Turner 	write_activer(hypctx, 0, true, wval);
156447e07394SAndrew Turner }
156547e07394SAndrew Turner 
156647e07394SAndrew Turner /* GICR_ICACTIVER0 */
156747e07394SAndrew Turner static void
redist_icactiver0_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)156847e07394SAndrew Turner redist_icactiver0_write(struct hypctx *hypctx, u_int reg, u_int offset,
156947e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
157047e07394SAndrew Turner {
157147e07394SAndrew Turner 	write_activer(hypctx, 0, false, wval);
157247e07394SAndrew Turner }
157347e07394SAndrew Turner 
157447e07394SAndrew Turner /* GICR_IPRIORITYR */
157547e07394SAndrew Turner static void
redist_ipriorityr_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)157647e07394SAndrew Turner redist_ipriorityr_read(struct hypctx *hypctx, u_int reg, uint64_t *rval,
157747e07394SAndrew Turner     void *arg)
157847e07394SAndrew Turner {
157947e07394SAndrew Turner 	int n;
158047e07394SAndrew Turner 
158147e07394SAndrew Turner 	n = (reg - GICR_IPRIORITYR(0)) / 4;
158247e07394SAndrew Turner 	*rval = read_priorityr(hypctx, n);
158347e07394SAndrew Turner }
158447e07394SAndrew Turner 
158547e07394SAndrew Turner static void
redist_ipriorityr_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)158647e07394SAndrew Turner redist_ipriorityr_write(struct hypctx *hypctx, u_int reg, u_int offset,
158747e07394SAndrew Turner     u_int size, uint64_t wval, void *arg)
158847e07394SAndrew Turner {
158947e07394SAndrew Turner 	u_int irq_base;
159047e07394SAndrew Turner 
159147e07394SAndrew Turner 	irq_base = (reg - GICR_IPRIORITYR(0)) + offset;
159247e07394SAndrew Turner 	write_priorityr(hypctx, irq_base, size, wval);
159347e07394SAndrew Turner }
159447e07394SAndrew Turner 
159547e07394SAndrew Turner /* GICR_ICFGR1 */
159647e07394SAndrew Turner static void
redist_icfgr1_read(struct hypctx * hypctx,u_int reg,uint64_t * rval,void * arg)159747e07394SAndrew Turner redist_icfgr1_read(struct hypctx *hypctx, u_int reg, uint64_t *rval, void *arg)
159847e07394SAndrew Turner {
159947e07394SAndrew Turner 	*rval = read_config(hypctx, 1);
160047e07394SAndrew Turner }
160147e07394SAndrew Turner 
160247e07394SAndrew Turner static void
redist_icfgr1_write(struct hypctx * hypctx,u_int reg,u_int offset,u_int size,uint64_t wval,void * arg)160347e07394SAndrew Turner redist_icfgr1_write(struct hypctx *hypctx, u_int reg, u_int offset, u_int size,
160447e07394SAndrew Turner     uint64_t wval, void *arg)
160547e07394SAndrew Turner {
160647e07394SAndrew Turner 	MPASS(offset == 0);
160747e07394SAndrew Turner 	MPASS(size == 4);
160847e07394SAndrew Turner 	write_config(hypctx, 1, wval);
160947e07394SAndrew Turner }
161047e07394SAndrew Turner 
161147e07394SAndrew Turner static int
redist_read(struct vcpu * vcpu,uint64_t fault_ipa,uint64_t * rval,int size,void * arg)161247e07394SAndrew Turner redist_read(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t *rval,
161347e07394SAndrew Turner     int size, void *arg)
161447e07394SAndrew Turner {
161547e07394SAndrew Turner 	struct hyp *hyp;
161647e07394SAndrew Turner 	struct hypctx *hypctx, *target_hypctx;
161747e07394SAndrew Turner 	struct vgic_v3 *vgic;
161847e07394SAndrew Turner 	uint64_t reg;
161947e07394SAndrew Turner 	int vcpuid;
162047e07394SAndrew Turner 
162147e07394SAndrew Turner 	/* Find the current vcpu ctx to get the vgic struct */
162247e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
162347e07394SAndrew Turner 	hyp = hypctx->hyp;
162447e07394SAndrew Turner 	vgic = hyp->vgic;
162547e07394SAndrew Turner 
162647e07394SAndrew Turner 	/* Check the register is one of ours and is the correct size */
162747e07394SAndrew Turner 	if (fault_ipa < vgic->redist_start ||
162847e07394SAndrew Turner 	    fault_ipa + size > vgic->redist_end) {
162947e07394SAndrew Turner 		return (EINVAL);
163047e07394SAndrew Turner 	}
163147e07394SAndrew Turner 
163247e07394SAndrew Turner 	vcpuid = (fault_ipa - vgic->redist_start) /
163347e07394SAndrew Turner 	    (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
163447e07394SAndrew Turner 	if (vcpuid >= vm_get_maxcpus(hyp->vm)) {
163547e07394SAndrew Turner 		/*
163647e07394SAndrew Turner 		 * This should never happen, but lets be defensive so if it
163747e07394SAndrew Turner 		 * does we don't panic a non-INVARIANTS kernel.
163847e07394SAndrew Turner 		 */
163947e07394SAndrew Turner #ifdef INVARIANTS
164047e07394SAndrew Turner 		panic("%s: Invalid vcpuid %d", __func__, vcpuid);
164147e07394SAndrew Turner #else
164247e07394SAndrew Turner 		*rval = 0;
164347e07394SAndrew Turner 		return (0);
164447e07394SAndrew Turner #endif
164547e07394SAndrew Turner 	}
164647e07394SAndrew Turner 
164747e07394SAndrew Turner 	/* Find the target vcpu ctx for the access */
164847e07394SAndrew Turner 	target_hypctx = hyp->ctx[vcpuid];
164947e07394SAndrew Turner 	if (target_hypctx == NULL) {
165047e07394SAndrew Turner 		/*
165147e07394SAndrew Turner 		 * The CPU has not yet started. The redistributor and CPU are
165247e07394SAndrew Turner 		 * in the same power domain. As such the redistributor will
165347e07394SAndrew Turner 		 * also be powered down so any access will raise an external
165447e07394SAndrew Turner 		 * abort.
165547e07394SAndrew Turner 		 */
165647e07394SAndrew Turner 		raise_data_insn_abort(hypctx, fault_ipa, true,
165747e07394SAndrew Turner 		    ISS_DATA_DFSC_EXT);
165847e07394SAndrew Turner 		return (0);
165947e07394SAndrew Turner 	}
166047e07394SAndrew Turner 
166147e07394SAndrew Turner 	reg = (fault_ipa - vgic->redist_start) %
166247e07394SAndrew Turner 	    (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
166347e07394SAndrew Turner 
166447e07394SAndrew Turner 	/*
166547e07394SAndrew Turner 	 * As described in vgic_register_read an access with an invalid
166647e07394SAndrew Turner 	 * alignment is read with an unknown value
166747e07394SAndrew Turner 	 */
166847e07394SAndrew Turner 	if ((reg & (size - 1)) != 0) {
166947e07394SAndrew Turner 		*rval = 0;
167047e07394SAndrew Turner 		return (0);
167147e07394SAndrew Turner 	}
167247e07394SAndrew Turner 
167347e07394SAndrew Turner 	if (reg < GICR_RD_BASE_SIZE) {
167447e07394SAndrew Turner 		if (vgic_register_read(target_hypctx, redist_rd_registers,
167547e07394SAndrew Turner 		    nitems(redist_rd_registers), reg, size, rval, NULL))
167647e07394SAndrew Turner 			return (0);
167747e07394SAndrew Turner 	} else if (reg < (GICR_SGI_BASE + GICR_SGI_BASE_SIZE)) {
167847e07394SAndrew Turner 		if (vgic_register_read(target_hypctx, redist_sgi_registers,
167947e07394SAndrew Turner 		    nitems(redist_sgi_registers), reg - GICR_SGI_BASE, size,
168047e07394SAndrew Turner 		    rval, NULL))
168147e07394SAndrew Turner 			return (0);
168247e07394SAndrew Turner 	}
168347e07394SAndrew Turner 
168447e07394SAndrew Turner 	/* Reserved register addresses are RES0 so we can hardware it to 0 */
168547e07394SAndrew Turner 	*rval = 0;
168647e07394SAndrew Turner 	return (0);
168747e07394SAndrew Turner }
168847e07394SAndrew Turner 
168947e07394SAndrew Turner static int
redist_write(struct vcpu * vcpu,uint64_t fault_ipa,uint64_t wval,int size,void * arg)169047e07394SAndrew Turner redist_write(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t wval,
169147e07394SAndrew Turner     int size, void *arg)
169247e07394SAndrew Turner {
169347e07394SAndrew Turner 	struct hyp *hyp;
169447e07394SAndrew Turner 	struct hypctx *hypctx, *target_hypctx;
169547e07394SAndrew Turner 	struct vgic_v3 *vgic;
169647e07394SAndrew Turner 	uint64_t reg;
169747e07394SAndrew Turner 	int vcpuid;
169847e07394SAndrew Turner 
169947e07394SAndrew Turner 	/* Find the current vcpu ctx to get the vgic struct */
170047e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
170147e07394SAndrew Turner 	hyp = hypctx->hyp;
170247e07394SAndrew Turner 	vgic = hyp->vgic;
170347e07394SAndrew Turner 
170447e07394SAndrew Turner 	/* Check the register is one of ours and is the correct size */
170547e07394SAndrew Turner 	if (fault_ipa < vgic->redist_start ||
170647e07394SAndrew Turner 	    fault_ipa + size > vgic->redist_end) {
170747e07394SAndrew Turner 		return (EINVAL);
170847e07394SAndrew Turner 	}
170947e07394SAndrew Turner 
171047e07394SAndrew Turner 	vcpuid = (fault_ipa - vgic->redist_start) /
171147e07394SAndrew Turner 	    (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
171247e07394SAndrew Turner 	if (vcpuid >= vm_get_maxcpus(hyp->vm)) {
171347e07394SAndrew Turner 		/*
171447e07394SAndrew Turner 		 * This should never happen, but lets be defensive so if it
171547e07394SAndrew Turner 		 * does we don't panic a non-INVARIANTS kernel.
171647e07394SAndrew Turner 		 */
171747e07394SAndrew Turner #ifdef INVARIANTS
171847e07394SAndrew Turner 		panic("%s: Invalid vcpuid %d", __func__, vcpuid);
171947e07394SAndrew Turner #else
172047e07394SAndrew Turner 		return (0);
172147e07394SAndrew Turner #endif
172247e07394SAndrew Turner 	}
172347e07394SAndrew Turner 
172447e07394SAndrew Turner 	/* Find the target vcpu ctx for the access */
172547e07394SAndrew Turner 	target_hypctx = hyp->ctx[vcpuid];
172647e07394SAndrew Turner 	if (target_hypctx == NULL) {
172747e07394SAndrew Turner 		/*
172847e07394SAndrew Turner 		 * The CPU has not yet started. The redistributor and CPU are
172947e07394SAndrew Turner 		 * in the same power domain. As such the redistributor will
173047e07394SAndrew Turner 		 * also be powered down so any access will raise an external
173147e07394SAndrew Turner 		 * abort.
173247e07394SAndrew Turner 		 */
173347e07394SAndrew Turner 		raise_data_insn_abort(hypctx, fault_ipa, true,
173447e07394SAndrew Turner 		    ISS_DATA_DFSC_EXT);
173547e07394SAndrew Turner 		return (0);
173647e07394SAndrew Turner 	}
173747e07394SAndrew Turner 
173847e07394SAndrew Turner 	reg = (fault_ipa - vgic->redist_start) %
173947e07394SAndrew Turner 	    (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
174047e07394SAndrew Turner 
174147e07394SAndrew Turner 	/*
174247e07394SAndrew Turner 	 * As described in vgic_register_read an access with an invalid
174347e07394SAndrew Turner 	 * alignment is write ignored.
174447e07394SAndrew Turner 	 */
174547e07394SAndrew Turner 	if ((reg & (size - 1)) != 0)
174647e07394SAndrew Turner 		return (0);
174747e07394SAndrew Turner 
174847e07394SAndrew Turner 	if (reg < GICR_RD_BASE_SIZE) {
174947e07394SAndrew Turner 		if (vgic_register_write(target_hypctx, redist_rd_registers,
175047e07394SAndrew Turner 		    nitems(redist_rd_registers), reg, size, wval, NULL))
175147e07394SAndrew Turner 			return (0);
175247e07394SAndrew Turner 	} else if (reg < (GICR_SGI_BASE + GICR_SGI_BASE_SIZE)) {
175347e07394SAndrew Turner 		if (vgic_register_write(target_hypctx, redist_sgi_registers,
175447e07394SAndrew Turner 		    nitems(redist_sgi_registers), reg - GICR_SGI_BASE, size,
175547e07394SAndrew Turner 		    wval, NULL))
175647e07394SAndrew Turner 			return (0);
175747e07394SAndrew Turner 	}
175847e07394SAndrew Turner 
175947e07394SAndrew Turner 	/* Reserved register addresses are RES0 so we can ignore the write */
176047e07394SAndrew Turner 	return (0);
176147e07394SAndrew Turner }
176247e07394SAndrew Turner 
176347e07394SAndrew Turner static int
vgic_v3_icc_sgi1r_read(struct vcpu * vcpu,uint64_t * rval,void * arg)176447e07394SAndrew Turner vgic_v3_icc_sgi1r_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
176547e07394SAndrew Turner {
176647e07394SAndrew Turner 	/*
176747e07394SAndrew Turner 	 * TODO: Inject an unknown exception.
176847e07394SAndrew Turner 	 */
176947e07394SAndrew Turner 	*rval = 0;
177047e07394SAndrew Turner 	return (0);
177147e07394SAndrew Turner }
177247e07394SAndrew Turner 
177347e07394SAndrew Turner static int
vgic_v3_icc_sgi1r_write(struct vcpu * vcpu,uint64_t rval,void * arg)177447e07394SAndrew Turner vgic_v3_icc_sgi1r_write(struct vcpu *vcpu, uint64_t rval, void *arg)
177547e07394SAndrew Turner {
177647e07394SAndrew Turner 	struct vm *vm;
177747e07394SAndrew Turner 	struct hyp *hyp;
177847e07394SAndrew Turner 	cpuset_t active_cpus;
177947e07394SAndrew Turner 	uint64_t mpidr, aff1, aff2, aff3;
178047e07394SAndrew Turner 	uint32_t irqid;
178147e07394SAndrew Turner 	int cpus, cpu_off, target_vcpuid, vcpuid;
178247e07394SAndrew Turner 
178347e07394SAndrew Turner 	vm = vcpu_vm(vcpu);
178447e07394SAndrew Turner 	hyp = vm_get_cookie(vm);
178547e07394SAndrew Turner 	active_cpus = vm_active_cpus(vm);
178647e07394SAndrew Turner 	vcpuid = vcpu_vcpuid(vcpu);
178747e07394SAndrew Turner 
178847e07394SAndrew Turner 	irqid = ICC_SGI1R_EL1_SGIID_VAL(rval) >> ICC_SGI1R_EL1_SGIID_SHIFT;
178947e07394SAndrew Turner 	if ((rval & ICC_SGI1R_EL1_IRM) == 0) {
179047e07394SAndrew Turner 		/* Non-zero points at no vcpus */
179147e07394SAndrew Turner 		if (ICC_SGI1R_EL1_RS_VAL(rval) != 0)
179247e07394SAndrew Turner 			return (0);
179347e07394SAndrew Turner 
179447e07394SAndrew Turner 		aff1 = ICC_SGI1R_EL1_AFF1_VAL(rval) >> ICC_SGI1R_EL1_AFF1_SHIFT;
179547e07394SAndrew Turner 		aff2 = ICC_SGI1R_EL1_AFF2_VAL(rval) >> ICC_SGI1R_EL1_AFF2_SHIFT;
179647e07394SAndrew Turner 		aff3 = ICC_SGI1R_EL1_AFF3_VAL(rval) >> ICC_SGI1R_EL1_AFF3_SHIFT;
179747e07394SAndrew Turner 		mpidr = aff3 << MPIDR_AFF3_SHIFT |
179847e07394SAndrew Turner 		    aff2 << MPIDR_AFF2_SHIFT | aff1 << MPIDR_AFF1_SHIFT;
179947e07394SAndrew Turner 
180047e07394SAndrew Turner 		cpus = ICC_SGI1R_EL1_TL_VAL(rval) >> ICC_SGI1R_EL1_TL_SHIFT;
180147e07394SAndrew Turner 		cpu_off = 0;
180247e07394SAndrew Turner 		while (cpus > 0) {
180347e07394SAndrew Turner 			if (cpus & 1) {
180447e07394SAndrew Turner 				target_vcpuid = mpidr_to_vcpu(hyp,
180547e07394SAndrew Turner 				    mpidr | (cpu_off << MPIDR_AFF0_SHIFT));
180647e07394SAndrew Turner 				if (target_vcpuid >= 0 &&
180747e07394SAndrew Turner 				    CPU_ISSET(target_vcpuid, &active_cpus)) {
180847e07394SAndrew Turner 					INJECT_IRQ(hyp, target_vcpuid, irqid,
180947e07394SAndrew Turner 					    true);
181047e07394SAndrew Turner 				}
181147e07394SAndrew Turner 			}
181247e07394SAndrew Turner 			cpu_off++;
181347e07394SAndrew Turner 			cpus >>= 1;
181447e07394SAndrew Turner 		}
181547e07394SAndrew Turner 	} else {
181647e07394SAndrew Turner 		/* Send an IPI to all CPUs other than the current CPU */
181747e07394SAndrew Turner 		for (target_vcpuid = 0; target_vcpuid < vm_get_maxcpus(vm);
181847e07394SAndrew Turner 		    target_vcpuid++) {
181947e07394SAndrew Turner 			if (CPU_ISSET(target_vcpuid, &active_cpus) &&
182047e07394SAndrew Turner 			    target_vcpuid != vcpuid) {
182147e07394SAndrew Turner 				INJECT_IRQ(hyp, target_vcpuid, irqid, true);
182247e07394SAndrew Turner 			}
182347e07394SAndrew Turner 		}
182447e07394SAndrew Turner 	}
182547e07394SAndrew Turner 
182647e07394SAndrew Turner 	return (0);
182747e07394SAndrew Turner }
182847e07394SAndrew Turner 
182947e07394SAndrew Turner static void
vgic_v3_mmio_init(struct hyp * hyp)183047e07394SAndrew Turner vgic_v3_mmio_init(struct hyp *hyp)
183147e07394SAndrew Turner {
183247e07394SAndrew Turner 	struct vgic_v3 *vgic;
183347e07394SAndrew Turner 	struct vgic_v3_irq *irq;
183447e07394SAndrew Turner 	int i;
183547e07394SAndrew Turner 
183647e07394SAndrew Turner 	/* Allocate memory for the SPIs */
183747e07394SAndrew Turner 	vgic = hyp->vgic;
183847e07394SAndrew Turner 	vgic->irqs = malloc((VGIC_NIRQS - VGIC_PRV_I_NUM) *
183947e07394SAndrew Turner 	    sizeof(*vgic->irqs), M_VGIC_V3, M_WAITOK | M_ZERO);
184047e07394SAndrew Turner 
184147e07394SAndrew Turner 	for (i = 0; i < VGIC_NIRQS - VGIC_PRV_I_NUM; i++) {
184247e07394SAndrew Turner 		irq = &vgic->irqs[i];
184347e07394SAndrew Turner 
184447e07394SAndrew Turner 		mtx_init(&irq->irq_spinmtx, "VGIC IRQ spinlock", NULL,
184547e07394SAndrew Turner 		    MTX_SPIN);
184647e07394SAndrew Turner 
184747e07394SAndrew Turner 		irq->irq = i + VGIC_PRV_I_NUM;
184847e07394SAndrew Turner 	}
184947e07394SAndrew Turner }
185047e07394SAndrew Turner 
185147e07394SAndrew Turner static void
vgic_v3_mmio_destroy(struct hyp * hyp)185247e07394SAndrew Turner vgic_v3_mmio_destroy(struct hyp *hyp)
185347e07394SAndrew Turner {
185447e07394SAndrew Turner 	struct vgic_v3 *vgic;
185547e07394SAndrew Turner 	struct vgic_v3_irq *irq;
185647e07394SAndrew Turner 	int i;
185747e07394SAndrew Turner 
185847e07394SAndrew Turner 	vgic = hyp->vgic;
185947e07394SAndrew Turner 	for (i = 0; i < VGIC_NIRQS - VGIC_PRV_I_NUM; i++) {
186047e07394SAndrew Turner 		irq = &vgic->irqs[i];
186147e07394SAndrew Turner 
186247e07394SAndrew Turner 		mtx_destroy(&irq->irq_spinmtx);
186347e07394SAndrew Turner 	}
186447e07394SAndrew Turner 
186547e07394SAndrew Turner 	free(vgic->irqs, M_VGIC_V3);
186647e07394SAndrew Turner }
186747e07394SAndrew Turner 
186847e07394SAndrew Turner static int
vgic_v3_attach_to_vm(device_t dev,struct hyp * hyp,struct vm_vgic_descr * descr)186947e07394SAndrew Turner vgic_v3_attach_to_vm(device_t dev, struct hyp *hyp, struct vm_vgic_descr *descr)
187047e07394SAndrew Turner {
187147e07394SAndrew Turner 	struct vm *vm;
187247e07394SAndrew Turner 	struct vgic_v3 *vgic;
187347e07394SAndrew Turner 	size_t cpu_count;
187447e07394SAndrew Turner 
187547e07394SAndrew Turner 	if (descr->ver.version != 3)
187647e07394SAndrew Turner 		return (EINVAL);
187747e07394SAndrew Turner 
187847e07394SAndrew Turner 	/*
187947e07394SAndrew Turner 	 * The register bases need to be 64k aligned
188047e07394SAndrew Turner 	 * The redist register space is the RD + SGI size
188147e07394SAndrew Turner 	 */
188247e07394SAndrew Turner 	if (!__is_aligned(descr->v3_regs.dist_start, PAGE_SIZE_64K) ||
188347e07394SAndrew Turner 	    !__is_aligned(descr->v3_regs.redist_start, PAGE_SIZE_64K) ||
188447e07394SAndrew Turner 	    !__is_aligned(descr->v3_regs.redist_size,
188547e07394SAndrew Turner 	     GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE))
188647e07394SAndrew Turner 		return (EINVAL);
188747e07394SAndrew Turner 
188847e07394SAndrew Turner 	/* The dist register space is 1 64k block */
188947e07394SAndrew Turner 	if (descr->v3_regs.dist_size != PAGE_SIZE_64K)
189047e07394SAndrew Turner 		return (EINVAL);
189147e07394SAndrew Turner 
189247e07394SAndrew Turner 	vm = hyp->vm;
189347e07394SAndrew Turner 
189447e07394SAndrew Turner 	/*
189547e07394SAndrew Turner 	 * Return an error if the redist space is too large for the maximum
189647e07394SAndrew Turner 	 * number of CPUs we support.
189747e07394SAndrew Turner 	 */
189847e07394SAndrew Turner 	cpu_count = descr->v3_regs.redist_size /
189947e07394SAndrew Turner 	    (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE);
190047e07394SAndrew Turner 	if (cpu_count > vm_get_maxcpus(vm))
190147e07394SAndrew Turner 		return (EINVAL);
190247e07394SAndrew Turner 
190347e07394SAndrew Turner 	vgic = hyp->vgic;
190447e07394SAndrew Turner 
190547e07394SAndrew Turner 	/* Set the distributor address and size for trapping guest access. */
190647e07394SAndrew Turner 	vgic->dist_start = descr->v3_regs.dist_start;
190747e07394SAndrew Turner 	vgic->dist_end = descr->v3_regs.dist_start + descr->v3_regs.dist_size;
190847e07394SAndrew Turner 
190947e07394SAndrew Turner 	vgic->redist_start = descr->v3_regs.redist_start;
191047e07394SAndrew Turner 	vgic->redist_end = descr->v3_regs.redist_start +
191147e07394SAndrew Turner 	    descr->v3_regs.redist_size;
191247e07394SAndrew Turner 
191347e07394SAndrew Turner 	vm_register_inst_handler(vm, descr->v3_regs.dist_start,
191447e07394SAndrew Turner 	    descr->v3_regs.dist_size, dist_read, dist_write);
191547e07394SAndrew Turner 	vm_register_inst_handler(vm, descr->v3_regs.redist_start,
191647e07394SAndrew Turner 	    descr->v3_regs.redist_size, redist_read, redist_write);
191747e07394SAndrew Turner 
191847e07394SAndrew Turner 	vm_register_reg_handler(vm, ISS_MSR_REG(ICC_SGI1R_EL1),
191947e07394SAndrew Turner 	    ISS_MSR_REG_MASK, vgic_v3_icc_sgi1r_read, vgic_v3_icc_sgi1r_write,
192047e07394SAndrew Turner 	    NULL);
192147e07394SAndrew Turner 
192247e07394SAndrew Turner 	vgic_v3_mmio_init(hyp);
192347e07394SAndrew Turner 
192447e07394SAndrew Turner 	hyp->vgic_attached = true;
192547e07394SAndrew Turner 
192647e07394SAndrew Turner 	return (0);
192747e07394SAndrew Turner }
192847e07394SAndrew Turner 
192947e07394SAndrew Turner static void
vgic_v3_detach_from_vm(device_t dev,struct hyp * hyp)193047e07394SAndrew Turner vgic_v3_detach_from_vm(device_t dev, struct hyp *hyp)
193147e07394SAndrew Turner {
193247e07394SAndrew Turner 	if (hyp->vgic_attached) {
193347e07394SAndrew Turner 		hyp->vgic_attached = false;
193447e07394SAndrew Turner 		vgic_v3_mmio_destroy(hyp);
193547e07394SAndrew Turner 	}
193647e07394SAndrew Turner }
193747e07394SAndrew Turner 
193847e07394SAndrew Turner static struct vgic_v3_irq *
vgic_v3_get_irq(struct hyp * hyp,int vcpuid,uint32_t irqid)193947e07394SAndrew Turner vgic_v3_get_irq(struct hyp *hyp, int vcpuid, uint32_t irqid)
194047e07394SAndrew Turner {
194147e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
194247e07394SAndrew Turner 	struct vgic_v3_irq *irq;
194347e07394SAndrew Turner 	struct hypctx *hypctx;
194447e07394SAndrew Turner 
194547e07394SAndrew Turner 	if (irqid < VGIC_PRV_I_NUM) {
194647e07394SAndrew Turner 		if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(hyp->vm))
194747e07394SAndrew Turner 			return (NULL);
194847e07394SAndrew Turner 		hypctx = hyp->ctx[vcpuid];
194947e07394SAndrew Turner 		if (hypctx == NULL)
195047e07394SAndrew Turner 			return (NULL);
195147e07394SAndrew Turner 		vgic_cpu = hypctx->vgic_cpu;
195247e07394SAndrew Turner 		irq = &vgic_cpu->private_irqs[irqid];
195347e07394SAndrew Turner 	} else if (irqid <= GIC_LAST_SPI) {
195447e07394SAndrew Turner 		irqid -= VGIC_PRV_I_NUM;
195547e07394SAndrew Turner 		if (irqid >= VGIC_NIRQS)
195647e07394SAndrew Turner 			return (NULL);
195747e07394SAndrew Turner 		irq = &hyp->vgic->irqs[irqid];
195847e07394SAndrew Turner 	} else if (irqid < GIC_FIRST_LPI) {
195947e07394SAndrew Turner 		return (NULL);
196047e07394SAndrew Turner 	} else {
196147e07394SAndrew Turner 		/* No support for LPIs */
196247e07394SAndrew Turner 		return (NULL);
196347e07394SAndrew Turner 	}
196447e07394SAndrew Turner 
196547e07394SAndrew Turner 	mtx_lock_spin(&irq->irq_spinmtx);
196647e07394SAndrew Turner 	return (irq);
196747e07394SAndrew Turner }
196847e07394SAndrew Turner 
196947e07394SAndrew Turner static void
vgic_v3_release_irq(struct vgic_v3_irq * irq)197047e07394SAndrew Turner vgic_v3_release_irq(struct vgic_v3_irq *irq)
197147e07394SAndrew Turner {
197247e07394SAndrew Turner 
197347e07394SAndrew Turner 	mtx_unlock_spin(&irq->irq_spinmtx);
197447e07394SAndrew Turner }
197547e07394SAndrew Turner 
197647e07394SAndrew Turner static bool
vgic_v3_has_pending_irq(device_t dev,struct hypctx * hypctx)197747e07394SAndrew Turner vgic_v3_has_pending_irq(device_t dev, struct hypctx *hypctx)
197847e07394SAndrew Turner {
197947e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
198047e07394SAndrew Turner 	bool empty;
198147e07394SAndrew Turner 
198247e07394SAndrew Turner 	vgic_cpu = hypctx->vgic_cpu;
198347e07394SAndrew Turner 	mtx_lock_spin(&vgic_cpu->lr_mtx);
198447e07394SAndrew Turner 	empty = TAILQ_EMPTY(&vgic_cpu->irq_act_pend);
198547e07394SAndrew Turner 	mtx_unlock_spin(&vgic_cpu->lr_mtx);
198647e07394SAndrew Turner 
198747e07394SAndrew Turner 	return (!empty);
198847e07394SAndrew Turner }
198947e07394SAndrew Turner 
199047e07394SAndrew Turner static bool
vgic_v3_check_irq(struct vgic_v3_irq * irq,bool level)199147e07394SAndrew Turner vgic_v3_check_irq(struct vgic_v3_irq *irq, bool level)
199247e07394SAndrew Turner {
199347e07394SAndrew Turner 	/*
199447e07394SAndrew Turner 	 * Only inject if:
199547e07394SAndrew Turner 	 *  - Level-triggered IRQ: level changes low -> high
199647e07394SAndrew Turner 	 *  - Edge-triggered IRQ: level is high
199747e07394SAndrew Turner 	 */
199847e07394SAndrew Turner 	switch (irq->config & VGIC_CONFIG_MASK) {
199947e07394SAndrew Turner 	case VGIC_CONFIG_LEVEL:
200047e07394SAndrew Turner 		return (level != irq->level);
200147e07394SAndrew Turner 	case VGIC_CONFIG_EDGE:
200247e07394SAndrew Turner 		return (level);
200347e07394SAndrew Turner 	default:
200447e07394SAndrew Turner 		break;
200547e07394SAndrew Turner 	}
200647e07394SAndrew Turner 
200747e07394SAndrew Turner 	return (false);
200847e07394SAndrew Turner }
200947e07394SAndrew Turner 
201047e07394SAndrew Turner static int
vgic_v3_inject_irq(device_t dev,struct hyp * hyp,int vcpuid,uint32_t irqid,bool level)201147e07394SAndrew Turner vgic_v3_inject_irq(device_t dev, struct hyp *hyp, int vcpuid, uint32_t irqid,
201247e07394SAndrew Turner     bool level)
201347e07394SAndrew Turner {
201447e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
201547e07394SAndrew Turner 	struct vgic_v3_irq *irq;
201647e07394SAndrew Turner 	struct hypctx *hypctx;
201747e07394SAndrew Turner 	int target_vcpu;
201847e07394SAndrew Turner 	bool notify;
201947e07394SAndrew Turner 
202047e07394SAndrew Turner 	if (!hyp->vgic_attached)
202147e07394SAndrew Turner 		return (ENODEV);
202247e07394SAndrew Turner 
202347e07394SAndrew Turner 	KASSERT(vcpuid == -1 || irqid < VGIC_PRV_I_NUM,
202447e07394SAndrew Turner 	    ("%s: SPI/LPI with vcpuid set: irq %u vcpuid %u", __func__, irqid,
202547e07394SAndrew Turner 	    vcpuid));
202647e07394SAndrew Turner 
202747e07394SAndrew Turner 	irq = vgic_v3_get_irq(hyp, vcpuid, irqid);
202847e07394SAndrew Turner 	if (irq == NULL) {
202947e07394SAndrew Turner 		eprintf("Malformed IRQ %u.\n", irqid);
203047e07394SAndrew Turner 		return (EINVAL);
203147e07394SAndrew Turner 	}
203247e07394SAndrew Turner 
203347e07394SAndrew Turner 	target_vcpu = irq->target_vcpu;
203447e07394SAndrew Turner 	KASSERT(vcpuid == -1 || vcpuid == target_vcpu,
203547e07394SAndrew Turner 	    ("%s: Interrupt %u has bad cpu affinity: vcpu %d target vcpu %d",
203647e07394SAndrew Turner 	    __func__, irqid, vcpuid, target_vcpu));
203747e07394SAndrew Turner 	KASSERT(target_vcpu >= 0 && target_vcpu < vm_get_maxcpus(hyp->vm),
203847e07394SAndrew Turner 	    ("%s: Interrupt %u sent to invalid vcpu %d", __func__, irqid,
203947e07394SAndrew Turner 	    target_vcpu));
204047e07394SAndrew Turner 
204147e07394SAndrew Turner 	if (vcpuid == -1)
204247e07394SAndrew Turner 		vcpuid = target_vcpu;
204347e07394SAndrew Turner 	/* TODO: Check from 0 to vm->maxcpus */
204447e07394SAndrew Turner 	if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(hyp->vm)) {
204547e07394SAndrew Turner 		vgic_v3_release_irq(irq);
204647e07394SAndrew Turner 		return (EINVAL);
204747e07394SAndrew Turner 	}
204847e07394SAndrew Turner 
204947e07394SAndrew Turner 	hypctx = hyp->ctx[vcpuid];
205047e07394SAndrew Turner 	if (hypctx == NULL) {
205147e07394SAndrew Turner 		vgic_v3_release_irq(irq);
205247e07394SAndrew Turner 		return (EINVAL);
205347e07394SAndrew Turner 	}
205447e07394SAndrew Turner 
205547e07394SAndrew Turner 	notify = false;
205647e07394SAndrew Turner 	vgic_cpu = hypctx->vgic_cpu;
205747e07394SAndrew Turner 
205847e07394SAndrew Turner 	mtx_lock_spin(&vgic_cpu->lr_mtx);
205947e07394SAndrew Turner 
206047e07394SAndrew Turner 	if (!vgic_v3_check_irq(irq, level)) {
206147e07394SAndrew Turner 		goto out;
206247e07394SAndrew Turner 	}
206347e07394SAndrew Turner 
206447e07394SAndrew Turner 	if ((irq->config & VGIC_CONFIG_MASK) == VGIC_CONFIG_LEVEL)
206547e07394SAndrew Turner 		irq->level = level;
206647e07394SAndrew Turner 	else /* VGIC_CONFIG_EDGE */
206747e07394SAndrew Turner 		irq->pending = true;
206847e07394SAndrew Turner 
206947e07394SAndrew Turner 	notify = vgic_v3_queue_irq(hyp, vgic_cpu, vcpuid, irq);
207047e07394SAndrew Turner 
207147e07394SAndrew Turner out:
207247e07394SAndrew Turner 	mtx_unlock_spin(&vgic_cpu->lr_mtx);
207347e07394SAndrew Turner 	vgic_v3_release_irq(irq);
207447e07394SAndrew Turner 
207547e07394SAndrew Turner 	if (notify)
207647e07394SAndrew Turner 		vcpu_notify_event(vm_vcpu(hyp->vm, vcpuid));
207747e07394SAndrew Turner 
207847e07394SAndrew Turner 	return (0);
207947e07394SAndrew Turner }
208047e07394SAndrew Turner 
208147e07394SAndrew Turner static int
vgic_v3_inject_msi(device_t dev,struct hyp * hyp,uint64_t msg,uint64_t addr)208247e07394SAndrew Turner vgic_v3_inject_msi(device_t dev, struct hyp *hyp, uint64_t msg, uint64_t addr)
208347e07394SAndrew Turner {
208447e07394SAndrew Turner 	struct vgic_v3 *vgic;
208547e07394SAndrew Turner 	uint64_t reg;
208647e07394SAndrew Turner 
208747e07394SAndrew Turner 	vgic = hyp->vgic;
208847e07394SAndrew Turner 
208947e07394SAndrew Turner 	/* This is a 4 byte register */
209047e07394SAndrew Turner 	if (addr < vgic->dist_start || addr + 4 > vgic->dist_end) {
209147e07394SAndrew Turner 		return (EINVAL);
209247e07394SAndrew Turner 	}
209347e07394SAndrew Turner 
209447e07394SAndrew Turner 	reg = addr - vgic->dist_start;
209547e07394SAndrew Turner 	if (reg != GICD_SETSPI_NSR)
209647e07394SAndrew Turner 		return (EINVAL);
209747e07394SAndrew Turner 
209847e07394SAndrew Turner 	return (INJECT_IRQ(hyp, -1, msg, true));
209947e07394SAndrew Turner }
210047e07394SAndrew Turner 
210147e07394SAndrew Turner static void
vgic_v3_flush_hwstate(device_t dev,struct hypctx * hypctx)210247e07394SAndrew Turner vgic_v3_flush_hwstate(device_t dev, struct hypctx *hypctx)
210347e07394SAndrew Turner {
210447e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
210547e07394SAndrew Turner 	struct vgic_v3_irq *irq;
210647e07394SAndrew Turner 	int i;
210747e07394SAndrew Turner 
210847e07394SAndrew Turner 	vgic_cpu = hypctx->vgic_cpu;
210947e07394SAndrew Turner 
211047e07394SAndrew Turner 	/*
211147e07394SAndrew Turner 	 * All Distributor writes have been executed at this point, do not
211247e07394SAndrew Turner 	 * protect Distributor reads with a mutex.
211347e07394SAndrew Turner 	 *
211447e07394SAndrew Turner 	 * This is callled with all interrupts disabled, so there is no need for
211547e07394SAndrew Turner 	 * a List Register spinlock either.
211647e07394SAndrew Turner 	 */
211747e07394SAndrew Turner 	mtx_lock_spin(&vgic_cpu->lr_mtx);
211847e07394SAndrew Turner 
211947e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_hcr_el2 &= ~ICH_HCR_EL2_UIE;
212047e07394SAndrew Turner 
212147e07394SAndrew Turner 	/* Exit early if there are no buffered interrupts */
212247e07394SAndrew Turner 	if (TAILQ_EMPTY(&vgic_cpu->irq_act_pend))
212347e07394SAndrew Turner 		goto out;
212447e07394SAndrew Turner 
212547e07394SAndrew Turner 	KASSERT(vgic_cpu->ich_lr_used == 0, ("%s: Used LR count not zero %u",
212647e07394SAndrew Turner 	    __func__, vgic_cpu->ich_lr_used));
212747e07394SAndrew Turner 
212847e07394SAndrew Turner 	i = 0;
212947e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_elrsr_el2 =
213047e07394SAndrew Turner 	    (1u << hypctx->vgic_v3_regs.ich_lr_num) - 1;
213147e07394SAndrew Turner 	TAILQ_FOREACH(irq, &vgic_cpu->irq_act_pend, act_pend_list) {
213247e07394SAndrew Turner 		/* No free list register, stop searching for IRQs */
213347e07394SAndrew Turner 		if (i == hypctx->vgic_v3_regs.ich_lr_num)
213447e07394SAndrew Turner 			break;
213547e07394SAndrew Turner 
213647e07394SAndrew Turner 		if (!irq->enabled)
213747e07394SAndrew Turner 			continue;
213847e07394SAndrew Turner 
213947e07394SAndrew Turner 		hypctx->vgic_v3_regs.ich_lr_el2[i] = ICH_LR_EL2_GROUP1 |
214047e07394SAndrew Turner 		    ((uint64_t)irq->priority << ICH_LR_EL2_PRIO_SHIFT) |
214147e07394SAndrew Turner 		    irq->irq;
214247e07394SAndrew Turner 
214347e07394SAndrew Turner 		if (irq->active) {
214447e07394SAndrew Turner 			hypctx->vgic_v3_regs.ich_lr_el2[i] |=
214547e07394SAndrew Turner 			    ICH_LR_EL2_STATE_ACTIVE;
214647e07394SAndrew Turner 		}
214747e07394SAndrew Turner 
214847e07394SAndrew Turner #ifdef notyet
214947e07394SAndrew Turner 		/* TODO: Check why this is needed */
215047e07394SAndrew Turner 		if ((irq->config & _MASK) == LEVEL)
215147e07394SAndrew Turner 			hypctx->vgic_v3_regs.ich_lr_el2[i] |= ICH_LR_EL2_EOI;
215247e07394SAndrew Turner #endif
215347e07394SAndrew Turner 
215447e07394SAndrew Turner 		if (!irq->active && vgic_v3_irq_pending(irq)) {
215547e07394SAndrew Turner 			hypctx->vgic_v3_regs.ich_lr_el2[i] |=
215647e07394SAndrew Turner 			    ICH_LR_EL2_STATE_PENDING;
215747e07394SAndrew Turner 
215847e07394SAndrew Turner 			/*
215947e07394SAndrew Turner 			 * This IRQ is now pending on the guest. Allow for
216047e07394SAndrew Turner 			 * another edge that could cause the interrupt to
216147e07394SAndrew Turner 			 * be raised again.
216247e07394SAndrew Turner 			 */
216347e07394SAndrew Turner 			if ((irq->config & VGIC_CONFIG_MASK) ==
216447e07394SAndrew Turner 			    VGIC_CONFIG_EDGE) {
216547e07394SAndrew Turner 				irq->pending = false;
216647e07394SAndrew Turner 			}
216747e07394SAndrew Turner 		}
216847e07394SAndrew Turner 
216947e07394SAndrew Turner 		i++;
217047e07394SAndrew Turner 	}
217147e07394SAndrew Turner 	vgic_cpu->ich_lr_used = i;
217247e07394SAndrew Turner 
217347e07394SAndrew Turner out:
217447e07394SAndrew Turner 	mtx_unlock_spin(&vgic_cpu->lr_mtx);
217547e07394SAndrew Turner }
217647e07394SAndrew Turner 
217747e07394SAndrew Turner static void
vgic_v3_sync_hwstate(device_t dev,struct hypctx * hypctx)217847e07394SAndrew Turner vgic_v3_sync_hwstate(device_t dev, struct hypctx *hypctx)
217947e07394SAndrew Turner {
218047e07394SAndrew Turner 	struct vgic_v3_cpu *vgic_cpu;
218147e07394SAndrew Turner 	struct vgic_v3_irq *irq;
218247e07394SAndrew Turner 	uint64_t lr;
218347e07394SAndrew Turner 	int i;
218447e07394SAndrew Turner 
218547e07394SAndrew Turner 	vgic_cpu = hypctx->vgic_cpu;
218647e07394SAndrew Turner 
218747e07394SAndrew Turner 	/* Exit early if there are no buffered interrupts */
218847e07394SAndrew Turner 	if (vgic_cpu->ich_lr_used == 0)
218947e07394SAndrew Turner 		return;
219047e07394SAndrew Turner 
219147e07394SAndrew Turner 	/*
219247e07394SAndrew Turner 	 * Check on the IRQ state after running the guest. ich_lr_used and
219347e07394SAndrew Turner 	 * ich_lr_el2 are only ever used within this thread so is safe to
219447e07394SAndrew Turner 	 * access unlocked.
219547e07394SAndrew Turner 	 */
219647e07394SAndrew Turner 	for (i = 0; i < vgic_cpu->ich_lr_used; i++) {
219747e07394SAndrew Turner 		lr = hypctx->vgic_v3_regs.ich_lr_el2[i];
219847e07394SAndrew Turner 		hypctx->vgic_v3_regs.ich_lr_el2[i] = 0;
219947e07394SAndrew Turner 
220047e07394SAndrew Turner 		irq = vgic_v3_get_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
220147e07394SAndrew Turner 		    ICH_LR_EL2_VINTID(lr));
220247e07394SAndrew Turner 		if (irq == NULL)
220347e07394SAndrew Turner 			continue;
220447e07394SAndrew Turner 
220547e07394SAndrew Turner 		irq->active = (lr & ICH_LR_EL2_STATE_ACTIVE) != 0;
220647e07394SAndrew Turner 
220747e07394SAndrew Turner 		if ((irq->config & VGIC_CONFIG_MASK) == VGIC_CONFIG_EDGE) {
220847e07394SAndrew Turner 			/*
220947e07394SAndrew Turner 			 * If we have an edge triggered IRQ preserve the
221047e07394SAndrew Turner 			 * pending bit until the IRQ has been handled.
221147e07394SAndrew Turner 			 */
221247e07394SAndrew Turner 			if ((lr & ICH_LR_EL2_STATE_PENDING) != 0) {
221347e07394SAndrew Turner 				irq->pending = true;
221447e07394SAndrew Turner 			}
221547e07394SAndrew Turner 		} else {
221647e07394SAndrew Turner 			/*
221747e07394SAndrew Turner 			 * If we have a level triggerend IRQ remove the
221847e07394SAndrew Turner 			 * pending bit if the IRQ has been handled.
221947e07394SAndrew Turner 			 * The level is separate, so may still be high
222047e07394SAndrew Turner 			 * triggering another IRQ.
222147e07394SAndrew Turner 			 */
222247e07394SAndrew Turner 			if ((lr & ICH_LR_EL2_STATE_PENDING) == 0) {
222347e07394SAndrew Turner 				irq->pending = false;
222447e07394SAndrew Turner 			}
222547e07394SAndrew Turner 		}
222647e07394SAndrew Turner 
222747e07394SAndrew Turner 		/* Lock to update irq_act_pend */
222847e07394SAndrew Turner 		mtx_lock_spin(&vgic_cpu->lr_mtx);
222947e07394SAndrew Turner 		if (irq->active) {
223047e07394SAndrew Turner 			/* Ensure the active IRQ is at the head of the list */
223147e07394SAndrew Turner 			TAILQ_REMOVE(&vgic_cpu->irq_act_pend, irq,
223247e07394SAndrew Turner 			    act_pend_list);
223347e07394SAndrew Turner 			TAILQ_INSERT_HEAD(&vgic_cpu->irq_act_pend, irq,
223447e07394SAndrew Turner 			    act_pend_list);
223547e07394SAndrew Turner 		} else if (!vgic_v3_irq_pending(irq)) {
223647e07394SAndrew Turner 			/* If pending or active remove from the list */
223747e07394SAndrew Turner 			TAILQ_REMOVE(&vgic_cpu->irq_act_pend, irq,
223847e07394SAndrew Turner 			    act_pend_list);
223947e07394SAndrew Turner 			irq->on_aplist = false;
224047e07394SAndrew Turner 		}
224147e07394SAndrew Turner 		mtx_unlock_spin(&vgic_cpu->lr_mtx);
224247e07394SAndrew Turner 		vgic_v3_release_irq(irq);
224347e07394SAndrew Turner 	}
224447e07394SAndrew Turner 
224547e07394SAndrew Turner 	hypctx->vgic_v3_regs.ich_hcr_el2 &= ~ICH_HCR_EL2_EOICOUNT_MASK;
224647e07394SAndrew Turner 	vgic_cpu->ich_lr_used = 0;
224747e07394SAndrew Turner }
224847e07394SAndrew Turner 
224947e07394SAndrew Turner static void
vgic_v3_init(device_t dev)225047e07394SAndrew Turner vgic_v3_init(device_t dev)
225147e07394SAndrew Turner {
225247e07394SAndrew Turner 	uint64_t ich_vtr_el2;
225347e07394SAndrew Turner 	uint32_t pribits, prebits;
225447e07394SAndrew Turner 
225547e07394SAndrew Turner 	ich_vtr_el2 = vmm_call_hyp(HYP_READ_REGISTER, HYP_REG_ICH_VTR);
225647e07394SAndrew Turner 
225747e07394SAndrew Turner 	/* TODO: These fields are common with the vgicv2 driver */
225847e07394SAndrew Turner 	pribits = ICH_VTR_EL2_PRIBITS(ich_vtr_el2);
225947e07394SAndrew Turner 	switch (pribits) {
226047e07394SAndrew Turner 	default:
226147e07394SAndrew Turner 	case 5:
226247e07394SAndrew Turner 		virt_features.min_prio = 0xf8;
226347e07394SAndrew Turner 		break;
226447e07394SAndrew Turner 	case 6:
226547e07394SAndrew Turner 		virt_features.min_prio = 0xfc;
226647e07394SAndrew Turner 		break;
226747e07394SAndrew Turner 	case 7:
226847e07394SAndrew Turner 		virt_features.min_prio = 0xfe;
226947e07394SAndrew Turner 		break;
227047e07394SAndrew Turner 	case 8:
227147e07394SAndrew Turner 		virt_features.min_prio = 0xff;
227247e07394SAndrew Turner 		break;
227347e07394SAndrew Turner 	}
227447e07394SAndrew Turner 
227547e07394SAndrew Turner 	prebits = ICH_VTR_EL2_PREBITS(ich_vtr_el2);
227647e07394SAndrew Turner 	switch (prebits) {
227747e07394SAndrew Turner 	default:
227847e07394SAndrew Turner 	case 5:
227947e07394SAndrew Turner 		virt_features.ich_apr_num = 1;
228047e07394SAndrew Turner 		break;
228147e07394SAndrew Turner 	case 6:
228247e07394SAndrew Turner 		virt_features.ich_apr_num = 2;
228347e07394SAndrew Turner 		break;
228447e07394SAndrew Turner 	case 7:
228547e07394SAndrew Turner 		virt_features.ich_apr_num = 4;
228647e07394SAndrew Turner 		break;
228747e07394SAndrew Turner 	}
228847e07394SAndrew Turner 
228947e07394SAndrew Turner 	virt_features.ich_lr_num = ICH_VTR_EL2_LISTREGS(ich_vtr_el2);
229047e07394SAndrew Turner }
229147e07394SAndrew Turner 
229247e07394SAndrew Turner static int
vgic_v3_probe(device_t dev)229347e07394SAndrew Turner vgic_v3_probe(device_t dev)
229447e07394SAndrew Turner {
229547e07394SAndrew Turner 	if (!gic_get_vgic(dev))
229647e07394SAndrew Turner 		return (EINVAL);
229747e07394SAndrew Turner 
229847e07394SAndrew Turner 	/* We currently only support the GICv3 */
229947e07394SAndrew Turner 	if (gic_get_hw_rev(dev) < 3)
230047e07394SAndrew Turner 		return (EINVAL);
230147e07394SAndrew Turner 
230247e07394SAndrew Turner 	device_set_desc(dev, "Virtual GIC v3");
230347e07394SAndrew Turner 	return (BUS_PROBE_DEFAULT);
230447e07394SAndrew Turner }
230547e07394SAndrew Turner 
230647e07394SAndrew Turner static int
vgic_v3_attach(device_t dev)230747e07394SAndrew Turner vgic_v3_attach(device_t dev)
230847e07394SAndrew Turner {
230947e07394SAndrew Turner 	vgic_dev = dev;
231047e07394SAndrew Turner 	return (0);
231147e07394SAndrew Turner }
231247e07394SAndrew Turner 
231347e07394SAndrew Turner static int
vgic_v3_detach(device_t dev)231447e07394SAndrew Turner vgic_v3_detach(device_t dev)
231547e07394SAndrew Turner {
231647e07394SAndrew Turner 	vgic_dev = NULL;
231747e07394SAndrew Turner 	return (0);
231847e07394SAndrew Turner }
231947e07394SAndrew Turner 
232047e07394SAndrew Turner static device_method_t vgic_v3_methods[] = {
232147e07394SAndrew Turner 	/* Device interface */
232247e07394SAndrew Turner 	DEVMETHOD(device_probe,		vgic_v3_probe),
232347e07394SAndrew Turner 	DEVMETHOD(device_attach,	vgic_v3_attach),
232447e07394SAndrew Turner 	DEVMETHOD(device_detach,	vgic_v3_detach),
232547e07394SAndrew Turner 
232647e07394SAndrew Turner 	/* VGIC interface */
232747e07394SAndrew Turner 	DEVMETHOD(vgic_init,		vgic_v3_init),
232847e07394SAndrew Turner 	DEVMETHOD(vgic_attach_to_vm,	vgic_v3_attach_to_vm),
232947e07394SAndrew Turner 	DEVMETHOD(vgic_detach_from_vm,	vgic_v3_detach_from_vm),
233047e07394SAndrew Turner 	DEVMETHOD(vgic_vminit,		vgic_v3_vminit),
233147e07394SAndrew Turner 	DEVMETHOD(vgic_cpuinit,		vgic_v3_cpuinit),
233247e07394SAndrew Turner 	DEVMETHOD(vgic_cpucleanup,	vgic_v3_cpucleanup),
233347e07394SAndrew Turner 	DEVMETHOD(vgic_vmcleanup,	vgic_v3_vmcleanup),
233447e07394SAndrew Turner 	DEVMETHOD(vgic_max_cpu_count,	vgic_v3_max_cpu_count),
233547e07394SAndrew Turner 	DEVMETHOD(vgic_has_pending_irq,	vgic_v3_has_pending_irq),
233647e07394SAndrew Turner 	DEVMETHOD(vgic_inject_irq,	vgic_v3_inject_irq),
233747e07394SAndrew Turner 	DEVMETHOD(vgic_inject_msi,	vgic_v3_inject_msi),
233847e07394SAndrew Turner 	DEVMETHOD(vgic_flush_hwstate,	vgic_v3_flush_hwstate),
233947e07394SAndrew Turner 	DEVMETHOD(vgic_sync_hwstate,	vgic_v3_sync_hwstate),
234047e07394SAndrew Turner 
234147e07394SAndrew Turner 	/* End */
234247e07394SAndrew Turner 	DEVMETHOD_END
234347e07394SAndrew Turner };
234447e07394SAndrew Turner 
234547e07394SAndrew Turner /* TODO: Create a vgic base class? */
234647e07394SAndrew Turner DEFINE_CLASS_0(vgic, vgic_v3_driver, vgic_v3_methods, 0);
234747e07394SAndrew Turner 
234847e07394SAndrew Turner DRIVER_MODULE(vgic_v3, gic, vgic_v3_driver, 0, 0);
2349