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 unsigned long funcid = cp->a6; 17 unsigned long hva, i; 18 struct kvm_vcpu *tmp; 19 20 switch (funcid) { 21 case SBI_EXT_SUSP_SYSTEM_SUSPEND: 22 if (lower_32_bits(cp->a0) != SBI_SUSP_SLEEP_TYPE_SUSPEND_TO_RAM) { 23 retdata->err_val = SBI_ERR_INVALID_PARAM; 24 return 0; 25 } 26 27 if (!(cp->sstatus & SR_SPP)) { 28 retdata->err_val = SBI_ERR_FAILURE; 29 return 0; 30 } 31 32 hva = kvm_vcpu_gfn_to_hva_prot(vcpu, cp->a1 >> PAGE_SHIFT, NULL); 33 if (kvm_is_error_hva(hva)) { 34 retdata->err_val = SBI_ERR_INVALID_ADDRESS; 35 return 0; 36 } 37 38 /* 39 * Check that all other vCPUs are stopped before entering 40 * system suspend. 41 * 42 * There is a known TOCTOU race here: a concurrent HSM 43 * HART_START on another vCPU can start a vCPU after it 44 * has already passed this check, violating the invariant. 45 * 46 * We do not fix this because: 47 * 1. Triggering the race requires a pathological guest. 48 * 2. Only guest state is at risk, not host integrity. 49 * 3. Userspace can double-check vCPU states before 50 * proceeding with suspend. 51 */ 52 kvm_for_each_vcpu(i, tmp, vcpu->kvm) { 53 if (tmp == vcpu) 54 continue; 55 if (!kvm_riscv_vcpu_stopped(tmp)) { 56 retdata->err_val = SBI_ERR_DENIED; 57 return 0; 58 } 59 } 60 61 kvm_riscv_vcpu_sbi_request_reset(vcpu, cp->a1, cp->a2); 62 63 /* userspace provides the suspend implementation */ 64 return kvm_riscv_vcpu_sbi_forward_handler(vcpu, run, retdata); 65 default: 66 retdata->err_val = SBI_ERR_NOT_SUPPORTED; 67 break; 68 } 69 70 return 0; 71 } 72 73 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_susp = { 74 .extid_start = SBI_EXT_SUSP, 75 .extid_end = SBI_EXT_SUSP, 76 .default_disabled = true, 77 .handler = kvm_sbi_ext_susp_handler, 78 }; 79