xref: /linux/arch/riscv/kvm/vcpu_sbi_system.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
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