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