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