1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2019 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
kvm_linux_err_map_sbi(int err)15 static int kvm_linux_err_map_sbi(int err)
16 {
17 switch (err) {
18 case 0:
19 return SBI_SUCCESS;
20 case -EPERM:
21 return SBI_ERR_DENIED;
22 case -EINVAL:
23 return SBI_ERR_INVALID_PARAM;
24 case -EFAULT:
25 return SBI_ERR_INVALID_ADDRESS;
26 case -EOPNOTSUPP:
27 return SBI_ERR_NOT_SUPPORTED;
28 case -EALREADY:
29 return SBI_ERR_ALREADY_AVAILABLE;
30 default:
31 return SBI_ERR_FAILURE;
32 };
33 }
34
35 #ifndef CONFIG_RISCV_SBI_V01
36 static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
37 .extid_start = -1UL,
38 .extid_end = -1UL,
39 .handler = NULL,
40 };
41 #endif
42
43 static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
44 &vcpu_sbi_ext_v01,
45 &vcpu_sbi_ext_base,
46 &vcpu_sbi_ext_time,
47 &vcpu_sbi_ext_ipi,
48 &vcpu_sbi_ext_rfence,
49 &vcpu_sbi_ext_srst,
50 &vcpu_sbi_ext_hsm,
51 &vcpu_sbi_ext_experimental,
52 &vcpu_sbi_ext_vendor,
53 };
54
kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu * vcpu,struct kvm_run * run)55 void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)
56 {
57 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
58
59 vcpu->arch.sbi_context.return_handled = 0;
60 vcpu->stat.ecall_exit_stat++;
61 run->exit_reason = KVM_EXIT_RISCV_SBI;
62 run->riscv_sbi.extension_id = cp->a7;
63 run->riscv_sbi.function_id = cp->a6;
64 run->riscv_sbi.args[0] = cp->a0;
65 run->riscv_sbi.args[1] = cp->a1;
66 run->riscv_sbi.args[2] = cp->a2;
67 run->riscv_sbi.args[3] = cp->a3;
68 run->riscv_sbi.args[4] = cp->a4;
69 run->riscv_sbi.args[5] = cp->a5;
70 run->riscv_sbi.ret[0] = cp->a0;
71 run->riscv_sbi.ret[1] = cp->a1;
72 }
73
kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu * vcpu,struct kvm_run * run,u32 type,u64 reason)74 void kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu *vcpu,
75 struct kvm_run *run,
76 u32 type, u64 reason)
77 {
78 unsigned long i;
79 struct kvm_vcpu *tmp;
80
81 kvm_for_each_vcpu(i, tmp, vcpu->kvm)
82 tmp->arch.power_off = true;
83 kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
84
85 memset(&run->system_event, 0, sizeof(run->system_event));
86 run->system_event.type = type;
87 run->system_event.ndata = 1;
88 run->system_event.data[0] = reason;
89 run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
90 }
91
kvm_riscv_vcpu_sbi_return(struct kvm_vcpu * vcpu,struct kvm_run * run)92 int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
93 {
94 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
95
96 /* Handle SBI return only once */
97 if (vcpu->arch.sbi_context.return_handled)
98 return 0;
99 vcpu->arch.sbi_context.return_handled = 1;
100
101 /* Update return values */
102 cp->a0 = run->riscv_sbi.ret[0];
103 cp->a1 = run->riscv_sbi.ret[1];
104
105 /* Move to next instruction */
106 vcpu->arch.guest_context.sepc += 4;
107
108 return 0;
109 }
110
kvm_vcpu_sbi_find_ext(unsigned long extid)111 const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid)
112 {
113 int i = 0;
114
115 for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) {
116 if (sbi_ext[i]->extid_start <= extid &&
117 sbi_ext[i]->extid_end >= extid)
118 return sbi_ext[i];
119 }
120
121 return NULL;
122 }
123
kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu * vcpu,struct kvm_run * run)124 int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
125 {
126 int ret = 1;
127 bool next_sepc = true;
128 bool userspace_exit = false;
129 struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
130 const struct kvm_vcpu_sbi_extension *sbi_ext;
131 struct kvm_cpu_trap utrap = { 0 };
132 unsigned long out_val = 0;
133 bool ext_is_v01 = false;
134
135 sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7);
136 if (sbi_ext && sbi_ext->handler) {
137 #ifdef CONFIG_RISCV_SBI_V01
138 if (cp->a7 >= SBI_EXT_0_1_SET_TIMER &&
139 cp->a7 <= SBI_EXT_0_1_SHUTDOWN)
140 ext_is_v01 = true;
141 #endif
142 ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit);
143 } else {
144 /* Return error for unsupported SBI calls */
145 cp->a0 = SBI_ERR_NOT_SUPPORTED;
146 goto ecall_done;
147 }
148
149 /* Handle special error cases i.e trap, exit or userspace forward */
150 if (utrap.scause) {
151 /* No need to increment sepc or exit ioctl loop */
152 ret = 1;
153 utrap.sepc = cp->sepc;
154 kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
155 next_sepc = false;
156 goto ecall_done;
157 }
158
159 /* Exit ioctl loop or Propagate the error code the guest */
160 if (userspace_exit) {
161 next_sepc = false;
162 ret = 0;
163 } else {
164 /**
165 * SBI extension handler always returns an Linux error code. Convert
166 * it to the SBI specific error code that can be propagated the SBI
167 * caller.
168 */
169 ret = kvm_linux_err_map_sbi(ret);
170 cp->a0 = ret;
171 ret = 1;
172 }
173 ecall_done:
174 if (next_sepc)
175 cp->sepc += 4;
176 if (!ext_is_v01)
177 cp->a1 = out_val;
178
179 return ret;
180 }
181