xref: /freebsd/sys/arm64/vmm/io/vtimer.c (revision a745cdc19b7f92b490f7c332abad82945f3b06cb)
147e07394SAndrew Turner /*-
247e07394SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
347e07394SAndrew Turner  *
447e07394SAndrew Turner  * Copyright (c) 2017 The FreeBSD Foundation
547e07394SAndrew Turner  *
647e07394SAndrew Turner  * Redistribution and use in source and binary forms, with or without
747e07394SAndrew Turner  * modification, are permitted provided that the following conditions
847e07394SAndrew Turner  * are met:
947e07394SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
1047e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
1147e07394SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
1247e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
1347e07394SAndrew Turner  *    documentation and/or other materials provided with the distribution.
1447e07394SAndrew Turner  * 3. The name of the company nor the name of the author may be used to
1547e07394SAndrew Turner  *    endorse or promote products derived from this software without specific
1647e07394SAndrew Turner  *    prior written permission.
1747e07394SAndrew Turner  *
1847e07394SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1947e07394SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2047e07394SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2147e07394SAndrew Turner  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2247e07394SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2347e07394SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2447e07394SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2547e07394SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2647e07394SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2747e07394SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2847e07394SAndrew Turner  * SUCH DAMAGE.
2947e07394SAndrew Turner  */
3047e07394SAndrew Turner 
3147e07394SAndrew Turner #include <sys/cdefs.h>
3247e07394SAndrew Turner #include <sys/types.h>
3347e07394SAndrew Turner #include <sys/systm.h>
3447e07394SAndrew Turner #include <sys/bus.h>
3547e07394SAndrew Turner #include <sys/kernel.h>
3647e07394SAndrew Turner #include <sys/module.h>
3747e07394SAndrew Turner #include <sys/mutex.h>
3847e07394SAndrew Turner #include <sys/rman.h>
3947e07394SAndrew Turner #include <sys/time.h>
4047e07394SAndrew Turner #include <sys/timeet.h>
4147e07394SAndrew Turner #include <sys/timetc.h>
4247e07394SAndrew Turner 
4347e07394SAndrew Turner #include <machine/bus.h>
4447e07394SAndrew Turner #include <machine/machdep.h>
4547e07394SAndrew Turner #include <machine/vmm.h>
4647e07394SAndrew Turner #include <machine/armreg.h>
4747e07394SAndrew Turner 
4847e07394SAndrew Turner #include <arm64/vmm/arm64.h>
4947e07394SAndrew Turner 
5047e07394SAndrew Turner #include "vgic.h"
5147e07394SAndrew Turner #include "vtimer.h"
5247e07394SAndrew Turner 
5347e07394SAndrew Turner #define	RES1		0xffffffffffffffffUL
5447e07394SAndrew Turner 
5547e07394SAndrew Turner #define timer_enabled(ctl)	\
5647e07394SAndrew Turner     (!((ctl) & CNTP_CTL_IMASK) && ((ctl) & CNTP_CTL_ENABLE))
5747e07394SAndrew Turner 
5847e07394SAndrew Turner static uint64_t cnthctl_el2_reg;
5947e07394SAndrew Turner static uint32_t tmr_frq;
6047e07394SAndrew Turner 
6147e07394SAndrew Turner #define timer_condition_met(ctl)	((ctl) & CNTP_CTL_ISTATUS)
6247e07394SAndrew Turner 
6347e07394SAndrew Turner static void vtimer_schedule_irq(struct hypctx *hypctx, bool phys);
6447e07394SAndrew Turner 
6547e07394SAndrew Turner static int
vtimer_virtual_timer_intr(void * arg)6647e07394SAndrew Turner vtimer_virtual_timer_intr(void *arg)
6747e07394SAndrew Turner {
6847e07394SAndrew Turner 	struct hypctx *hypctx;
6947e07394SAndrew Turner 	uint64_t cntpct_el0;
7047e07394SAndrew Turner 	uint32_t cntv_ctl;
7147e07394SAndrew Turner 
7247e07394SAndrew Turner 	hypctx = arm64_get_active_vcpu();
7347e07394SAndrew Turner 	cntv_ctl = READ_SPECIALREG(cntv_ctl_el0);
7447e07394SAndrew Turner 
7547e07394SAndrew Turner 	if (!hypctx) {
7647e07394SAndrew Turner 		/* vm_destroy() was called. */
7747e07394SAndrew Turner 		eprintf("No active vcpu\n");
7847e07394SAndrew Turner 		cntv_ctl = READ_SPECIALREG(cntv_ctl_el0);
7947e07394SAndrew Turner 		goto out;
8047e07394SAndrew Turner 	}
8147e07394SAndrew Turner 	if (!timer_enabled(cntv_ctl)) {
8247e07394SAndrew Turner 		eprintf("Timer not enabled\n");
8347e07394SAndrew Turner 		goto out;
8447e07394SAndrew Turner 	}
8547e07394SAndrew Turner 	if (!timer_condition_met(cntv_ctl)) {
8647e07394SAndrew Turner 		eprintf("Timer condition not met\n");
8747e07394SAndrew Turner 		goto out;
8847e07394SAndrew Turner 	}
8947e07394SAndrew Turner 
9047e07394SAndrew Turner 	cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
9147e07394SAndrew Turner 	    hypctx->hyp->vtimer.cntvoff_el2;
9247e07394SAndrew Turner 	if (hypctx->vtimer_cpu.virt_timer.cntx_cval_el0 < cntpct_el0)
9347e07394SAndrew Turner 		vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
9447e07394SAndrew Turner 		    GT_VIRT_IRQ, true);
9547e07394SAndrew Turner 
9647e07394SAndrew Turner 	cntv_ctl = hypctx->vtimer_cpu.virt_timer.cntx_ctl_el0;
9747e07394SAndrew Turner 
9847e07394SAndrew Turner out:
9947e07394SAndrew Turner 	/*
10047e07394SAndrew Turner 	 * Disable the timer interrupt. This will prevent the interrupt from
10147e07394SAndrew Turner 	 * being reasserted as soon as we exit the handler and getting stuck
10247e07394SAndrew Turner 	 * in an infinite loop.
10347e07394SAndrew Turner 	 *
10447e07394SAndrew Turner 	 * This is safe to do because the guest disabled the timer, and then
10547e07394SAndrew Turner 	 * enables it as part of the interrupt handling routine.
10647e07394SAndrew Turner 	 */
10747e07394SAndrew Turner 	cntv_ctl &= ~CNTP_CTL_ENABLE;
10847e07394SAndrew Turner 	WRITE_SPECIALREG(cntv_ctl_el0, cntv_ctl);
10947e07394SAndrew Turner 
11047e07394SAndrew Turner 	return (FILTER_HANDLED);
11147e07394SAndrew Turner }
11247e07394SAndrew Turner 
11347e07394SAndrew Turner int
vtimer_init(uint64_t cnthctl_el2)11447e07394SAndrew Turner vtimer_init(uint64_t cnthctl_el2)
11547e07394SAndrew Turner {
11647e07394SAndrew Turner 	cnthctl_el2_reg = cnthctl_el2;
11747e07394SAndrew Turner 	/*
11847e07394SAndrew Turner 	 * The guest *MUST* use the same timer frequency as the host. The
11947e07394SAndrew Turner 	 * register CNTFRQ_EL0 is accessible to the guest and a different value
12047e07394SAndrew Turner 	 * in the guest dts file might have unforseen consequences.
12147e07394SAndrew Turner 	 */
12247e07394SAndrew Turner 	tmr_frq = READ_SPECIALREG(cntfrq_el0);
12347e07394SAndrew Turner 
12447e07394SAndrew Turner 	return (0);
12547e07394SAndrew Turner }
12647e07394SAndrew Turner 
12747e07394SAndrew Turner void
vtimer_vminit(struct hyp * hyp)12847e07394SAndrew Turner vtimer_vminit(struct hyp *hyp)
12947e07394SAndrew Turner {
13047e07394SAndrew Turner 	uint64_t now;
13147e07394SAndrew Turner 
132*a745cdc1SAndrew Turner 	hyp->vtimer.cnthctl_el2 = cnthctl_el2_reg;
133*a745cdc1SAndrew Turner 
13447e07394SAndrew Turner 	/*
13547e07394SAndrew Turner 	 * Configure the Counter-timer Hypervisor Control Register for the VM.
136*a745cdc1SAndrew Turner 	 */
137*a745cdc1SAndrew Turner 	if (in_vhe()) {
138*a745cdc1SAndrew Turner 		/*
139*a745cdc1SAndrew Turner 		 * CNTHCTL_E2H_EL0PCTEN: trap EL0 access to CNTP{CT,CTSS}_EL0
140*a745cdc1SAndrew Turner 		 * CNTHCTL_E2H_EL1VCTEN: don't trap EL0 access to
141*a745cdc1SAndrew Turner 		 *                       CNTV{CT,CTSS}_EL0
142*a745cdc1SAndrew Turner 		 * CNTHCTL_E2H_EL0VTEN: don't trap EL0 access to
143*a745cdc1SAndrew Turner 		 *                      CNTV_{CTL,CVAL,TVAL}_EL0
144*a745cdc1SAndrew Turner 		 * CNTHCTL_E2H_EL0PTEN: trap EL0 access to
145*a745cdc1SAndrew Turner 		 *                      CNTP_{CTL,CVAL,TVAL}_EL0
146*a745cdc1SAndrew Turner 		 * CNTHCTL_E2H_EL1PCEN: trap EL1 access to
147*a745cdc1SAndrew Turner 		                        CNTP_{CTL,CVAL,TVAL}_EL0
148*a745cdc1SAndrew Turner 		 * CNTHCTL_E2H_EL1PCTEN: trap access to CNTPCT_EL0
14947e07394SAndrew Turner 		 *
150*a745cdc1SAndrew Turner 		 * TODO: Don't trap when FEAT_ECV is present
151*a745cdc1SAndrew Turner 		 */
152*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 &= ~CNTHCTL_E2H_EL0PCTEN;
153*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 |= CNTHCTL_E2H_EL0VCTEN;
154*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 |= CNTHCTL_E2H_EL0VTEN;
155*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 &= ~CNTHCTL_E2H_EL0PTEN;
156*a745cdc1SAndrew Turner 
157*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 &= ~CNTHCTL_E2H_EL1PTEN;
158*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 &= ~CNTHCTL_E2H_EL1PCTEN;
159*a745cdc1SAndrew Turner 	} else {
160*a745cdc1SAndrew Turner 		/*
161*a745cdc1SAndrew Turner 		 * CNTHCTL_EL1PCEN: trap access to CNTP_{CTL, CVAL, TVAL}_EL0
162*a745cdc1SAndrew Turner 		 *                  from EL1
16347e07394SAndrew Turner 		 * CNTHCTL_EL1PCTEN: trap access to CNTPCT_EL0
16447e07394SAndrew Turner 		 */
165*a745cdc1SAndrew Turner 		hyp->vtimer.cnthctl_el2 &= ~CNTHCTL_EL1PCEN;
16647e07394SAndrew Turner 		hyp->vtimer.cnthctl_el2 &= ~CNTHCTL_EL1PCTEN;
167*a745cdc1SAndrew Turner 	}
16847e07394SAndrew Turner 
16947e07394SAndrew Turner 	now = READ_SPECIALREG(cntpct_el0);
17047e07394SAndrew Turner 	hyp->vtimer.cntvoff_el2 = now;
17147e07394SAndrew Turner 
17247e07394SAndrew Turner 	return;
17347e07394SAndrew Turner }
17447e07394SAndrew Turner 
17547e07394SAndrew Turner void
vtimer_cpuinit(struct hypctx * hypctx)17647e07394SAndrew Turner vtimer_cpuinit(struct hypctx *hypctx)
17747e07394SAndrew Turner {
17847e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
17947e07394SAndrew Turner 
18047e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
18147e07394SAndrew Turner 	/*
18247e07394SAndrew Turner 	 * Configure physical timer interrupts for the VCPU.
18347e07394SAndrew Turner 	 *
18447e07394SAndrew Turner 	 * CNTP_CTL_IMASK: mask interrupts
18547e07394SAndrew Turner 	 * ~CNTP_CTL_ENABLE: disable the timer
18647e07394SAndrew Turner 	 */
18747e07394SAndrew Turner 	vtimer_cpu->phys_timer.cntx_ctl_el0 = CNTP_CTL_IMASK & ~CNTP_CTL_ENABLE;
18847e07394SAndrew Turner 
18947e07394SAndrew Turner 	mtx_init(&vtimer_cpu->phys_timer.mtx, "vtimer phys callout mutex", NULL,
19047e07394SAndrew Turner 	    MTX_DEF);
19147e07394SAndrew Turner 	callout_init_mtx(&vtimer_cpu->phys_timer.callout,
19247e07394SAndrew Turner 	    &vtimer_cpu->phys_timer.mtx, 0);
19347e07394SAndrew Turner 	vtimer_cpu->phys_timer.irqid = GT_PHYS_NS_IRQ;
19447e07394SAndrew Turner 
19547e07394SAndrew Turner 	mtx_init(&vtimer_cpu->virt_timer.mtx, "vtimer virt callout mutex", NULL,
19647e07394SAndrew Turner 	    MTX_DEF);
19747e07394SAndrew Turner 	callout_init_mtx(&vtimer_cpu->virt_timer.callout,
19847e07394SAndrew Turner 	    &vtimer_cpu->virt_timer.mtx, 0);
19947e07394SAndrew Turner 	vtimer_cpu->virt_timer.irqid = GT_VIRT_IRQ;
20047e07394SAndrew Turner }
20147e07394SAndrew Turner 
20247e07394SAndrew Turner void
vtimer_cpucleanup(struct hypctx * hypctx)20347e07394SAndrew Turner vtimer_cpucleanup(struct hypctx *hypctx)
20447e07394SAndrew Turner {
20547e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
20647e07394SAndrew Turner 
20747e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
20847e07394SAndrew Turner 	callout_drain(&vtimer_cpu->phys_timer.callout);
20947e07394SAndrew Turner 	callout_drain(&vtimer_cpu->virt_timer.callout);
21047e07394SAndrew Turner 	mtx_destroy(&vtimer_cpu->phys_timer.mtx);
21147e07394SAndrew Turner 	mtx_destroy(&vtimer_cpu->virt_timer.mtx);
21247e07394SAndrew Turner }
21347e07394SAndrew Turner 
21447e07394SAndrew Turner void
vtimer_vmcleanup(struct hyp * hyp)21547e07394SAndrew Turner vtimer_vmcleanup(struct hyp *hyp)
21647e07394SAndrew Turner {
21747e07394SAndrew Turner 	struct hypctx *hypctx;
21847e07394SAndrew Turner 	uint32_t cntv_ctl;
21947e07394SAndrew Turner 
22047e07394SAndrew Turner 	hypctx = arm64_get_active_vcpu();
22147e07394SAndrew Turner 	if (!hypctx) {
22247e07394SAndrew Turner 		/* The active VM was destroyed, stop the timer. */
22347e07394SAndrew Turner 		cntv_ctl = READ_SPECIALREG(cntv_ctl_el0);
22447e07394SAndrew Turner 		cntv_ctl &= ~CNTP_CTL_ENABLE;
22547e07394SAndrew Turner 		WRITE_SPECIALREG(cntv_ctl_el0, cntv_ctl);
22647e07394SAndrew Turner 	}
22747e07394SAndrew Turner }
22847e07394SAndrew Turner 
22947e07394SAndrew Turner void
vtimer_cleanup(void)23047e07394SAndrew Turner vtimer_cleanup(void)
23147e07394SAndrew Turner {
23247e07394SAndrew Turner }
23347e07394SAndrew Turner 
23447e07394SAndrew Turner void
vtimer_sync_hwstate(struct hypctx * hypctx)23547e07394SAndrew Turner vtimer_sync_hwstate(struct hypctx *hypctx)
23647e07394SAndrew Turner {
23747e07394SAndrew Turner 	struct vtimer_timer *timer;
23847e07394SAndrew Turner 	uint64_t cntpct_el0;
23947e07394SAndrew Turner 
24047e07394SAndrew Turner 	timer = &hypctx->vtimer_cpu.virt_timer;
24147e07394SAndrew Turner 	cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
24247e07394SAndrew Turner 	    hypctx->hyp->vtimer.cntvoff_el2;
24347e07394SAndrew Turner 	if (!timer_enabled(timer->cntx_ctl_el0)) {
24447e07394SAndrew Turner 		vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
24547e07394SAndrew Turner 		    timer->irqid, false);
24647e07394SAndrew Turner 	} else if (timer->cntx_cval_el0 < cntpct_el0) {
24747e07394SAndrew Turner 		vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
24847e07394SAndrew Turner 		    timer->irqid, true);
24947e07394SAndrew Turner 	} else {
25047e07394SAndrew Turner 		vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
25147e07394SAndrew Turner 		    timer->irqid, false);
25247e07394SAndrew Turner 		vtimer_schedule_irq(hypctx, false);
25347e07394SAndrew Turner 	}
25447e07394SAndrew Turner }
25547e07394SAndrew Turner 
25647e07394SAndrew Turner static void
vtimer_inject_irq_callout_phys(void * context)25747e07394SAndrew Turner vtimer_inject_irq_callout_phys(void *context)
25847e07394SAndrew Turner {
25947e07394SAndrew Turner 	struct hypctx *hypctx;
26047e07394SAndrew Turner 
26147e07394SAndrew Turner 	hypctx = context;
26247e07394SAndrew Turner 	vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
26347e07394SAndrew Turner 	    hypctx->vtimer_cpu.phys_timer.irqid, true);
26447e07394SAndrew Turner }
26547e07394SAndrew Turner 
26647e07394SAndrew Turner static void
vtimer_inject_irq_callout_virt(void * context)26747e07394SAndrew Turner vtimer_inject_irq_callout_virt(void *context)
26847e07394SAndrew Turner {
26947e07394SAndrew Turner 	struct hypctx *hypctx;
27047e07394SAndrew Turner 
27147e07394SAndrew Turner 	hypctx = context;
27247e07394SAndrew Turner 	vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
27347e07394SAndrew Turner 	    hypctx->vtimer_cpu.virt_timer.irqid, true);
27447e07394SAndrew Turner }
27547e07394SAndrew Turner 
27647e07394SAndrew Turner static void
vtimer_schedule_irq(struct hypctx * hypctx,bool phys)27747e07394SAndrew Turner vtimer_schedule_irq(struct hypctx *hypctx, bool phys)
27847e07394SAndrew Turner {
27947e07394SAndrew Turner 	sbintime_t time;
28047e07394SAndrew Turner 	struct vtimer_timer *timer;
28147e07394SAndrew Turner 	uint64_t cntpct_el0;
28247e07394SAndrew Turner 	uint64_t diff;
28347e07394SAndrew Turner 
28447e07394SAndrew Turner 	if (phys)
28547e07394SAndrew Turner 		timer = &hypctx->vtimer_cpu.phys_timer;
28647e07394SAndrew Turner 	else
28747e07394SAndrew Turner 		timer = &hypctx->vtimer_cpu.virt_timer;
28847e07394SAndrew Turner 	cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
28947e07394SAndrew Turner 	    hypctx->hyp->vtimer.cntvoff_el2;
29047e07394SAndrew Turner 	if (timer->cntx_cval_el0 < cntpct_el0) {
29147e07394SAndrew Turner 		/* Timer set in the past, trigger interrupt */
29247e07394SAndrew Turner 		vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
29347e07394SAndrew Turner 		    timer->irqid, true);
29447e07394SAndrew Turner 	} else {
29547e07394SAndrew Turner 		diff = timer->cntx_cval_el0 - cntpct_el0;
29647e07394SAndrew Turner 		time = diff * SBT_1S / tmr_frq;
29747e07394SAndrew Turner 		if (phys)
29847e07394SAndrew Turner 			callout_reset_sbt(&timer->callout, time, 0,
29947e07394SAndrew Turner 			    vtimer_inject_irq_callout_phys, hypctx, 0);
30047e07394SAndrew Turner 		else
30147e07394SAndrew Turner 			callout_reset_sbt(&timer->callout, time, 0,
30247e07394SAndrew Turner 			    vtimer_inject_irq_callout_virt, hypctx, 0);
30347e07394SAndrew Turner 	}
30447e07394SAndrew Turner }
30547e07394SAndrew Turner 
30647e07394SAndrew Turner static void
vtimer_remove_irq(struct hypctx * hypctx,struct vcpu * vcpu)30747e07394SAndrew Turner vtimer_remove_irq(struct hypctx *hypctx, struct vcpu *vcpu)
30847e07394SAndrew Turner {
30947e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
31047e07394SAndrew Turner 	struct vtimer_timer *timer;
31147e07394SAndrew Turner 
31247e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
31347e07394SAndrew Turner 	timer = &vtimer_cpu->phys_timer;
31447e07394SAndrew Turner 
31547e07394SAndrew Turner 	callout_drain(&timer->callout);
31647e07394SAndrew Turner 	/*
31747e07394SAndrew Turner 	 * The interrupt needs to be deactivated here regardless of the callout
31847e07394SAndrew Turner 	 * function having been executed. The timer interrupt can be masked with
31947e07394SAndrew Turner 	 * the CNTP_CTL_EL0.IMASK bit instead of reading the IAR register.
32047e07394SAndrew Turner 	 * Masking the interrupt doesn't remove it from the list registers.
32147e07394SAndrew Turner 	 */
32247e07394SAndrew Turner 	vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(vcpu), timer->irqid, false);
32347e07394SAndrew Turner }
32447e07394SAndrew Turner 
32547e07394SAndrew Turner /*
32647e07394SAndrew Turner  * Timer emulation functions.
32747e07394SAndrew Turner  *
32847e07394SAndrew Turner  * The guest should use the virtual timer, however some software, e.g. u-boot,
32947e07394SAndrew Turner  * used the physical timer. Emulate this in software for the guest to use.
33047e07394SAndrew Turner  *
33147e07394SAndrew Turner  * Adjust for cntvoff_el2 so the physical and virtual timers are at similar
33247e07394SAndrew Turner  * times. This simplifies interrupt handling in the virtual timer as the
33347e07394SAndrew Turner  * adjustment will have already happened.
33447e07394SAndrew Turner  */
33547e07394SAndrew Turner 
33647e07394SAndrew Turner int
vtimer_phys_ctl_read(struct vcpu * vcpu,uint64_t * rval,void * arg)33747e07394SAndrew Turner vtimer_phys_ctl_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
33847e07394SAndrew Turner {
33947e07394SAndrew Turner 	struct hyp *hyp;
34047e07394SAndrew Turner 	struct hypctx *hypctx;
34147e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
34247e07394SAndrew Turner 	uint64_t cntpct_el0;
34347e07394SAndrew Turner 
34447e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
34547e07394SAndrew Turner 	hyp = hypctx->hyp;
34647e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
34747e07394SAndrew Turner 
34847e07394SAndrew Turner 	cntpct_el0 = READ_SPECIALREG(cntpct_el0) - hyp->vtimer.cntvoff_el2;
34947e07394SAndrew Turner 	if (vtimer_cpu->phys_timer.cntx_cval_el0 < cntpct_el0)
35047e07394SAndrew Turner 		/* Timer condition met */
35147e07394SAndrew Turner 		*rval = vtimer_cpu->phys_timer.cntx_ctl_el0 | CNTP_CTL_ISTATUS;
35247e07394SAndrew Turner 	else
35347e07394SAndrew Turner 		*rval = vtimer_cpu->phys_timer.cntx_ctl_el0 & ~CNTP_CTL_ISTATUS;
35447e07394SAndrew Turner 
35547e07394SAndrew Turner 	return (0);
35647e07394SAndrew Turner }
35747e07394SAndrew Turner 
35847e07394SAndrew Turner int
vtimer_phys_ctl_write(struct vcpu * vcpu,uint64_t wval,void * arg)35947e07394SAndrew Turner vtimer_phys_ctl_write(struct vcpu *vcpu, uint64_t wval, void *arg)
36047e07394SAndrew Turner {
36147e07394SAndrew Turner 	struct hypctx *hypctx;
36247e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
36347e07394SAndrew Turner 	uint64_t ctl_el0;
36447e07394SAndrew Turner 	bool timer_toggled_on;
36547e07394SAndrew Turner 
36647e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
36747e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
36847e07394SAndrew Turner 
36947e07394SAndrew Turner 	timer_toggled_on = false;
37047e07394SAndrew Turner 	ctl_el0 = vtimer_cpu->phys_timer.cntx_ctl_el0;
37147e07394SAndrew Turner 
37247e07394SAndrew Turner 	if (!timer_enabled(ctl_el0) && timer_enabled(wval))
37347e07394SAndrew Turner 		timer_toggled_on = true;
37447e07394SAndrew Turner 	else if (timer_enabled(ctl_el0) && !timer_enabled(wval))
37547e07394SAndrew Turner 		vtimer_remove_irq(hypctx, vcpu);
37647e07394SAndrew Turner 
37747e07394SAndrew Turner 	vtimer_cpu->phys_timer.cntx_ctl_el0 = wval;
37847e07394SAndrew Turner 
37947e07394SAndrew Turner 	if (timer_toggled_on)
38047e07394SAndrew Turner 		vtimer_schedule_irq(hypctx, true);
38147e07394SAndrew Turner 
38247e07394SAndrew Turner 	return (0);
38347e07394SAndrew Turner }
38447e07394SAndrew Turner 
38547e07394SAndrew Turner int
vtimer_phys_cnt_read(struct vcpu * vcpu,uint64_t * rval,void * arg)38647e07394SAndrew Turner vtimer_phys_cnt_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
38747e07394SAndrew Turner {
38847e07394SAndrew Turner 	struct vm *vm;
38947e07394SAndrew Turner 	struct hyp *hyp;
39047e07394SAndrew Turner 
39147e07394SAndrew Turner 	vm = vcpu_vm(vcpu);
39247e07394SAndrew Turner 	hyp = vm_get_cookie(vm);
39347e07394SAndrew Turner 	*rval = READ_SPECIALREG(cntpct_el0) - hyp->vtimer.cntvoff_el2;
39447e07394SAndrew Turner 	return (0);
39547e07394SAndrew Turner }
39647e07394SAndrew Turner 
39747e07394SAndrew Turner int
vtimer_phys_cnt_write(struct vcpu * vcpu,uint64_t wval,void * arg)39847e07394SAndrew Turner vtimer_phys_cnt_write(struct vcpu *vcpu, uint64_t wval, void *arg)
39947e07394SAndrew Turner {
40047e07394SAndrew Turner 	return (0);
40147e07394SAndrew Turner }
40247e07394SAndrew Turner 
40347e07394SAndrew Turner int
vtimer_phys_cval_read(struct vcpu * vcpu,uint64_t * rval,void * arg)40447e07394SAndrew Turner vtimer_phys_cval_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
40547e07394SAndrew Turner {
40647e07394SAndrew Turner 	struct hypctx *hypctx;
40747e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
40847e07394SAndrew Turner 
40947e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
41047e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
41147e07394SAndrew Turner 
41247e07394SAndrew Turner 	*rval = vtimer_cpu->phys_timer.cntx_cval_el0;
41347e07394SAndrew Turner 
41447e07394SAndrew Turner 	return (0);
41547e07394SAndrew Turner }
41647e07394SAndrew Turner 
41747e07394SAndrew Turner int
vtimer_phys_cval_write(struct vcpu * vcpu,uint64_t wval,void * arg)41847e07394SAndrew Turner vtimer_phys_cval_write(struct vcpu *vcpu, uint64_t wval, void *arg)
41947e07394SAndrew Turner {
42047e07394SAndrew Turner 	struct hypctx *hypctx;
42147e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
42247e07394SAndrew Turner 
42347e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
42447e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
42547e07394SAndrew Turner 
42647e07394SAndrew Turner 	vtimer_cpu->phys_timer.cntx_cval_el0 = wval;
42747e07394SAndrew Turner 
42847e07394SAndrew Turner 	vtimer_remove_irq(hypctx, vcpu);
42947e07394SAndrew Turner 	if (timer_enabled(vtimer_cpu->phys_timer.cntx_ctl_el0)) {
43047e07394SAndrew Turner 		vtimer_schedule_irq(hypctx, true);
43147e07394SAndrew Turner 	}
43247e07394SAndrew Turner 
43347e07394SAndrew Turner 	return (0);
43447e07394SAndrew Turner }
43547e07394SAndrew Turner 
43647e07394SAndrew Turner int
vtimer_phys_tval_read(struct vcpu * vcpu,uint64_t * rval,void * arg)43747e07394SAndrew Turner vtimer_phys_tval_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
43847e07394SAndrew Turner {
43947e07394SAndrew Turner 	struct hyp *hyp;
44047e07394SAndrew Turner 	struct hypctx *hypctx;
44147e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
44247e07394SAndrew Turner 	uint32_t cntpct_el0;
44347e07394SAndrew Turner 
44447e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
44547e07394SAndrew Turner 	hyp = hypctx->hyp;
44647e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
44747e07394SAndrew Turner 
44847e07394SAndrew Turner 	if (!(vtimer_cpu->phys_timer.cntx_ctl_el0 & CNTP_CTL_ENABLE)) {
44947e07394SAndrew Turner 		/*
45047e07394SAndrew Turner 		 * ARMv8 Architecture Manual, p. D7-2702: the result of reading
45147e07394SAndrew Turner 		 * TVAL when the timer is disabled is UNKNOWN. I have chosen to
45247e07394SAndrew Turner 		 * return the maximum value possible on 32 bits which means the
45347e07394SAndrew Turner 		 * timer will fire very far into the future.
45447e07394SAndrew Turner 		 */
45547e07394SAndrew Turner 		*rval = (uint32_t)RES1;
45647e07394SAndrew Turner 	} else {
45747e07394SAndrew Turner 		cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
45847e07394SAndrew Turner 		    hyp->vtimer.cntvoff_el2;
45947e07394SAndrew Turner 		*rval = vtimer_cpu->phys_timer.cntx_cval_el0 - cntpct_el0;
46047e07394SAndrew Turner 	}
46147e07394SAndrew Turner 
46247e07394SAndrew Turner 	return (0);
46347e07394SAndrew Turner }
46447e07394SAndrew Turner 
46547e07394SAndrew Turner int
vtimer_phys_tval_write(struct vcpu * vcpu,uint64_t wval,void * arg)46647e07394SAndrew Turner vtimer_phys_tval_write(struct vcpu *vcpu, uint64_t wval, void *arg)
46747e07394SAndrew Turner {
46847e07394SAndrew Turner 	struct hyp *hyp;
46947e07394SAndrew Turner 	struct hypctx *hypctx;
47047e07394SAndrew Turner 	struct vtimer_cpu *vtimer_cpu;
47147e07394SAndrew Turner 	uint64_t cntpct_el0;
47247e07394SAndrew Turner 
47347e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
47447e07394SAndrew Turner 	hyp = hypctx->hyp;
47547e07394SAndrew Turner 	vtimer_cpu = &hypctx->vtimer_cpu;
47647e07394SAndrew Turner 
47747e07394SAndrew Turner 	cntpct_el0 = READ_SPECIALREG(cntpct_el0) - hyp->vtimer.cntvoff_el2;
47847e07394SAndrew Turner 	vtimer_cpu->phys_timer.cntx_cval_el0 = (int32_t)wval + cntpct_el0;
47947e07394SAndrew Turner 
48047e07394SAndrew Turner 	vtimer_remove_irq(hypctx, vcpu);
48147e07394SAndrew Turner 	if (timer_enabled(vtimer_cpu->phys_timer.cntx_ctl_el0)) {
48247e07394SAndrew Turner 		vtimer_schedule_irq(hypctx, true);
48347e07394SAndrew Turner 	}
48447e07394SAndrew Turner 
48547e07394SAndrew Turner 	return (0);
48647e07394SAndrew Turner }
48747e07394SAndrew Turner 
48847e07394SAndrew Turner struct vtimer_softc {
48947e07394SAndrew Turner 	struct resource *res;
49047e07394SAndrew Turner 	void *ihl;
49147e07394SAndrew Turner 	int rid;
49247e07394SAndrew Turner };
49347e07394SAndrew Turner 
49447e07394SAndrew Turner static int
vtimer_probe(device_t dev)49547e07394SAndrew Turner vtimer_probe(device_t dev)
49647e07394SAndrew Turner {
49747e07394SAndrew Turner 	device_set_desc(dev, "Virtual timer");
49847e07394SAndrew Turner 	return (BUS_PROBE_DEFAULT);
49947e07394SAndrew Turner }
50047e07394SAndrew Turner 
50147e07394SAndrew Turner static int
vtimer_attach(device_t dev)50247e07394SAndrew Turner vtimer_attach(device_t dev)
50347e07394SAndrew Turner {
50447e07394SAndrew Turner 	struct vtimer_softc *sc;
50547e07394SAndrew Turner 
50647e07394SAndrew Turner 	sc = device_get_softc(dev);
50747e07394SAndrew Turner 
50847e07394SAndrew Turner 	sc->rid = 0;
50947e07394SAndrew Turner 	sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid, RF_ACTIVE);
51047e07394SAndrew Turner 	if (sc->res == NULL)
51147e07394SAndrew Turner 		return (ENXIO);
51247e07394SAndrew Turner 
51347e07394SAndrew Turner 	bus_setup_intr(dev, sc->res, INTR_TYPE_CLK, vtimer_virtual_timer_intr,
51447e07394SAndrew Turner 	    NULL, NULL, &sc->ihl);
51547e07394SAndrew Turner 
51647e07394SAndrew Turner 	return (0);
51747e07394SAndrew Turner }
51847e07394SAndrew Turner 
51947e07394SAndrew Turner static device_method_t vtimer_methods[] = {
52047e07394SAndrew Turner 	/* Device interface */
52147e07394SAndrew Turner 	DEVMETHOD(device_probe,		vtimer_probe),
52247e07394SAndrew Turner 	DEVMETHOD(device_attach,	vtimer_attach),
52347e07394SAndrew Turner 
52447e07394SAndrew Turner 	/* End */
52547e07394SAndrew Turner 	DEVMETHOD_END
52647e07394SAndrew Turner };
52747e07394SAndrew Turner 
52847e07394SAndrew Turner DEFINE_CLASS_0(vtimer, vtimer_driver, vtimer_methods,
52947e07394SAndrew Turner     sizeof(struct vtimer_softc));
53047e07394SAndrew Turner 
53147e07394SAndrew Turner DRIVER_MODULE(vtimer, generic_timer, vtimer_driver, 0, 0);
532