xref: /freebsd/sys/riscv/vmm/vmm_aplic.c (revision ae65d59d4b8c227ac27b58497b9295e09fa9a179)
1d3916eacSRuslan Bukin /*-
2d3916eacSRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
3d3916eacSRuslan Bukin  *
4d3916eacSRuslan Bukin  * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
5d3916eacSRuslan Bukin  *
6d3916eacSRuslan Bukin  * This software was developed by the University of Cambridge Computer
7d3916eacSRuslan Bukin  * Laboratory (Department of Computer Science and Technology) under Innovate
8d3916eacSRuslan Bukin  * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
9d3916eacSRuslan Bukin  * Prototype".
10d3916eacSRuslan Bukin  *
11d3916eacSRuslan Bukin  * Redistribution and use in source and binary forms, with or without
12d3916eacSRuslan Bukin  * modification, are permitted provided that the following conditions
13d3916eacSRuslan Bukin  * are met:
14d3916eacSRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
15d3916eacSRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
16d3916eacSRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
17d3916eacSRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
18d3916eacSRuslan Bukin  *    documentation and/or other materials provided with the distribution.
19d3916eacSRuslan Bukin  *
20d3916eacSRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21d3916eacSRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22d3916eacSRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23d3916eacSRuslan Bukin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24d3916eacSRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25d3916eacSRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26d3916eacSRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27d3916eacSRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28d3916eacSRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29d3916eacSRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30d3916eacSRuslan Bukin  * SUCH DAMAGE.
31d3916eacSRuslan Bukin  */
32d3916eacSRuslan Bukin 
33d3916eacSRuslan Bukin #include <sys/types.h>
34d3916eacSRuslan Bukin #include <sys/errno.h>
35d3916eacSRuslan Bukin #include <sys/systm.h>
36d3916eacSRuslan Bukin #include <sys/bus.h>
37d3916eacSRuslan Bukin #include <sys/kernel.h>
38d3916eacSRuslan Bukin #include <sys/lock.h>
39d3916eacSRuslan Bukin #include <sys/malloc.h>
40d3916eacSRuslan Bukin #include <sys/module.h>
41d3916eacSRuslan Bukin #include <sys/mutex.h>
42d3916eacSRuslan Bukin #include <sys/rman.h>
43d3916eacSRuslan Bukin #include <sys/smp.h>
44d3916eacSRuslan Bukin 
45d3916eacSRuslan Bukin #include <riscv/vmm/riscv.h>
46d3916eacSRuslan Bukin #include <riscv/vmm/vmm_aplic.h>
47d3916eacSRuslan Bukin 
48d3916eacSRuslan Bukin #include <machine/vmm_instruction_emul.h>
49d3916eacSRuslan Bukin #include <machine/vmm_dev.h>
50d3916eacSRuslan Bukin 
51d3916eacSRuslan Bukin MALLOC_DEFINE(M_APLIC, "RISC-V VMM APLIC", "RISC-V AIA APLIC");
52d3916eacSRuslan Bukin 
53d3916eacSRuslan Bukin #define	APLIC_DOMAINCFG		0x0000
54d3916eacSRuslan Bukin #define	 DOMAINCFG_IE		(1 << 8) /* Interrupt Enable. */
55d3916eacSRuslan Bukin #define	 DOMAINCFG_DM		(1 << 2) /* Direct Mode. */
56d3916eacSRuslan Bukin #define	 DOMAINCFG_BE		(1 << 0) /* Big-Endian. */
57d3916eacSRuslan Bukin #define	APLIC_SOURCECFG(x)	(0x0004 + ((x) - 1) * 4)
58d3916eacSRuslan Bukin #define	 SOURCECFG_D		(1 << 10) /* D - Delegate. */
59d3916eacSRuslan Bukin /* If D == 0. */
60d3916eacSRuslan Bukin #define	 SOURCECFG_SM_S		(0)
61d3916eacSRuslan Bukin #define	 SOURCECFG_SM_M		(0x7 << SOURCECFG_SM_S)
62d3916eacSRuslan Bukin #define	 SOURCECFG_SM_INACTIVE	(0) /* Not delegated. */
63d3916eacSRuslan Bukin #define	 SOURCECFG_SM_DETACHED	(1)
64d3916eacSRuslan Bukin #define	 SOURCECFG_SM_RESERVED	(2)
65d3916eacSRuslan Bukin #define	 SOURCECFG_SM_RESERVED1	(3)
66d3916eacSRuslan Bukin #define	 SOURCECFG_SM_EDGE1	(4) /* Rising edge. */
67d3916eacSRuslan Bukin #define	 SOURCECFG_SM_EDGE0	(5) /* Falling edge. */
68d3916eacSRuslan Bukin #define	 SOURCECFG_SM_LEVEL1	(6) /* High. */
69d3916eacSRuslan Bukin #define	 SOURCECFG_SM_LEVEL0	(7) /* Low. */
70d3916eacSRuslan Bukin /* If D == 1. */
71d3916eacSRuslan Bukin #define	 SOURCECFG_CHILD_INDEX_S	(0)
72d3916eacSRuslan Bukin #define	 SOURCECFG_CHILD_INDEX_M	(0x3ff << SOURCECFG_CHILD_INDEX_S)
73*ae65d59dSRuslan Bukin #define	APLIC_SETIP		0x1c00
74d3916eacSRuslan Bukin #define	APLIC_SETIPNUM		0x1cdc
75*ae65d59dSRuslan Bukin #define	APLIC_CLRIP		0x1d00
76d3916eacSRuslan Bukin #define	APLIC_CLRIPNUM		0x1ddc
77*ae65d59dSRuslan Bukin #define	APLIC_SETIE		0x1e00
78d3916eacSRuslan Bukin #define	APLIC_SETIENUM		0x1edc
79*ae65d59dSRuslan Bukin #define	APLIC_CLRIE		0x1f00
80d3916eacSRuslan Bukin #define	APLIC_CLRIENUM		0x1fdc
81d3916eacSRuslan Bukin #define	APLIC_GENMSI		0x3000
82d3916eacSRuslan Bukin #define	APLIC_TARGET(x)		(0x3004 + ((x) - 1) * 4)
83d3916eacSRuslan Bukin #define	 TARGET_HART_S		18
84d3916eacSRuslan Bukin #define	 TARGET_HART_M		0x3fff
85d3916eacSRuslan Bukin #define	APLIC_IDC(x)		(0x4000 + (x) * 32)
86d3916eacSRuslan Bukin #define	 IDC_IDELIVERY(x)	(APLIC_IDC(x) + 0x0)
87d3916eacSRuslan Bukin #define	 IDC_IFORCE(x)		(APLIC_IDC(x) + 0x4)
88d3916eacSRuslan Bukin #define	 IDC_ITHRESHOLD(x)	(APLIC_IDC(x) + 0x8)
89d3916eacSRuslan Bukin #define	 IDC_TOPI(x)		(APLIC_IDC(x) + 0x18)
90d3916eacSRuslan Bukin #define	 IDC_CLAIMI(x)		(APLIC_IDC(x) + 0x1C)
91d3916eacSRuslan Bukin #define	   CLAIMI_IRQ_S		(16)
92d3916eacSRuslan Bukin #define	   CLAIMI_IRQ_M		(0x3ff << CLAIMI_IRQ_S)
93d3916eacSRuslan Bukin #define	   CLAIMI_PRIO_S	(0)
94d3916eacSRuslan Bukin #define	   CLAIMI_PRIO_M	(0xff << CLAIMI_PRIO_S)
95d3916eacSRuslan Bukin 
96d3916eacSRuslan Bukin #define	APLIC_NIRQS	63
97d3916eacSRuslan Bukin 
98d3916eacSRuslan Bukin struct aplic_irq {
99d3916eacSRuslan Bukin 	uint32_t sourcecfg;
100d3916eacSRuslan Bukin 	uint32_t state;
101d3916eacSRuslan Bukin #define	APLIC_IRQ_STATE_PENDING	(1 << 0)
102d3916eacSRuslan Bukin #define	APLIC_IRQ_STATE_ENABLED	(1 << 1)
103*ae65d59dSRuslan Bukin #define	APLIC_IRQ_STATE_INPUT	(1 << 2)
104d3916eacSRuslan Bukin 	uint32_t target;
105d3916eacSRuslan Bukin 	uint32_t target_hart;
106d3916eacSRuslan Bukin };
107d3916eacSRuslan Bukin 
108d3916eacSRuslan Bukin struct aplic {
109d3916eacSRuslan Bukin 	uint32_t mem_start;
110d3916eacSRuslan Bukin 	uint32_t mem_end;
111d3916eacSRuslan Bukin 	struct mtx mtx;
112d3916eacSRuslan Bukin 	struct aplic_irq *irqs;
113d3916eacSRuslan Bukin 	int nirqs;
114d3916eacSRuslan Bukin 	uint32_t domaincfg;
115d3916eacSRuslan Bukin };
116d3916eacSRuslan Bukin 
117d3916eacSRuslan Bukin static int
aplic_handle_sourcecfg(struct aplic * aplic,int i,bool write,uint64_t * val)118d3916eacSRuslan Bukin aplic_handle_sourcecfg(struct aplic *aplic, int i, bool write, uint64_t *val)
119d3916eacSRuslan Bukin {
120d3916eacSRuslan Bukin 	struct aplic_irq *irq;
121d3916eacSRuslan Bukin 
122d3916eacSRuslan Bukin 	if (i <= 0 || i > aplic->nirqs)
123d3916eacSRuslan Bukin 		return (ENOENT);
124d3916eacSRuslan Bukin 
125d3916eacSRuslan Bukin 	mtx_lock_spin(&aplic->mtx);
126d3916eacSRuslan Bukin 	irq = &aplic->irqs[i];
127d3916eacSRuslan Bukin 	if (write)
128d3916eacSRuslan Bukin 		irq->sourcecfg = *val;
129d3916eacSRuslan Bukin 	else
130d3916eacSRuslan Bukin 		*val = irq->sourcecfg;
131d3916eacSRuslan Bukin 	mtx_unlock_spin(&aplic->mtx);
132d3916eacSRuslan Bukin 
133d3916eacSRuslan Bukin 	return (0);
134d3916eacSRuslan Bukin }
135d3916eacSRuslan Bukin 
136d3916eacSRuslan Bukin static int
aplic_set_enabled(struct aplic * aplic,bool write,uint64_t * val,bool enabled)137d3916eacSRuslan Bukin aplic_set_enabled(struct aplic *aplic, bool write, uint64_t *val, bool enabled)
138d3916eacSRuslan Bukin {
139d3916eacSRuslan Bukin 	struct aplic_irq *irq;
140d3916eacSRuslan Bukin 	int i;
141d3916eacSRuslan Bukin 
142d3916eacSRuslan Bukin 	if (!write) {
143d3916eacSRuslan Bukin 		*val = 0;
144d3916eacSRuslan Bukin 		return (0);
145d3916eacSRuslan Bukin 	}
146d3916eacSRuslan Bukin 
147d3916eacSRuslan Bukin 	i = *val;
148d3916eacSRuslan Bukin 	if (i <= 0 || i > aplic->nirqs)
149d3916eacSRuslan Bukin 		return (-1);
150d3916eacSRuslan Bukin 
151d3916eacSRuslan Bukin 	irq = &aplic->irqs[i];
152d3916eacSRuslan Bukin 
153d3916eacSRuslan Bukin 	mtx_lock_spin(&aplic->mtx);
154*ae65d59dSRuslan Bukin 	if ((irq->sourcecfg & SOURCECFG_SM_M) != SOURCECFG_SM_INACTIVE) {
155d3916eacSRuslan Bukin 		if (enabled)
156d3916eacSRuslan Bukin 			irq->state |= APLIC_IRQ_STATE_ENABLED;
157d3916eacSRuslan Bukin 		else
158d3916eacSRuslan Bukin 			irq->state &= ~APLIC_IRQ_STATE_ENABLED;
159*ae65d59dSRuslan Bukin 	}
160d3916eacSRuslan Bukin 	mtx_unlock_spin(&aplic->mtx);
161d3916eacSRuslan Bukin 
162d3916eacSRuslan Bukin 	return (0);
163d3916eacSRuslan Bukin }
164d3916eacSRuslan Bukin 
165*ae65d59dSRuslan Bukin static void
aplic_set_enabled_word(struct aplic * aplic,bool write,uint32_t word,uint64_t * val,bool enabled)166*ae65d59dSRuslan Bukin aplic_set_enabled_word(struct aplic *aplic, bool write, uint32_t word,
167*ae65d59dSRuslan Bukin     uint64_t *val, bool enabled)
168*ae65d59dSRuslan Bukin {
169*ae65d59dSRuslan Bukin 	uint64_t v;
170*ae65d59dSRuslan Bukin 	int i;
171*ae65d59dSRuslan Bukin 
172*ae65d59dSRuslan Bukin 	if (!write) {
173*ae65d59dSRuslan Bukin 		*val = 0;
174*ae65d59dSRuslan Bukin 		return;
175*ae65d59dSRuslan Bukin 	}
176*ae65d59dSRuslan Bukin 
177*ae65d59dSRuslan Bukin 	/*
178*ae65d59dSRuslan Bukin 	 * The write is ignored if value written is not an active interrupt
179*ae65d59dSRuslan Bukin 	 * source number in the domain.
180*ae65d59dSRuslan Bukin 	 */
181*ae65d59dSRuslan Bukin 	for (i = 0; i < 32; i++)
182*ae65d59dSRuslan Bukin 		if (*val & (1u << i)) {
183*ae65d59dSRuslan Bukin 			v = word * 32 + i;
184*ae65d59dSRuslan Bukin 			(void)aplic_set_enabled(aplic, write, &v, enabled);
185*ae65d59dSRuslan Bukin 		}
186*ae65d59dSRuslan Bukin }
187*ae65d59dSRuslan Bukin 
188d3916eacSRuslan Bukin static int
aplic_handle_target(struct aplic * aplic,int i,bool write,uint64_t * val)189d3916eacSRuslan Bukin aplic_handle_target(struct aplic *aplic, int i, bool write, uint64_t *val)
190d3916eacSRuslan Bukin {
191d3916eacSRuslan Bukin 	struct aplic_irq *irq;
192d3916eacSRuslan Bukin 
193d3916eacSRuslan Bukin 	mtx_lock_spin(&aplic->mtx);
194d3916eacSRuslan Bukin 	irq = &aplic->irqs[i];
195d3916eacSRuslan Bukin 	if (write) {
196d3916eacSRuslan Bukin 		irq->target = *val;
197d3916eacSRuslan Bukin 		irq->target_hart = (irq->target >> TARGET_HART_S);
198d3916eacSRuslan Bukin 	} else
199d3916eacSRuslan Bukin 		*val = irq->target;
200d3916eacSRuslan Bukin 	mtx_unlock_spin(&aplic->mtx);
201d3916eacSRuslan Bukin 
202d3916eacSRuslan Bukin 	return (0);
203d3916eacSRuslan Bukin }
204d3916eacSRuslan Bukin 
205d3916eacSRuslan Bukin static int
aplic_handle_idc_claimi(struct hyp * hyp,struct aplic * aplic,int cpu_id,bool write,uint64_t * val)206d3916eacSRuslan Bukin aplic_handle_idc_claimi(struct hyp *hyp, struct aplic *aplic, int cpu_id,
207d3916eacSRuslan Bukin     bool write, uint64_t *val)
208d3916eacSRuslan Bukin {
209d3916eacSRuslan Bukin 	struct aplic_irq *irq;
210d3916eacSRuslan Bukin 	bool found;
211d3916eacSRuslan Bukin 	int i;
212d3916eacSRuslan Bukin 
213d3916eacSRuslan Bukin 	/* Writes to claimi are ignored. */
214d3916eacSRuslan Bukin 	if (write)
215d3916eacSRuslan Bukin 		return (-1);
216d3916eacSRuslan Bukin 
217d3916eacSRuslan Bukin 	found = false;
218d3916eacSRuslan Bukin 
219d3916eacSRuslan Bukin 	mtx_lock_spin(&aplic->mtx);
220d3916eacSRuslan Bukin 	for (i = 0; i < aplic->nirqs; i++) {
221d3916eacSRuslan Bukin 		irq = &aplic->irqs[i];
222d3916eacSRuslan Bukin 		if (irq->target_hart != cpu_id)
223d3916eacSRuslan Bukin 			continue;
224d3916eacSRuslan Bukin 		if (irq->state & APLIC_IRQ_STATE_PENDING) {
225d3916eacSRuslan Bukin 			*val = (i << CLAIMI_IRQ_S) | (0 << CLAIMI_PRIO_S);
226d3916eacSRuslan Bukin 			irq->state &= ~APLIC_IRQ_STATE_PENDING;
227d3916eacSRuslan Bukin 			found = true;
228d3916eacSRuslan Bukin 			break;
229d3916eacSRuslan Bukin 		}
230d3916eacSRuslan Bukin 	}
231d3916eacSRuslan Bukin 	mtx_unlock_spin(&aplic->mtx);
232d3916eacSRuslan Bukin 
233d3916eacSRuslan Bukin 	if (found == false)
234d3916eacSRuslan Bukin 		*val = 0;
235d3916eacSRuslan Bukin 
236d3916eacSRuslan Bukin 	return (0);
237d3916eacSRuslan Bukin }
238d3916eacSRuslan Bukin 
239d3916eacSRuslan Bukin static int
aplic_handle_idc(struct hyp * hyp,struct aplic * aplic,int cpu,int reg,bool write,uint64_t * val)240d3916eacSRuslan Bukin aplic_handle_idc(struct hyp *hyp, struct aplic *aplic, int cpu, int reg,
241d3916eacSRuslan Bukin     bool write, uint64_t *val)
242d3916eacSRuslan Bukin {
243d3916eacSRuslan Bukin 	int error;
244d3916eacSRuslan Bukin 
245d3916eacSRuslan Bukin 	switch (reg + APLIC_IDC(0)) {
246d3916eacSRuslan Bukin 	case IDC_IDELIVERY(0):
247d3916eacSRuslan Bukin 	case IDC_IFORCE(0):
248d3916eacSRuslan Bukin 	case IDC_ITHRESHOLD(0):
249d3916eacSRuslan Bukin 	case IDC_TOPI(0):
250d3916eacSRuslan Bukin 		error = 0;
251d3916eacSRuslan Bukin 		break;
252d3916eacSRuslan Bukin 	case IDC_CLAIMI(0):
253d3916eacSRuslan Bukin 		error = aplic_handle_idc_claimi(hyp, aplic, cpu, write, val);
254d3916eacSRuslan Bukin 		break;
255d3916eacSRuslan Bukin 	default:
256d3916eacSRuslan Bukin 		error = ENOENT;
257d3916eacSRuslan Bukin 	}
258d3916eacSRuslan Bukin 
259d3916eacSRuslan Bukin 	return (error);
260d3916eacSRuslan Bukin }
261d3916eacSRuslan Bukin 
262d3916eacSRuslan Bukin static int
aplic_mmio_access(struct hyp * hyp,struct aplic * aplic,uint64_t reg,bool write,uint64_t * val)263d3916eacSRuslan Bukin aplic_mmio_access(struct hyp *hyp, struct aplic *aplic, uint64_t reg,
264d3916eacSRuslan Bukin     bool write, uint64_t *val)
265d3916eacSRuslan Bukin {
266d3916eacSRuslan Bukin 	int error;
267d3916eacSRuslan Bukin 	int cpu;
268d3916eacSRuslan Bukin 	int r;
269d3916eacSRuslan Bukin 	int i;
270d3916eacSRuslan Bukin 
271*ae65d59dSRuslan Bukin 	dprintf("%s: reg %lx\n", __func__, reg);
272*ae65d59dSRuslan Bukin 
273d3916eacSRuslan Bukin 	if ((reg >= APLIC_SOURCECFG(1)) &&
274d3916eacSRuslan Bukin 	    (reg <= APLIC_SOURCECFG(aplic->nirqs))) {
275d3916eacSRuslan Bukin 		i = ((reg - APLIC_SOURCECFG(1)) >> 2) + 1;
276d3916eacSRuslan Bukin 		error = aplic_handle_sourcecfg(aplic, i, write, val);
277d3916eacSRuslan Bukin 		return (error);
278d3916eacSRuslan Bukin 	}
279d3916eacSRuslan Bukin 
280d3916eacSRuslan Bukin 	if ((reg >= APLIC_TARGET(1)) && (reg <= APLIC_TARGET(aplic->nirqs))) {
281d3916eacSRuslan Bukin 		i = ((reg - APLIC_TARGET(1)) >> 2) + 1;
282d3916eacSRuslan Bukin 		error = aplic_handle_target(aplic, i, write, val);
283d3916eacSRuslan Bukin 		return (error);
284d3916eacSRuslan Bukin 	}
285d3916eacSRuslan Bukin 
286d3916eacSRuslan Bukin 	if ((reg >= APLIC_IDC(0)) && (reg < APLIC_IDC(mp_ncpus))) {
287d3916eacSRuslan Bukin 		cpu = (reg - APLIC_IDC(0)) >> 5;
288d3916eacSRuslan Bukin 		r = (reg - APLIC_IDC(0)) % 32;
289d3916eacSRuslan Bukin 		error = aplic_handle_idc(hyp, aplic, cpu, r, write, val);
290d3916eacSRuslan Bukin 		return (error);
291d3916eacSRuslan Bukin 	}
292d3916eacSRuslan Bukin 
293*ae65d59dSRuslan Bukin 	if ((reg >= APLIC_CLRIE) && (reg < (APLIC_CLRIE + aplic->nirqs * 4))) {
294*ae65d59dSRuslan Bukin 		i = (reg - APLIC_CLRIE) >> 2;
295*ae65d59dSRuslan Bukin 		aplic_set_enabled_word(aplic, write, i, val, false);
296*ae65d59dSRuslan Bukin 		return (0);
297*ae65d59dSRuslan Bukin 	}
298*ae65d59dSRuslan Bukin 
299d3916eacSRuslan Bukin 	switch (reg) {
300d3916eacSRuslan Bukin 	case APLIC_DOMAINCFG:
301*ae65d59dSRuslan Bukin 		mtx_lock_spin(&aplic->mtx);
302*ae65d59dSRuslan Bukin 		if (write)
303d3916eacSRuslan Bukin 			aplic->domaincfg = *val & DOMAINCFG_IE;
304*ae65d59dSRuslan Bukin 		else
305*ae65d59dSRuslan Bukin 			*val = aplic->domaincfg;
306*ae65d59dSRuslan Bukin 		mtx_unlock_spin(&aplic->mtx);
307d3916eacSRuslan Bukin 		error = 0;
308d3916eacSRuslan Bukin 		break;
309d3916eacSRuslan Bukin 	case APLIC_SETIENUM:
310d3916eacSRuslan Bukin 		error = aplic_set_enabled(aplic, write, val, true);
311d3916eacSRuslan Bukin 		break;
312d3916eacSRuslan Bukin 	case APLIC_CLRIENUM:
313d3916eacSRuslan Bukin 		error = aplic_set_enabled(aplic, write, val, false);
314d3916eacSRuslan Bukin 		break;
315d3916eacSRuslan Bukin 	default:
316d3916eacSRuslan Bukin 		dprintf("%s: unknown reg %lx", __func__, reg);
317d3916eacSRuslan Bukin 		error = ENOENT;
318d3916eacSRuslan Bukin 		break;
319d3916eacSRuslan Bukin 	};
320d3916eacSRuslan Bukin 
321d3916eacSRuslan Bukin 	return (error);
322d3916eacSRuslan Bukin }
323d3916eacSRuslan Bukin 
324d3916eacSRuslan Bukin static int
mem_read(struct vcpu * vcpu,uint64_t fault_ipa,uint64_t * rval,int size,void * arg)325d3916eacSRuslan Bukin mem_read(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t *rval, int size,
326d3916eacSRuslan Bukin     void *arg)
327d3916eacSRuslan Bukin {
328d3916eacSRuslan Bukin 	struct hypctx *hypctx;
329d3916eacSRuslan Bukin 	struct hyp *hyp;
330d3916eacSRuslan Bukin 	struct aplic *aplic;
331d3916eacSRuslan Bukin 	uint64_t reg;
332d3916eacSRuslan Bukin 	uint64_t val;
333d3916eacSRuslan Bukin 	int error;
334d3916eacSRuslan Bukin 
335d3916eacSRuslan Bukin 	hypctx = vcpu_get_cookie(vcpu);
336d3916eacSRuslan Bukin 	hyp = hypctx->hyp;
337d3916eacSRuslan Bukin 	aplic = hyp->aplic;
338d3916eacSRuslan Bukin 
339d3916eacSRuslan Bukin 	dprintf("%s: fault_ipa %lx size %d\n", __func__, fault_ipa, size);
340d3916eacSRuslan Bukin 
341d3916eacSRuslan Bukin 	if (fault_ipa < aplic->mem_start || fault_ipa + size > aplic->mem_end)
342d3916eacSRuslan Bukin 		return (EINVAL);
343d3916eacSRuslan Bukin 
344d3916eacSRuslan Bukin 	reg = fault_ipa - aplic->mem_start;
345d3916eacSRuslan Bukin 
346d3916eacSRuslan Bukin 	error = aplic_mmio_access(hyp, aplic, reg, false, &val);
347d3916eacSRuslan Bukin 	if (error == 0)
348d3916eacSRuslan Bukin 		*rval = val;
349d3916eacSRuslan Bukin 
350d3916eacSRuslan Bukin 	return (error);
351d3916eacSRuslan Bukin }
352d3916eacSRuslan Bukin 
353d3916eacSRuslan Bukin static int
mem_write(struct vcpu * vcpu,uint64_t fault_ipa,uint64_t wval,int size,void * arg)354d3916eacSRuslan Bukin mem_write(struct vcpu *vcpu, uint64_t fault_ipa, uint64_t wval, int size,
355d3916eacSRuslan Bukin     void *arg)
356d3916eacSRuslan Bukin {
357d3916eacSRuslan Bukin 	struct hypctx *hypctx;
358d3916eacSRuslan Bukin 	struct hyp *hyp;
359d3916eacSRuslan Bukin 	struct aplic *aplic;
360d3916eacSRuslan Bukin 	uint64_t reg;
361d3916eacSRuslan Bukin 	uint64_t val;
362d3916eacSRuslan Bukin 	int error;
363d3916eacSRuslan Bukin 
364d3916eacSRuslan Bukin 	hypctx = vcpu_get_cookie(vcpu);
365d3916eacSRuslan Bukin 	hyp = hypctx->hyp;
366d3916eacSRuslan Bukin 	aplic = hyp->aplic;
367d3916eacSRuslan Bukin 
368d3916eacSRuslan Bukin 	dprintf("%s: fault_ipa %lx wval %lx size %d\n", __func__, fault_ipa,
369d3916eacSRuslan Bukin 	    wval, size);
370d3916eacSRuslan Bukin 
371d3916eacSRuslan Bukin 	if (fault_ipa < aplic->mem_start || fault_ipa + size > aplic->mem_end)
372d3916eacSRuslan Bukin 		return (EINVAL);
373d3916eacSRuslan Bukin 
374d3916eacSRuslan Bukin 	reg = fault_ipa - aplic->mem_start;
375d3916eacSRuslan Bukin 
376d3916eacSRuslan Bukin 	val = wval;
377d3916eacSRuslan Bukin 
378d3916eacSRuslan Bukin 	error = aplic_mmio_access(hyp, aplic, reg, true, &val);
379d3916eacSRuslan Bukin 
380d3916eacSRuslan Bukin 	return (error);
381d3916eacSRuslan Bukin }
382d3916eacSRuslan Bukin 
383d3916eacSRuslan Bukin void
aplic_vminit(struct hyp * hyp)384d3916eacSRuslan Bukin aplic_vminit(struct hyp *hyp)
385d3916eacSRuslan Bukin {
386d3916eacSRuslan Bukin 	struct aplic *aplic;
387d3916eacSRuslan Bukin 
388d3916eacSRuslan Bukin 	hyp->aplic = malloc(sizeof(*hyp->aplic), M_APLIC,
389d3916eacSRuslan Bukin 	    M_WAITOK | M_ZERO);
390d3916eacSRuslan Bukin 	aplic = hyp->aplic;
391d3916eacSRuslan Bukin 
392d3916eacSRuslan Bukin 	mtx_init(&aplic->mtx, "APLIC lock", NULL, MTX_SPIN);
393d3916eacSRuslan Bukin }
394d3916eacSRuslan Bukin 
395d3916eacSRuslan Bukin void
aplic_vmcleanup(struct hyp * hyp)396d3916eacSRuslan Bukin aplic_vmcleanup(struct hyp *hyp)
397d3916eacSRuslan Bukin {
398d3916eacSRuslan Bukin 	struct aplic *aplic;
399d3916eacSRuslan Bukin 
400d3916eacSRuslan Bukin 	aplic = hyp->aplic;
401d3916eacSRuslan Bukin 
402d3916eacSRuslan Bukin 	mtx_destroy(&aplic->mtx);
403d3916eacSRuslan Bukin 
404d3916eacSRuslan Bukin 	free(hyp->aplic, M_APLIC);
405d3916eacSRuslan Bukin }
406d3916eacSRuslan Bukin 
407d3916eacSRuslan Bukin int
aplic_attach_to_vm(struct hyp * hyp,struct vm_aplic_descr * descr)408d3916eacSRuslan Bukin aplic_attach_to_vm(struct hyp *hyp, struct vm_aplic_descr *descr)
409d3916eacSRuslan Bukin {
410d3916eacSRuslan Bukin 	struct aplic *aplic;
411d3916eacSRuslan Bukin 	struct vm *vm;
412d3916eacSRuslan Bukin 
413d3916eacSRuslan Bukin 	vm = hyp->vm;
414d3916eacSRuslan Bukin 
415d3916eacSRuslan Bukin 	dprintf("%s\n", __func__);
416d3916eacSRuslan Bukin 
417d3916eacSRuslan Bukin 	vm_register_inst_handler(vm, descr->mem_start, descr->mem_size,
418d3916eacSRuslan Bukin 	    mem_read, mem_write);
419d3916eacSRuslan Bukin 
420d3916eacSRuslan Bukin 	aplic = hyp->aplic;
421d3916eacSRuslan Bukin 	aplic->nirqs = APLIC_NIRQS;
422d3916eacSRuslan Bukin 	aplic->mem_start = descr->mem_start;
423d3916eacSRuslan Bukin 	aplic->mem_end = descr->mem_start + descr->mem_size;
424d3916eacSRuslan Bukin 	aplic->irqs = malloc(sizeof(struct aplic_irq) * aplic->nirqs, M_APLIC,
425d3916eacSRuslan Bukin 	    M_WAITOK | M_ZERO);
426d3916eacSRuslan Bukin 
427d3916eacSRuslan Bukin 	hyp->aplic_attached = true;
428d3916eacSRuslan Bukin 
429d3916eacSRuslan Bukin 	return (0);
430d3916eacSRuslan Bukin }
431d3916eacSRuslan Bukin 
432d3916eacSRuslan Bukin void
aplic_detach_from_vm(struct hyp * hyp)433d3916eacSRuslan Bukin aplic_detach_from_vm(struct hyp *hyp)
434d3916eacSRuslan Bukin {
435d3916eacSRuslan Bukin 	struct aplic *aplic;
436d3916eacSRuslan Bukin 
437d3916eacSRuslan Bukin 	aplic = hyp->aplic;
438d3916eacSRuslan Bukin 
439d3916eacSRuslan Bukin 	dprintf("%s\n", __func__);
440d3916eacSRuslan Bukin 
441d3916eacSRuslan Bukin 	if (hyp->aplic_attached) {
442d3916eacSRuslan Bukin 		hyp->aplic_attached = false;
443d3916eacSRuslan Bukin 		free(aplic->irqs, M_APLIC);
444d3916eacSRuslan Bukin 	}
445d3916eacSRuslan Bukin }
446d3916eacSRuslan Bukin 
447d3916eacSRuslan Bukin int
aplic_check_pending(struct hypctx * hypctx)448d3916eacSRuslan Bukin aplic_check_pending(struct hypctx *hypctx)
449d3916eacSRuslan Bukin {
450d3916eacSRuslan Bukin 	struct aplic_irq *irq;
451d3916eacSRuslan Bukin 	struct aplic *aplic;
452d3916eacSRuslan Bukin 	struct hyp *hyp;
453d3916eacSRuslan Bukin 	int i;
454d3916eacSRuslan Bukin 
455d3916eacSRuslan Bukin 	hyp = hypctx->hyp;
456d3916eacSRuslan Bukin 	aplic = hyp->aplic;
457d3916eacSRuslan Bukin 
458d3916eacSRuslan Bukin 	mtx_lock_spin(&aplic->mtx);
459d3916eacSRuslan Bukin 	if ((aplic->domaincfg & DOMAINCFG_IE) == 0) {
460d3916eacSRuslan Bukin 		mtx_unlock_spin(&aplic->mtx);
461d3916eacSRuslan Bukin 		return (0);
462d3916eacSRuslan Bukin 	}
463d3916eacSRuslan Bukin 
464d3916eacSRuslan Bukin 	for (i = 0; i < aplic->nirqs; i++) {
465d3916eacSRuslan Bukin 		irq = &aplic->irqs[i];
466d3916eacSRuslan Bukin 		if (irq->target_hart != hypctx->cpu_id)
467d3916eacSRuslan Bukin 			continue;
468d3916eacSRuslan Bukin 		if ((irq->state & APLIC_IRQ_STATE_ENABLED) &&
469d3916eacSRuslan Bukin 		    (irq->state & APLIC_IRQ_STATE_PENDING)) {
470d3916eacSRuslan Bukin 			mtx_unlock_spin(&aplic->mtx);
471d3916eacSRuslan Bukin 			/* Found. */
472d3916eacSRuslan Bukin 			return (1);
473d3916eacSRuslan Bukin 		}
474d3916eacSRuslan Bukin 	}
475d3916eacSRuslan Bukin 	mtx_unlock_spin(&aplic->mtx);
476d3916eacSRuslan Bukin 
477d3916eacSRuslan Bukin 	return (0);
478d3916eacSRuslan Bukin }
479d3916eacSRuslan Bukin 
480d3916eacSRuslan Bukin int
aplic_inject_irq(struct hyp * hyp,int vcpuid,uint32_t irqid,bool level)481d3916eacSRuslan Bukin aplic_inject_irq(struct hyp *hyp, int vcpuid, uint32_t irqid, bool level)
482d3916eacSRuslan Bukin {
483d3916eacSRuslan Bukin 	struct aplic_irq *irq;
484d3916eacSRuslan Bukin 	struct aplic *aplic;
485d3916eacSRuslan Bukin 	bool notify;
486d3916eacSRuslan Bukin 	int error;
487*ae65d59dSRuslan Bukin 	int mask;
488d3916eacSRuslan Bukin 
489d3916eacSRuslan Bukin 	aplic = hyp->aplic;
490d3916eacSRuslan Bukin 
491d3916eacSRuslan Bukin 	error = 0;
492d3916eacSRuslan Bukin 
493d3916eacSRuslan Bukin 	mtx_lock_spin(&aplic->mtx);
494d3916eacSRuslan Bukin 	if ((aplic->domaincfg & DOMAINCFG_IE) == 0) {
495d3916eacSRuslan Bukin 		mtx_unlock_spin(&aplic->mtx);
496d3916eacSRuslan Bukin 		return (error);
497d3916eacSRuslan Bukin 	}
498d3916eacSRuslan Bukin 
499d3916eacSRuslan Bukin 	irq = &aplic->irqs[irqid];
500d3916eacSRuslan Bukin 	if (irq->sourcecfg & SOURCECFG_D) {
501d3916eacSRuslan Bukin 		mtx_unlock_spin(&aplic->mtx);
502d3916eacSRuslan Bukin 		return (error);
503d3916eacSRuslan Bukin 	}
504d3916eacSRuslan Bukin 
505d3916eacSRuslan Bukin 	notify = false;
506d3916eacSRuslan Bukin 	switch (irq->sourcecfg & SOURCECFG_SM_M) {
507*ae65d59dSRuslan Bukin 	case SOURCECFG_SM_LEVEL0:
508*ae65d59dSRuslan Bukin 		if (!level)
509d3916eacSRuslan Bukin 			irq->state |= APLIC_IRQ_STATE_PENDING;
510*ae65d59dSRuslan Bukin 		break;
511*ae65d59dSRuslan Bukin 	case SOURCECFG_SM_LEVEL1:
512*ae65d59dSRuslan Bukin 		if (level)
513*ae65d59dSRuslan Bukin 			irq->state |= APLIC_IRQ_STATE_PENDING;
514*ae65d59dSRuslan Bukin 		break;
515*ae65d59dSRuslan Bukin 	case SOURCECFG_SM_EDGE0:
516*ae65d59dSRuslan Bukin 		if (!level && (irq->state & APLIC_IRQ_STATE_INPUT))
517*ae65d59dSRuslan Bukin 			irq->state |= APLIC_IRQ_STATE_PENDING;
518*ae65d59dSRuslan Bukin 		break;
519*ae65d59dSRuslan Bukin 	case SOURCECFG_SM_EDGE1:
520*ae65d59dSRuslan Bukin 		if (level && !(irq->state & APLIC_IRQ_STATE_INPUT))
521*ae65d59dSRuslan Bukin 			irq->state |= APLIC_IRQ_STATE_PENDING;
522d3916eacSRuslan Bukin 		break;
523d3916eacSRuslan Bukin 	case SOURCECFG_SM_DETACHED:
524*ae65d59dSRuslan Bukin 	case SOURCECFG_SM_INACTIVE:
525d3916eacSRuslan Bukin 		break;
526d3916eacSRuslan Bukin 	default:
527d3916eacSRuslan Bukin 		error = ENXIO;
528d3916eacSRuslan Bukin 		break;
529d3916eacSRuslan Bukin 	}
530*ae65d59dSRuslan Bukin 
531*ae65d59dSRuslan Bukin 	if (level)
532*ae65d59dSRuslan Bukin 		irq->state |= APLIC_IRQ_STATE_INPUT;
533*ae65d59dSRuslan Bukin 	else
534*ae65d59dSRuslan Bukin 		irq->state &= ~APLIC_IRQ_STATE_INPUT;
535*ae65d59dSRuslan Bukin 
536*ae65d59dSRuslan Bukin 	mask = APLIC_IRQ_STATE_ENABLED | APLIC_IRQ_STATE_PENDING;
537*ae65d59dSRuslan Bukin 	if ((irq->state & mask) == mask)
538*ae65d59dSRuslan Bukin 		notify = true;
539*ae65d59dSRuslan Bukin 
540d3916eacSRuslan Bukin 	mtx_unlock_spin(&aplic->mtx);
541d3916eacSRuslan Bukin 
542d3916eacSRuslan Bukin 	if (notify)
543d3916eacSRuslan Bukin 		vcpu_notify_event(vm_vcpu(hyp->vm, irq->target_hart));
544d3916eacSRuslan Bukin 
545d3916eacSRuslan Bukin 	return (error);
546d3916eacSRuslan Bukin }
547d3916eacSRuslan Bukin 
548d3916eacSRuslan Bukin int
aplic_inject_msi(struct hyp * hyp,uint64_t msg,uint64_t addr)549d3916eacSRuslan Bukin aplic_inject_msi(struct hyp *hyp, uint64_t msg, uint64_t addr)
550d3916eacSRuslan Bukin {
551d3916eacSRuslan Bukin 
552d3916eacSRuslan Bukin 	/* TODO. */
553d3916eacSRuslan Bukin 
554d3916eacSRuslan Bukin 	return (ENXIO);
555d3916eacSRuslan Bukin }
556d3916eacSRuslan Bukin 
557d3916eacSRuslan Bukin void
aplic_cpuinit(struct hypctx * hypctx)558d3916eacSRuslan Bukin aplic_cpuinit(struct hypctx *hypctx)
559d3916eacSRuslan Bukin {
560d3916eacSRuslan Bukin 
561d3916eacSRuslan Bukin }
562d3916eacSRuslan Bukin 
563d3916eacSRuslan Bukin void
aplic_cpucleanup(struct hypctx * hypctx)564d3916eacSRuslan Bukin aplic_cpucleanup(struct hypctx *hypctx)
565d3916eacSRuslan Bukin {
566d3916eacSRuslan Bukin 
567d3916eacSRuslan Bukin }
568d3916eacSRuslan Bukin 
569d3916eacSRuslan Bukin void
aplic_flush_hwstate(struct hypctx * hypctx)570d3916eacSRuslan Bukin aplic_flush_hwstate(struct hypctx *hypctx)
571d3916eacSRuslan Bukin {
572d3916eacSRuslan Bukin 
573d3916eacSRuslan Bukin }
574d3916eacSRuslan Bukin 
575d3916eacSRuslan Bukin void
aplic_sync_hwstate(struct hypctx * hypctx)576d3916eacSRuslan Bukin aplic_sync_hwstate(struct hypctx *hypctx)
577d3916eacSRuslan Bukin {
578d3916eacSRuslan Bukin 
579d3916eacSRuslan Bukin }
580d3916eacSRuslan Bukin 
581d3916eacSRuslan Bukin int
aplic_max_cpu_count(struct hyp * hyp)582d3916eacSRuslan Bukin aplic_max_cpu_count(struct hyp *hyp)
583d3916eacSRuslan Bukin {
584d3916eacSRuslan Bukin 	int16_t max_count;
585d3916eacSRuslan Bukin 
586d3916eacSRuslan Bukin 	max_count = vm_get_maxcpus(hyp->vm);
587d3916eacSRuslan Bukin 
588d3916eacSRuslan Bukin 	return (max_count);
589d3916eacSRuslan Bukin }
590