blob: 77f9aa5f4ba578495ac8ffff562059865ed729ad [file] [log] [blame]
Michael Ellerman8e591cb2013-04-17 20:30:00 +00001/*
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
Benjamin Herrenschmidtbc5ad3f2013-04-17 20:30:26 +000020#ifdef CONFIG_KVM_XICS
21static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
22{
23 u32 irq, server, priority;
24 int rc;
25
26 if (args->nargs != 3 || args->nret != 1) {
27 rc = -3;
28 goto out;
29 }
30
31 irq = args->args[0];
32 server = args->args[1];
33 priority = args->args[2];
34
35 rc = kvmppc_xics_set_xive(vcpu->kvm, irq, server, priority);
36 if (rc)
37 rc = -3;
38out:
39 args->rets[0] = rc;
40}
41
42static void kvm_rtas_get_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
43{
44 u32 irq, server, priority;
45 int rc;
46
47 if (args->nargs != 1 || args->nret != 3) {
48 rc = -3;
49 goto out;
50 }
51
52 irq = args->args[0];
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
61 args->rets[1] = server;
62 args->rets[2] = priority;
63out:
64 args->rets[0] = rc;
65}
66#endif /* CONFIG_KVM_XICS */
Michael Ellerman8e591cb2013-04-17 20:30:00 +000067
68struct rtas_handler {
69 void (*handler)(struct kvm_vcpu *vcpu, struct rtas_args *args);
70 char *name;
71};
72
Benjamin Herrenschmidtbc5ad3f2013-04-17 20:30:26 +000073static struct rtas_handler rtas_handlers[] = {
74#ifdef CONFIG_KVM_XICS
75 { .name = "ibm,set-xive", .handler = kvm_rtas_set_xive },
76 { .name = "ibm,get-xive", .handler = kvm_rtas_get_xive },
77#endif
78};
Michael Ellerman8e591cb2013-04-17 20:30:00 +000079
80struct rtas_token_definition {
81 struct list_head list;
82 struct rtas_handler *handler;
83 u64 token;
84};
85
86static int rtas_name_matches(char *s1, char *s2)
87{
88 struct kvm_rtas_token_args args;
89 return !strncmp(s1, s2, sizeof(args.name));
90}
91
92static int rtas_token_undefine(struct kvm *kvm, char *name)
93{
94 struct rtas_token_definition *d, *tmp;
95
96 lockdep_assert_held(&kvm->lock);
97
98 list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
99 if (rtas_name_matches(d->handler->name, name)) {
100 list_del(&d->list);
101 kfree(d);
102 return 0;
103 }
104 }
105
106 /* It's not an error to undefine an undefined token */
107 return 0;
108}
109
110static int rtas_token_define(struct kvm *kvm, char *name, u64 token)
111{
112 struct rtas_token_definition *d;
113 struct rtas_handler *h = NULL;
114 bool found;
115 int i;
116
117 lockdep_assert_held(&kvm->lock);
118
119 list_for_each_entry(d, &kvm->arch.rtas_tokens, list) {
120 if (d->token == token)
121 return -EEXIST;
122 }
123
124 found = false;
125 for (i = 0; i < ARRAY_SIZE(rtas_handlers); i++) {
126 h = &rtas_handlers[i];
127 if (rtas_name_matches(h->name, name)) {
128 found = true;
129 break;
130 }
131 }
132
133 if (!found)
134 return -ENOENT;
135
136 d = kzalloc(sizeof(*d), GFP_KERNEL);
137 if (!d)
138 return -ENOMEM;
139
140 d->handler = h;
141 d->token = token;
142
143 list_add_tail(&d->list, &kvm->arch.rtas_tokens);
144
145 return 0;
146}
147
148int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp)
149{
150 struct kvm_rtas_token_args args;
151 int rc;
152
153 if (copy_from_user(&args, argp, sizeof(args)))
154 return -EFAULT;
155
156 mutex_lock(&kvm->lock);
157
158 if (args.token)
159 rc = rtas_token_define(kvm, args.name, args.token);
160 else
161 rc = rtas_token_undefine(kvm, args.name);
162
163 mutex_unlock(&kvm->lock);
164
165 return rc;
166}
167
168int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
169{
170 struct rtas_token_definition *d;
171 struct rtas_args args;
172 rtas_arg_t *orig_rets;
173 gpa_t args_phys;
174 int rc;
175
176 /* r4 contains the guest physical address of the RTAS args */
177 args_phys = kvmppc_get_gpr(vcpu, 4);
178
179 rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
180 if (rc)
181 goto fail;
182
183 /*
184 * args->rets is a pointer into args->args. Now that we've
185 * copied args we need to fix it up to point into our copy,
186 * not the guest args. We also need to save the original
187 * value so we can restore it on the way out.
188 */
189 orig_rets = args.rets;
190 args.rets = &args.args[args.nargs];
191
192 mutex_lock(&vcpu->kvm->lock);
193
194 rc = -ENOENT;
195 list_for_each_entry(d, &vcpu->kvm->arch.rtas_tokens, list) {
196 if (d->token == args.token) {
197 d->handler->handler(vcpu, &args);
198 rc = 0;
199 break;
200 }
201 }
202
203 mutex_unlock(&vcpu->kvm->lock);
204
205 if (rc == 0) {
206 args.rets = orig_rets;
207 rc = kvm_write_guest(vcpu->kvm, args_phys, &args, sizeof(args));
208 if (rc)
209 goto fail;
210 }
211
212 return rc;
213
214fail:
215 /*
216 * We only get here if the guest has called RTAS with a bogus
217 * args pointer. That means we can't get to the args, and so we
218 * can't fail the RTAS call. So fail right out to userspace,
219 * which should kill the guest.
220 */
221 return rc;
222}
223
224void kvmppc_rtas_tokens_free(struct kvm *kvm)
225{
226 struct rtas_token_definition *d, *tmp;
227
228 lockdep_assert_held(&kvm->lock);
229
230 list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
231 list_del(&d->list);
232 kfree(d);
233 }
234}