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