Implement a KVM SBI SUSP extension handler. The handler only validates the system suspend entry criteria and prepares for resuming in the appropriate state at the resume_addr (as specified by the SBI spec), but then it forwards the call to the VMM where any system suspend behavior may be implemented. Since VMM support is needed, KVM disables the extension by default. Signed-off-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20241017074538.18867-5-ajones@ventanamicro.com Signed-off-by: Anup Patel <anup@brainfault.org>
73 lines
1.7 KiB
C
73 lines
1.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2024 Ventana Micro Systems Inc.
|
|
*/
|
|
|
|
#include <linux/kvm_host.h>
|
|
|
|
#include <asm/kvm_vcpu_sbi.h>
|
|
#include <asm/sbi.h>
|
|
|
|
static int kvm_sbi_ext_susp_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
|
struct kvm_vcpu_sbi_return *retdata)
|
|
{
|
|
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
|
|
struct kvm_cpu_context *reset_cntx;
|
|
unsigned long funcid = cp->a6;
|
|
unsigned long hva, i;
|
|
struct kvm_vcpu *tmp;
|
|
|
|
switch (funcid) {
|
|
case SBI_EXT_SUSP_SYSTEM_SUSPEND:
|
|
if (cp->a0 != SBI_SUSP_SLEEP_TYPE_SUSPEND_TO_RAM) {
|
|
retdata->err_val = SBI_ERR_INVALID_PARAM;
|
|
return 0;
|
|
}
|
|
|
|
if (!(cp->sstatus & SR_SPP)) {
|
|
retdata->err_val = SBI_ERR_FAILURE;
|
|
return 0;
|
|
}
|
|
|
|
hva = kvm_vcpu_gfn_to_hva_prot(vcpu, cp->a1 >> PAGE_SHIFT, NULL);
|
|
if (kvm_is_error_hva(hva)) {
|
|
retdata->err_val = SBI_ERR_INVALID_ADDRESS;
|
|
return 0;
|
|
}
|
|
|
|
kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
|
|
if (tmp == vcpu)
|
|
continue;
|
|
if (!kvm_riscv_vcpu_stopped(tmp)) {
|
|
retdata->err_val = SBI_ERR_DENIED;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
spin_lock(&vcpu->arch.reset_cntx_lock);
|
|
reset_cntx = &vcpu->arch.guest_reset_context;
|
|
reset_cntx->sepc = cp->a1;
|
|
reset_cntx->a0 = vcpu->vcpu_id;
|
|
reset_cntx->a1 = cp->a2;
|
|
spin_unlock(&vcpu->arch.reset_cntx_lock);
|
|
|
|
kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
|
|
|
|
/* userspace provides the suspend implementation */
|
|
kvm_riscv_vcpu_sbi_forward(vcpu, run);
|
|
retdata->uexit = true;
|
|
break;
|
|
default:
|
|
retdata->err_val = SBI_ERR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_susp = {
|
|
.extid_start = SBI_EXT_SUSP,
|
|
.extid_end = SBI_EXT_SUSP,
|
|
.default_disabled = true,
|
|
.handler = kvm_sbi_ext_susp_handler,
|
|
};
|