1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
6 * Atish Patra <atish.patra@wdc.com>
9 #include <linux/errno.h>
10 #include <linux/err.h>
11 #include <linux/kvm_host.h>
13 #include <asm/kvm_vcpu_sbi.h>
15 static int kvm_linux_err_map_sbi(int err)
21 return SBI_ERR_DENIED;
23 return SBI_ERR_INVALID_PARAM;
25 return SBI_ERR_INVALID_ADDRESS;
27 return SBI_ERR_NOT_SUPPORTED;
29 return SBI_ERR_ALREADY_AVAILABLE;
31 return SBI_ERR_FAILURE;
35 #ifdef CONFIG_RISCV_SBI_V01
36 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01;
38 static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
44 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base;
45 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time;
46 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi;
47 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence;
48 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_srst;
49 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm;
50 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental;
51 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor;
53 static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
61 &vcpu_sbi_ext_experimental,
65 void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)
67 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
69 vcpu->arch.sbi_context.return_handled = 0;
70 vcpu->stat.ecall_exit_stat++;
71 run->exit_reason = KVM_EXIT_RISCV_SBI;
72 run->riscv_sbi.extension_id = cp->a7;
73 run->riscv_sbi.function_id = cp->a6;
74 run->riscv_sbi.args[0] = cp->a0;
75 run->riscv_sbi.args[1] = cp->a1;
76 run->riscv_sbi.args[2] = cp->a2;
77 run->riscv_sbi.args[3] = cp->a3;
78 run->riscv_sbi.args[4] = cp->a4;
79 run->riscv_sbi.args[5] = cp->a5;
80 run->riscv_sbi.ret[0] = cp->a0;
81 run->riscv_sbi.ret[1] = cp->a1;
84 void kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu *vcpu,
91 kvm_for_each_vcpu(i, tmp, vcpu->kvm)
92 tmp->arch.power_off = true;
93 kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
95 memset(&run->system_event, 0, sizeof(run->system_event));
96 run->system_event.type = type;
97 run->system_event.flags = flags;
98 run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
101 int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
103 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
105 /* Handle SBI return only once */
106 if (vcpu->arch.sbi_context.return_handled)
108 vcpu->arch.sbi_context.return_handled = 1;
110 /* Update return values */
111 cp->a0 = run->riscv_sbi.ret[0];
112 cp->a1 = run->riscv_sbi.ret[1];
114 /* Move to next instruction */
115 vcpu->arch.guest_context.sepc += 4;
120 const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid)
124 for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) {
125 if (sbi_ext[i]->extid_start <= extid &&
126 sbi_ext[i]->extid_end >= extid)
133 int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
136 bool next_sepc = true;
137 bool userspace_exit = false;
138 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
139 const struct kvm_vcpu_sbi_extension *sbi_ext;
140 struct kvm_cpu_trap utrap = { 0 };
141 unsigned long out_val = 0;
142 bool ext_is_v01 = false;
144 sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7);
145 if (sbi_ext && sbi_ext->handler) {
146 #ifdef CONFIG_RISCV_SBI_V01
147 if (cp->a7 >= SBI_EXT_0_1_SET_TIMER &&
148 cp->a7 <= SBI_EXT_0_1_SHUTDOWN)
151 ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit);
153 /* Return error for unsupported SBI calls */
154 cp->a0 = SBI_ERR_NOT_SUPPORTED;
158 /* Handle special error cases i.e trap, exit or userspace forward */
160 /* No need to increment sepc or exit ioctl loop */
162 utrap.sepc = cp->sepc;
163 kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
168 /* Exit ioctl loop or Propagate the error code the guest */
169 if (userspace_exit) {
174 * SBI extension handler always returns an Linux error code. Convert
175 * it to the SBI specific error code that can be propagated the SBI
178 ret = kvm_linux_err_map_sbi(ret);