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