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