Files
acrn-hypervisor/hypervisor/arch/riscv/guest/vm.c
Haoyu Tang 730f11e4c9 hv: riscv initialize VM time offset and vcpu timer
Initialize the VM's time_delta to provide a consistent time offset for
guests, and properly initialize vcpu timer-related CSRs (htimedelta and
vstimecmp) to enable guest timer virtualization.

Changes:
- Add time_delta field to vm_arch structure to track VM time offset
- Initialize time_delta to -cpu_ticks() at VM creation, making
  guest time start from 0
- Save/restore htimedelta CSR in vcpu load/unload paths
- Initialize vcpu's htimedelta from VM's time_delta
- Reset vstimecmp to invalid value (0xffffffffffffffff) on vcpu reset
- Add SSTC (Sstc extension) capability check

Tracked-On: #8851
Signed-off-by: Haoyu Tang <haoyu.tang@intel.com>
Acked-by: Wang Yu1 <yu1.wang@intel.com>
2025-12-26 07:57:51 +08:00

135 lines
2.9 KiB
C

/*
* Copyright (C) 2023-2025 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Author: Yifan Liu <yifan1.liu@intel.com>
* Haicheng Li <haicheng.li@intel.com>
*/
#include <vm.h>
#include <vcpu.h>
#include <fdt.h>
#include <vcpu.h>
#include <reloc.h>
#include <cpu.h>
#include <per_cpu.h>
#include <vfdt.h>
#include <libfdt.h>
#include <asm/guest/vcpu_priv.h>
static void riscv_vm_time_delta_init(struct acrn_vm *vm)
{
/* FIXME: move SSTC checking to platform caps */
ASSERT(!!(cpu_csr_read(CSR_HENVCFG) & ENVCFG_STCE), "SSTC must be enabled");
/* initialize VM startup time to 0 */
vm->arch_vm.time_delta = -cpu_ticks();
}
uint32_t vcpu_get_vhartid(struct acrn_vcpu *vcpu)
{
uint32_t vhartid = vcpu->vcpu_id;
if (is_service_vm(vcpu->vm)) {
/* vhartid == phartid */
vhartid = per_cpu(arch.hart_id, pcpuid_from_vcpu(vcpu));
}
/* RISC-V requires that at least one hart has hart ID 0,
* so we use logical ID (vcpu_id) for non-service vms.
*/
return vhartid;
}
struct acrn_vcpu *vcpu_from_vhartid(struct acrn_vm *vm, uint32_t vhartid)
{
struct acrn_vcpu *vcpu;
if (is_service_vm(vm)) {
/* vhartid == phartid */
vcpu = vcpu_from_pid(vm, get_pcpu_id_from_hart_id(vhartid));
} else {
/* vhartid == vcpu_id */
vcpu = vcpu_from_vid(vm, vhartid);
}
return vcpu;
}
int32_t arch_init_vm(struct acrn_vm *vm, struct acrn_vm_config *vm_config)
{
init_vsbi(vm);
(void)vm_config;
if (is_service_vm(vm)) {
init_service_vm_vfdt(vm);
}
riscv_vm_time_delta_init(vm);
return 0;
}
int32_t arch_deinit_vm(struct acrn_vm *vm)
{
(void)vm;
return 0;
}
int32_t arch_reset_vm(struct acrn_vm *vm)
{
uint16_t i;
struct acrn_vcpu *vcpu = NULL;
foreach_vcpu(i, vm, vcpu) {
reset_vcpu(vcpu);
}
return 0;
}
void arch_vm_prepare_bsp(struct acrn_vcpu *vcpu)
{
struct acrn_vm *vm = vcpu->vm;
vcpu_set_epc(vcpu, (uint64_t)vm->sw.kernel_info.kernel_entry_addr);
vcpu->arch.regs.a0 = vcpu_get_vhartid(vcpu);
vcpu->arch.regs.a1 = (uint64_t)vm->sw.fdt_info.load_addr;
}
void arch_trigger_level_intr(__unused struct acrn_vm *vm,
__unused uint32_t irq, __unused bool assert) {}
static void fdt_set_hart_isa_str_all(void *fdt, const char *isa_str)
{
int cpus_off, cpu, ret, len;
const char *val;
cpus_off = fdt_path_offset(fdt, "/cpus");
if (cpus_off > 0) {
fdt_for_each_subnode(cpu, fdt, cpus_off) {
val = (const char *)fdt_getprop(fdt, cpu, "device_type", &len);
if ((len > 0) && (strncmp(val, "cpu", 3) == 0)) {
ret = fdt_setprop_string(fdt, cpu, "riscv,isa", isa_str);
if (ret < 0) {
pr_err("Failed to set hart isa string: %d", ret);
}
}
}
}
}
void arch_init_service_vm_vfdt(struct acrn_vm *vm)
{
/* TODO: For now hardcode the isa string.
*
* To do it formally, get isa string from host, remove extensions
* that we do not support (such as "h") and pass to vm.
*/
const char *isa_str = "rv64imafdc_zicsr_zifencei_sstc";
fdt_set_hart_isa_str_all(vm_get_vfdt(vm), isa_str);
}