Index: sys/conf/files.riscv =================================================================== --- sys/conf/files.riscv +++ sys/conf/files.riscv @@ -83,6 +83,7 @@ riscv/vmm/vmm_riscv.c optional vmm riscv/vmm/vmm_sbi.c optional vmm riscv/vmm/vmm_switch.S optional vmm +riscv/vmm/vmm_vtimer.c optional vmm riscv/thead/thead.c standard Index: sys/modules/vmm/Makefile =================================================================== --- sys/modules/vmm/Makefile +++ sys/modules/vmm/Makefile @@ -154,7 +154,8 @@ SRCS+= vmm_aplic.c \ vmm_riscv.c \ vmm_sbi.c \ - vmm_switch.S + vmm_switch.S \ + vmm_vtimer.c .endif Index: sys/riscv/vmm/riscv.h =================================================================== --- sys/riscv/vmm/riscv.h +++ sys/riscv/vmm/riscv.h @@ -38,6 +38,8 @@ #include #include +#include + struct hypregs { uint64_t hyp_ra; uint64_t hyp_sp; @@ -78,6 +80,8 @@ bool has_exception; int cpu_id; int ipi_pending; + int interrupts_pending; + struct vtimer vtimer; }; struct hyp { @@ -128,5 +132,6 @@ void riscv_send_ipi(struct hypctx *hypctx, int hart_id); int riscv_check_ipi(struct hypctx *hypctx, bool clear); +bool riscv_check_interrupts_pending(struct hypctx *hypctx); #endif /* !_VMM_RISCV_H_ */ Index: sys/riscv/vmm/vmm.c =================================================================== --- sys/riscv/vmm/vmm.c +++ sys/riscv/vmm/vmm.c @@ -1414,6 +1414,9 @@ if (riscv_check_ipi(vcpu->cookie, false)) break; + if (riscv_check_interrupts_pending(vcpu->cookie)) + break; + if (vcpu_should_yield(vcpu)) break; Index: sys/riscv/vmm/vmm_riscv.c =================================================================== --- sys/riscv/vmm/vmm_riscv.c +++ sys/riscv/vmm/vmm_riscv.c @@ -107,11 +107,6 @@ return (ENXIO); } - if (!has_sstc) { - printf("vmm: riscv hart doesn't support SSTC extension.\n"); - return (ENXIO); - } - return (0); } @@ -229,6 +224,7 @@ hyp->ctx[vcpuid] = hypctx; aplic_cpuinit(hypctx); + vtimer_cpuinit(hypctx); return (hypctx); } @@ -561,29 +557,36 @@ return (val); } +bool +riscv_check_interrupts_pending(struct hypctx *hypctx) +{ + + if (hypctx->interrupts_pending) + return (true); + + return (false); +} + static void riscv_sync_interrupts(struct hypctx *hypctx) { int pending; pending = aplic_check_pending(hypctx); - if (pending) hypctx->guest_csrs.hvip |= HVIP_VSEIP; else hypctx->guest_csrs.hvip &= ~HVIP_VSEIP; - csr_write(hvip, hypctx->guest_csrs.hvip); -} - -static void -riscv_sync_ipi(struct hypctx *hypctx) -{ - /* Guest clears VSSIP bit manually. */ if (riscv_check_ipi(hypctx, true)) hypctx->guest_csrs.hvip |= HVIP_VSSIP; + if (riscv_check_interrupts_pending(hypctx)) + hypctx->guest_csrs.hvip |= HVIP_VSTIP; + else + hypctx->guest_csrs.hvip &= ~HVIP_VSTIP; + csr_write(hvip, hypctx->guest_csrs.hvip); } @@ -594,6 +597,7 @@ struct vm_exit *vme; struct vcpu *vcpu; register_t val; + uint64_t hvip; bool handled; hypctx = (struct hypctx *)vcpui; @@ -615,7 +619,8 @@ __asm __volatile("hfence.gvma" ::: "memory"); csr_write(hgatp, pmap->pm_satp); - csr_write(henvcfg, HENVCFG_STCE); + if (has_sstc) + csr_write(henvcfg, HENVCFG_STCE); csr_write(hie, HIE_VSEIE | HIE_VSSIE | HIE_SGEIE); /* TODO: should we trap rdcycle / rdtime? */ csr_write(hcounteren, HCOUNTEREN_CY | HCOUNTEREN_TM); @@ -653,9 +658,7 @@ */ riscv_set_active_vcpu(hypctx); aplic_flush_hwstate(hypctx); - riscv_sync_interrupts(hypctx); - riscv_sync_ipi(hypctx); dprintf("%s: Entering guest VM, vsatp %lx, ss %lx hs %lx\n", __func__, csr_read(vsatp), hypctx->guest_regs.hyp_sstatus, @@ -666,8 +669,18 @@ dprintf("%s: Leaving guest VM, hstatus %lx\n", __func__, hypctx->guest_regs.hyp_hstatus); + /* Guest can clear VSSIP. It can't clear VSTIP or VSEIP. */ + hvip = csr_read(hvip); + if ((hypctx->guest_csrs.hvip ^ hvip) & HVIP_VSSIP) { + if (hvip & HVIP_VSSIP) { + /* TODO: VSSIP was set by guest. */ + } else { + /* VSSIP was cleared by guest. */ + hypctx->guest_csrs.hvip &= ~HVIP_VSSIP; + } + } + aplic_sync_hwstate(hypctx); - riscv_sync_interrupts(hypctx); /* * TODO: deactivate stage 2 pmap here if needed. Index: sys/riscv/vmm/vmm_sbi.c =================================================================== --- sys/riscv/vmm/vmm_sbi.c +++ sys/riscv/vmm/vmm_sbi.c @@ -95,6 +95,31 @@ return (0); } +static int +vmm_sbi_handle_time(struct vcpu *vcpu, struct hypctx *hypctx) +{ + uint64_t func_id; + uint64_t next_val; + int ret; + + func_id = hypctx->guest_regs.hyp_a[6]; + next_val = hypctx->guest_regs.hyp_a[0]; + + switch (func_id) { + case SBI_TIME_SET_TIMER: + vtimer_set_timer(hypctx, next_val); + ret = 0; + break; + default: + ret = -1; + break; + } + + hypctx->guest_regs.hyp_a[0] = ret; + + return (0); +} + static int vmm_sbi_handle_ipi(struct vcpu *vcpu, struct hypctx *hypctx) { @@ -166,6 +191,7 @@ vmm_sbi_handle_rfnc(vcpu, hypctx); break; case SBI_EXT_ID_TIME: + vmm_sbi_handle_time(vcpu, hypctx); break; case SBI_EXT_ID_IPI: vmm_sbi_handle_ipi(vcpu, hypctx); Index: sys/riscv/vmm/vmm_vtimer.h =================================================================== --- /dev/null +++ sys/riscv/vmm/vmm_vtimer.h @@ -0,0 +1,47 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Ruslan Bukin + * + * This software was developed by the University of Cambridge Computer + * Laboratory (Department of Computer Science and Technology) under Innovate + * UK project 105694, "Digital Security by Design (DSbD) Technology Platform + * Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _VMM_VTIMER_H_ +#define _VMM_VTIMER_H_ + +struct hypctx; + +struct vtimer { + struct callout callout; + struct mtx mtx; + uint32_t freq; +}; + +void vtimer_cpuinit(struct hypctx *hypctx); +int vtimer_set_timer(struct hypctx *hypctx, uint64_t next_val); + +#endif /* !_VMM_VTIMER_H_ */ Index: sys/riscv/vmm/vmm_vtimer.c =================================================================== --- /dev/null +++ sys/riscv/vmm/vmm_vtimer.c @@ -0,0 +1,117 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Ruslan Bukin + * + * This software was developed by the University of Cambridge Computer + * Laboratory (Department of Computer Science and Technology) under Innovate + * UK project 105694, "Digital Security by Design (DSbD) Technology Platform + * Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "riscv.h" + +#define VTIMER_DEFAULT_FREQ 1000000 + +static int +vtimer_get_timebase(uint32_t *freq) +{ + phandle_t node; + int len; + + node = OF_finddevice("/cpus"); + if (node == -1) + return (ENXIO); + + len = OF_getproplen(node, "timebase-frequency"); + if (len != 4) + return (ENXIO); + + OF_getencprop(node, "timebase-frequency", freq, len); + + return (0); +} + +void +vtimer_cpuinit(struct hypctx *hypctx) +{ + struct vtimer *vtimer; + uint32_t freq; + int error; + + vtimer = &hypctx->vtimer; + mtx_init(&vtimer->mtx, "vtimer callout mutex", NULL, MTX_DEF); + callout_init_mtx(&vtimer->callout, &vtimer->mtx, 0); + + error = vtimer_get_timebase(&freq); + if (error) + freq = VTIMER_DEFAULT_FREQ; + + vtimer->freq = freq; +} + +static void +vtimer_inject_irq_callout(void *arg) +{ + struct hypctx *hypctx; + struct hyp *hyp; + + hypctx = arg; + hyp = hypctx->hyp; + + atomic_set_32(&hypctx->interrupts_pending, HVIP_VSTIP); + vcpu_notify_event(vm_vcpu(hyp->vm, hypctx->cpu_id)); +} + +int +vtimer_set_timer(struct hypctx *hypctx, uint64_t next_val) +{ + struct vtimer *vtimer; + sbintime_t time; + uint64_t curtime; + uint64_t delta; + + vtimer = &hypctx->vtimer; + + curtime = rdtime(); + if (curtime < next_val) { + delta = next_val - curtime; + time = delta * SBT_1S / vtimer->freq; + atomic_clear_32(&hypctx->interrupts_pending, HVIP_VSTIP); + callout_reset_sbt(&vtimer->callout, time, 0, + vtimer_inject_irq_callout, hypctx, 0); + } else + atomic_set_32(&hypctx->interrupts_pending, HVIP_VSTIP); + + return (0); +} Index: usr.sbin/bhyve/riscv/fdt.c =================================================================== --- usr.sbin/bhyve/riscv/fdt.c +++ usr.sbin/bhyve/riscv/fdt.c @@ -118,7 +118,8 @@ /* XXX: Needed given the root #address-cells? */ fdt_property_u32(fdt, "#address-cells", 1); fdt_property_u32(fdt, "#size-cells", 0); - fdt_property_u32(fdt, "timebase-frequency", 10000000); + /* TODO: take timebase from kernel? */ + fdt_property_u32(fdt, "timebase-frequency", 1000000); for (cpuid = 0; cpuid < ncpu; cpuid++) add_cpu(fdt, cpuid, isa);