Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
8e591cb7 ME |
2 | /* |
3 | * Copyright 2012 Michael Ellerman, IBM Corporation. | |
8e591cb7 ME |
4 | */ |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/kvm_host.h> | |
8 | #include <linux/kvm.h> | |
9 | #include <linux/err.h> | |
10 | ||
7c0f6ba6 | 11 | #include <linux/uaccess.h> |
8e591cb7 ME |
12 | #include <asm/kvm_book3s.h> |
13 | #include <asm/kvm_ppc.h> | |
14 | #include <asm/hvcall.h> | |
15 | #include <asm/rtas.h> | |
5af50993 | 16 | #include <asm/xive.h> |
8e591cb7 | 17 | |
bc5ad3f3 BH |
18 | #ifdef CONFIG_KVM_XICS |
19 | static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args) | |
20 | { | |
21 | u32 irq, server, priority; | |
22 | int rc; | |
23 | ||
19a44ecf | 24 | if (be32_to_cpu(args->nargs) != 3 || be32_to_cpu(args->nret) != 1) { |
bc5ad3f3 BH |
25 | rc = -3; |
26 | goto out; | |
27 | } | |
28 | ||
19a44ecf AG |
29 | irq = be32_to_cpu(args->args[0]); |
30 | server = be32_to_cpu(args->args[1]); | |
31 | priority = be32_to_cpu(args->args[2]); | |
bc5ad3f3 | 32 | |
03f95332 | 33 | if (xics_on_xive()) |
5af50993 BH |
34 | rc = kvmppc_xive_set_xive(vcpu->kvm, irq, server, priority); |
35 | else | |
36 | rc = kvmppc_xics_set_xive(vcpu->kvm, irq, server, priority); | |
bc5ad3f3 BH |
37 | if (rc) |
38 | rc = -3; | |
39 | out: | |
19a44ecf | 40 | args->rets[0] = cpu_to_be32(rc); |
bc5ad3f3 BH |
41 | } |
42 | ||
43 | static void kvm_rtas_get_xive(struct kvm_vcpu *vcpu, struct rtas_args *args) | |
44 | { | |
45 | u32 irq, server, priority; | |
46 | int rc; | |
47 | ||
19a44ecf | 48 | if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 3) { |
bc5ad3f3 BH |
49 | rc = -3; |
50 | goto out; | |
51 | } | |
52 | ||
19a44ecf | 53 | irq = be32_to_cpu(args->args[0]); |
bc5ad3f3 BH |
54 | |
55 | server = priority = 0; | |
03f95332 | 56 | if (xics_on_xive()) |
5af50993 BH |
57 | rc = kvmppc_xive_get_xive(vcpu->kvm, irq, &server, &priority); |
58 | else | |
59 | rc = kvmppc_xics_get_xive(vcpu->kvm, irq, &server, &priority); | |
bc5ad3f3 BH |
60 | if (rc) { |
61 | rc = -3; | |
62 | goto out; | |
63 | } | |
64 | ||
19a44ecf AG |
65 | args->rets[1] = cpu_to_be32(server); |
66 | args->rets[2] = cpu_to_be32(priority); | |
bc5ad3f3 | 67 | out: |
19a44ecf | 68 | args->rets[0] = cpu_to_be32(rc); |
bc5ad3f3 | 69 | } |
d19bd862 PM |
70 | |
71 | static void kvm_rtas_int_off(struct kvm_vcpu *vcpu, struct rtas_args *args) | |
72 | { | |
73 | u32 irq; | |
74 | int rc; | |
75 | ||
19a44ecf | 76 | if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 1) { |
d19bd862 PM |
77 | rc = -3; |
78 | goto out; | |
79 | } | |
80 | ||
19a44ecf | 81 | irq = be32_to_cpu(args->args[0]); |
d19bd862 | 82 | |
03f95332 | 83 | if (xics_on_xive()) |
5af50993 BH |
84 | rc = kvmppc_xive_int_off(vcpu->kvm, irq); |
85 | else | |
86 | rc = kvmppc_xics_int_off(vcpu->kvm, irq); | |
d19bd862 PM |
87 | if (rc) |
88 | rc = -3; | |
89 | out: | |
19a44ecf | 90 | args->rets[0] = cpu_to_be32(rc); |
d19bd862 PM |
91 | } |
92 | ||
93 | static void kvm_rtas_int_on(struct kvm_vcpu *vcpu, struct rtas_args *args) | |
94 | { | |
95 | u32 irq; | |
96 | int rc; | |
97 | ||
19a44ecf | 98 | if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 1) { |
d19bd862 PM |
99 | rc = -3; |
100 | goto out; | |
101 | } | |
102 | ||
19a44ecf | 103 | irq = be32_to_cpu(args->args[0]); |
d19bd862 | 104 | |
03f95332 | 105 | if (xics_on_xive()) |
5af50993 BH |
106 | rc = kvmppc_xive_int_on(vcpu->kvm, irq); |
107 | else | |
108 | rc = kvmppc_xics_int_on(vcpu->kvm, irq); | |
d19bd862 PM |
109 | if (rc) |
110 | rc = -3; | |
111 | out: | |
19a44ecf | 112 | args->rets[0] = cpu_to_be32(rc); |
d19bd862 | 113 | } |
bc5ad3f3 | 114 | #endif /* CONFIG_KVM_XICS */ |
8e591cb7 ME |
115 | |
116 | struct rtas_handler { | |
117 | void (*handler)(struct kvm_vcpu *vcpu, struct rtas_args *args); | |
118 | char *name; | |
119 | }; | |
120 | ||
bc5ad3f3 BH |
121 | static struct rtas_handler rtas_handlers[] = { |
122 | #ifdef CONFIG_KVM_XICS | |
123 | { .name = "ibm,set-xive", .handler = kvm_rtas_set_xive }, | |
124 | { .name = "ibm,get-xive", .handler = kvm_rtas_get_xive }, | |
d19bd862 PM |
125 | { .name = "ibm,int-off", .handler = kvm_rtas_int_off }, |
126 | { .name = "ibm,int-on", .handler = kvm_rtas_int_on }, | |
bc5ad3f3 BH |
127 | #endif |
128 | }; | |
8e591cb7 ME |
129 | |
130 | struct rtas_token_definition { | |
131 | struct list_head list; | |
132 | struct rtas_handler *handler; | |
133 | u64 token; | |
134 | }; | |
135 | ||
136 | static int rtas_name_matches(char *s1, char *s2) | |
137 | { | |
138 | struct kvm_rtas_token_args args; | |
139 | return !strncmp(s1, s2, sizeof(args.name)); | |
140 | } | |
141 | ||
142 | static int rtas_token_undefine(struct kvm *kvm, char *name) | |
143 | { | |
144 | struct rtas_token_definition *d, *tmp; | |
145 | ||
1659e27d | 146 | lockdep_assert_held(&kvm->arch.rtas_token_lock); |
8e591cb7 ME |
147 | |
148 | list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) { | |
149 | if (rtas_name_matches(d->handler->name, name)) { | |
150 | list_del(&d->list); | |
151 | kfree(d); | |
152 | return 0; | |
153 | } | |
154 | } | |
155 | ||
156 | /* It's not an error to undefine an undefined token */ | |
157 | return 0; | |
158 | } | |
159 | ||
160 | static int rtas_token_define(struct kvm *kvm, char *name, u64 token) | |
161 | { | |
162 | struct rtas_token_definition *d; | |
163 | struct rtas_handler *h = NULL; | |
164 | bool found; | |
165 | int i; | |
166 | ||
1659e27d | 167 | lockdep_assert_held(&kvm->arch.rtas_token_lock); |
8e591cb7 ME |
168 | |
169 | list_for_each_entry(d, &kvm->arch.rtas_tokens, list) { | |
170 | if (d->token == token) | |
171 | return -EEXIST; | |
172 | } | |
173 | ||
174 | found = false; | |
175 | for (i = 0; i < ARRAY_SIZE(rtas_handlers); i++) { | |
176 | h = &rtas_handlers[i]; | |
177 | if (rtas_name_matches(h->name, name)) { | |
178 | found = true; | |
179 | break; | |
180 | } | |
181 | } | |
182 | ||
183 | if (!found) | |
184 | return -ENOENT; | |
185 | ||
186 | d = kzalloc(sizeof(*d), GFP_KERNEL); | |
187 | if (!d) | |
188 | return -ENOMEM; | |
189 | ||
190 | d->handler = h; | |
191 | d->token = token; | |
192 | ||
193 | list_add_tail(&d->list, &kvm->arch.rtas_tokens); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp) | |
199 | { | |
200 | struct kvm_rtas_token_args args; | |
201 | int rc; | |
202 | ||
203 | if (copy_from_user(&args, argp, sizeof(args))) | |
204 | return -EFAULT; | |
205 | ||
1659e27d | 206 | mutex_lock(&kvm->arch.rtas_token_lock); |
8e591cb7 ME |
207 | |
208 | if (args.token) | |
209 | rc = rtas_token_define(kvm, args.name, args.token); | |
210 | else | |
211 | rc = rtas_token_undefine(kvm, args.name); | |
212 | ||
1659e27d | 213 | mutex_unlock(&kvm->arch.rtas_token_lock); |
8e591cb7 ME |
214 | |
215 | return rc; | |
216 | } | |
217 | ||
218 | int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu) | |
219 | { | |
220 | struct rtas_token_definition *d; | |
221 | struct rtas_args args; | |
222 | rtas_arg_t *orig_rets; | |
223 | gpa_t args_phys; | |
224 | int rc; | |
225 | ||
b24f36f3 PM |
226 | /* |
227 | * r4 contains the guest physical address of the RTAS args | |
228 | * Mask off the top 4 bits since this is a guest real address | |
229 | */ | |
230 | args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM; | |
8e591cb7 | 231 | |
2031f287 | 232 | kvm_vcpu_srcu_read_lock(vcpu); |
8e591cb7 | 233 | rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args)); |
2031f287 | 234 | kvm_vcpu_srcu_read_unlock(vcpu); |
8e591cb7 ME |
235 | if (rc) |
236 | goto fail; | |
237 | ||
238 | /* | |
239 | * args->rets is a pointer into args->args. Now that we've | |
240 | * copied args we need to fix it up to point into our copy, | |
241 | * not the guest args. We also need to save the original | |
242 | * value so we can restore it on the way out. | |
243 | */ | |
244 | orig_rets = args.rets; | |
f62f3c20 NP |
245 | if (be32_to_cpu(args.nargs) >= ARRAY_SIZE(args.args)) { |
246 | /* | |
247 | * Don't overflow our args array: ensure there is room for | |
248 | * at least rets[0] (even if the call specifies 0 nret). | |
249 | * | |
250 | * Each handler must then check for the correct nargs and nret | |
251 | * values, but they may always return failure in rets[0]. | |
252 | */ | |
253 | rc = -EINVAL; | |
254 | goto fail; | |
255 | } | |
19a44ecf | 256 | args.rets = &args.args[be32_to_cpu(args.nargs)]; |
8e591cb7 | 257 | |
1659e27d | 258 | mutex_lock(&vcpu->kvm->arch.rtas_token_lock); |
8e591cb7 ME |
259 | |
260 | rc = -ENOENT; | |
261 | list_for_each_entry(d, &vcpu->kvm->arch.rtas_tokens, list) { | |
19a44ecf | 262 | if (d->token == be32_to_cpu(args.token)) { |
8e591cb7 ME |
263 | d->handler->handler(vcpu, &args); |
264 | rc = 0; | |
265 | break; | |
266 | } | |
267 | } | |
268 | ||
1659e27d | 269 | mutex_unlock(&vcpu->kvm->arch.rtas_token_lock); |
8e591cb7 ME |
270 | |
271 | if (rc == 0) { | |
272 | args.rets = orig_rets; | |
273 | rc = kvm_write_guest(vcpu->kvm, args_phys, &args, sizeof(args)); | |
274 | if (rc) | |
275 | goto fail; | |
276 | } | |
277 | ||
278 | return rc; | |
279 | ||
280 | fail: | |
281 | /* | |
282 | * We only get here if the guest has called RTAS with a bogus | |
f62f3c20 NP |
283 | * args pointer or nargs/nret values that would overflow the |
284 | * array. That means we can't get to the args, and so we can't | |
285 | * fail the RTAS call. So fail right out to userspace, which | |
286 | * should kill the guest. | |
287 | * | |
288 | * SLOF should actually pass the hcall return value from the | |
289 | * rtas handler call in r3, so enter_rtas could be modified to | |
290 | * return a failure indication in r3 and we could return such | |
291 | * errors to the guest rather than failing to host userspace. | |
292 | * However old guests that don't test for failure could then | |
293 | * continue silently after errors, so for now we won't do this. | |
8e591cb7 ME |
294 | */ |
295 | return rc; | |
296 | } | |
2ba9f0d8 | 297 | EXPORT_SYMBOL_GPL(kvmppc_rtas_hcall); |
8e591cb7 ME |
298 | |
299 | void kvmppc_rtas_tokens_free(struct kvm *kvm) | |
300 | { | |
301 | struct rtas_token_definition *d, *tmp; | |
302 | ||
8e591cb7 ME |
303 | list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) { |
304 | list_del(&d->list); | |
305 | kfree(d); | |
306 | } | |
307 | } |