1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Copyright (c) 2021 Western Digital Corporation or its affiliates. |
4 | * |
5 | * Authors: |
6 | * Atish Patra <atish.patra@wdc.com> |
7 | */ |
8 | |
9 | #include <linux/errno.h> |
10 | #include <linux/err.h> |
11 | #include <linux/kvm_host.h> |
12 | #include <asm/sbi.h> |
13 | #include <asm/kvm_vcpu_sbi.h> |
14 | |
15 | static int kvm_sbi_hsm_vcpu_start(struct kvm_vcpu *vcpu) |
16 | { |
17 | struct kvm_cpu_context *reset_cntx; |
18 | struct kvm_cpu_context *cp = &vcpu->arch.guest_context; |
19 | struct kvm_vcpu *target_vcpu; |
20 | unsigned long target_vcpuid = cp->a0; |
21 | |
22 | target_vcpu = kvm_get_vcpu_by_id(kvm: vcpu->kvm, id: target_vcpuid); |
23 | if (!target_vcpu) |
24 | return SBI_ERR_INVALID_PARAM; |
25 | if (!target_vcpu->arch.power_off) |
26 | return SBI_ERR_ALREADY_AVAILABLE; |
27 | |
28 | reset_cntx = &target_vcpu->arch.guest_reset_context; |
29 | /* start address */ |
30 | reset_cntx->sepc = cp->a1; |
31 | /* target vcpu id to start */ |
32 | reset_cntx->a0 = target_vcpuid; |
33 | /* private data passed from kernel */ |
34 | reset_cntx->a1 = cp->a2; |
35 | kvm_make_request(req: KVM_REQ_VCPU_RESET, vcpu: target_vcpu); |
36 | |
37 | kvm_riscv_vcpu_power_on(target_vcpu); |
38 | |
39 | return 0; |
40 | } |
41 | |
42 | static int kvm_sbi_hsm_vcpu_stop(struct kvm_vcpu *vcpu) |
43 | { |
44 | if (vcpu->arch.power_off) |
45 | return SBI_ERR_FAILURE; |
46 | |
47 | kvm_riscv_vcpu_power_off(vcpu); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int kvm_sbi_hsm_vcpu_get_status(struct kvm_vcpu *vcpu) |
53 | { |
54 | struct kvm_cpu_context *cp = &vcpu->arch.guest_context; |
55 | unsigned long target_vcpuid = cp->a0; |
56 | struct kvm_vcpu *target_vcpu; |
57 | |
58 | target_vcpu = kvm_get_vcpu_by_id(kvm: vcpu->kvm, id: target_vcpuid); |
59 | if (!target_vcpu) |
60 | return SBI_ERR_INVALID_PARAM; |
61 | if (!target_vcpu->arch.power_off) |
62 | return SBI_HSM_STATE_STARTED; |
63 | else if (vcpu->stat.generic.blocking) |
64 | return SBI_HSM_STATE_SUSPENDED; |
65 | else |
66 | return SBI_HSM_STATE_STOPPED; |
67 | } |
68 | |
69 | static int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, |
70 | struct kvm_vcpu_sbi_return *retdata) |
71 | { |
72 | int ret = 0; |
73 | struct kvm_cpu_context *cp = &vcpu->arch.guest_context; |
74 | struct kvm *kvm = vcpu->kvm; |
75 | unsigned long funcid = cp->a6; |
76 | |
77 | switch (funcid) { |
78 | case SBI_EXT_HSM_HART_START: |
79 | mutex_lock(&kvm->lock); |
80 | ret = kvm_sbi_hsm_vcpu_start(vcpu); |
81 | mutex_unlock(lock: &kvm->lock); |
82 | break; |
83 | case SBI_EXT_HSM_HART_STOP: |
84 | ret = kvm_sbi_hsm_vcpu_stop(vcpu); |
85 | break; |
86 | case SBI_EXT_HSM_HART_STATUS: |
87 | ret = kvm_sbi_hsm_vcpu_get_status(vcpu); |
88 | if (ret >= 0) { |
89 | retdata->out_val = ret; |
90 | retdata->err_val = 0; |
91 | } |
92 | return 0; |
93 | case SBI_EXT_HSM_HART_SUSPEND: |
94 | switch (cp->a0) { |
95 | case SBI_HSM_SUSPEND_RET_DEFAULT: |
96 | kvm_riscv_vcpu_wfi(vcpu); |
97 | break; |
98 | case SBI_HSM_SUSPEND_NON_RET_DEFAULT: |
99 | ret = SBI_ERR_NOT_SUPPORTED; |
100 | break; |
101 | default: |
102 | ret = SBI_ERR_INVALID_PARAM; |
103 | } |
104 | break; |
105 | default: |
106 | ret = SBI_ERR_NOT_SUPPORTED; |
107 | } |
108 | |
109 | retdata->err_val = ret; |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm = { |
115 | .extid_start = SBI_EXT_HSM, |
116 | .extid_end = SBI_EXT_HSM, |
117 | .handler = kvm_sbi_ext_hsm_handler, |
118 | }; |
119 |