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 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