1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2024 Ventana Micro Systems Inc.
4 */
5
6 #include <linux/kvm_host.h>
7 #include <linux/wordpart.h>
8
9 #include <asm/kvm_vcpu_sbi.h>
10 #include <asm/sbi.h>
11
kvm_sbi_ext_susp_handler(struct kvm_vcpu * vcpu,struct kvm_run * run,struct kvm_vcpu_sbi_return * retdata)12 static int kvm_sbi_ext_susp_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
13 struct kvm_vcpu_sbi_return *retdata)
14 {
15 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
16 struct kvm_cpu_context *reset_cntx;
17 unsigned long funcid = cp->a6;
18 unsigned long hva, i;
19 struct kvm_vcpu *tmp;
20
21 switch (funcid) {
22 case SBI_EXT_SUSP_SYSTEM_SUSPEND:
23 if (lower_32_bits(cp->a0) != SBI_SUSP_SLEEP_TYPE_SUSPEND_TO_RAM) {
24 retdata->err_val = SBI_ERR_INVALID_PARAM;
25 return 0;
26 }
27
28 if (!(cp->sstatus & SR_SPP)) {
29 retdata->err_val = SBI_ERR_FAILURE;
30 return 0;
31 }
32
33 hva = kvm_vcpu_gfn_to_hva_prot(vcpu, cp->a1 >> PAGE_SHIFT, NULL);
34 if (kvm_is_error_hva(hva)) {
35 retdata->err_val = SBI_ERR_INVALID_ADDRESS;
36 return 0;
37 }
38
39 kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
40 if (tmp == vcpu)
41 continue;
42 if (!kvm_riscv_vcpu_stopped(tmp)) {
43 retdata->err_val = SBI_ERR_DENIED;
44 return 0;
45 }
46 }
47
48 spin_lock(&vcpu->arch.reset_cntx_lock);
49 reset_cntx = &vcpu->arch.guest_reset_context;
50 reset_cntx->sepc = cp->a1;
51 reset_cntx->a0 = vcpu->vcpu_id;
52 reset_cntx->a1 = cp->a2;
53 spin_unlock(&vcpu->arch.reset_cntx_lock);
54
55 kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
56
57 /* userspace provides the suspend implementation */
58 kvm_riscv_vcpu_sbi_forward(vcpu, run);
59 retdata->uexit = true;
60 break;
61 default:
62 retdata->err_val = SBI_ERR_NOT_SUPPORTED;
63 break;
64 }
65
66 return 0;
67 }
68
69 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_susp = {
70 .extid_start = SBI_EXT_SUSP,
71 .extid_end = SBI_EXT_SUSP,
72 .default_disabled = true,
73 .handler = kvm_sbi_ext_susp_handler,
74 };
75