aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kvm/book3s_rtas.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kvm/book3s_rtas.c')
-rw-r--r--arch/powerpc/kvm/book3s_rtas.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/arch/powerpc/kvm/book3s_rtas.c b/arch/powerpc/kvm/book3s_rtas.c
new file mode 100644
index 000000000000..6ad7050eb67d
--- /dev/null
+++ b/arch/powerpc/kvm/book3s_rtas.c
@@ -0,0 +1,182 @@
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
20
21struct rtas_handler {
22 void (*handler)(struct kvm_vcpu *vcpu, struct rtas_args *args);
23 char *name;
24};
25
26static struct rtas_handler rtas_handlers[] = { };
27
28struct rtas_token_definition {
29 struct list_head list;
30 struct rtas_handler *handler;
31 u64 token;
32};
33
34static int rtas_name_matches(char *s1, char *s2)
35{
36 struct kvm_rtas_token_args args;
37 return !strncmp(s1, s2, sizeof(args.name));
38}
39
40static int rtas_token_undefine(struct kvm *kvm, char *name)
41{
42 struct rtas_token_definition *d, *tmp;
43
44 lockdep_assert_held(&kvm->lock);
45
46 list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
47 if (rtas_name_matches(d->handler->name, name)) {
48 list_del(&d->list);
49 kfree(d);
50 return 0;
51 }
52 }
53
54 /* It's not an error to undefine an undefined token */
55 return 0;
56}
57
58static int rtas_token_define(struct kvm *kvm, char *name, u64 token)
59{
60 struct rtas_token_definition *d;
61 struct rtas_handler *h = NULL;
62 bool found;
63 int i;
64
65 lockdep_assert_held(&kvm->lock);
66
67 list_for_each_entry(d, &kvm->arch.rtas_tokens, list) {
68 if (d->token == token)
69 return -EEXIST;
70 }
71
72 found = false;
73 for (i = 0; i < ARRAY_SIZE(rtas_handlers); i++) {
74 h = &rtas_handlers[i];
75 if (rtas_name_matches(h->name, name)) {
76 found = true;
77 break;
78 }
79 }
80
81 if (!found)
82 return -ENOENT;
83
84 d = kzalloc(sizeof(*d), GFP_KERNEL);
85 if (!d)
86 return -ENOMEM;
87
88 d->handler = h;
89 d->token = token;
90
91 list_add_tail(&d->list, &kvm->arch.rtas_tokens);
92
93 return 0;
94}
95
96int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp)
97{
98 struct kvm_rtas_token_args args;
99 int rc;
100
101 if (copy_from_user(&args, argp, sizeof(args)))
102 return -EFAULT;
103
104 mutex_lock(&kvm->lock);
105
106 if (args.token)
107 rc = rtas_token_define(kvm, args.name, args.token);
108 else
109 rc = rtas_token_undefine(kvm, args.name);
110
111 mutex_unlock(&kvm->lock);
112
113 return rc;
114}
115
116int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
117{
118 struct rtas_token_definition *d;
119 struct rtas_args args;
120 rtas_arg_t *orig_rets;
121 gpa_t args_phys;
122 int rc;
123
124 /* r4 contains the guest physical address of the RTAS args */
125 args_phys = kvmppc_get_gpr(vcpu, 4);
126
127 rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
128 if (rc)
129 goto fail;
130
131 /*
132 * args->rets is a pointer into args->args. Now that we've
133 * copied args we need to fix it up to point into our copy,
134 * not the guest args. We also need to save the original
135 * value so we can restore it on the way out.
136 */
137 orig_rets = args.rets;
138 args.rets = &args.args[args.nargs];
139
140 mutex_lock(&vcpu->kvm->lock);
141
142 rc = -ENOENT;
143 list_for_each_entry(d, &vcpu->kvm->arch.rtas_tokens, list) {
144 if (d->token == args.token) {
145 d->handler->handler(vcpu, &args);
146 rc = 0;
147 break;
148 }
149 }
150
151 mutex_unlock(&vcpu->kvm->lock);
152
153 if (rc == 0) {
154 args.rets = orig_rets;
155 rc = kvm_write_guest(vcpu->kvm, args_phys, &args, sizeof(args));
156 if (rc)
157 goto fail;
158 }
159
160 return rc;
161
162fail:
163 /*
164 * We only get here if the guest has called RTAS with a bogus
165 * args pointer. That means we can't get to the args, and so we
166 * can't fail the RTAS call. So fail right out to userspace,
167 * which should kill the guest.
168 */
169 return rc;
170}
171
172void kvmppc_rtas_tokens_free(struct kvm *kvm)
173{
174 struct rtas_token_definition *d, *tmp;
175
176 lockdep_assert_held(&kvm->lock);
177
178 list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
179 list_del(&d->list);
180 kfree(d);
181 }
182}