s390/kvm,gaccess: fix guest access return code handling
[pandora-kernel.git] / arch / s390 / kvm / gaccess.h
1 /*
2  * access guest memory
3  *
4  * Copyright IBM Corp. 2008, 2009
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License (version 2 only)
8  * as published by the Free Software Foundation.
9  *
10  *    Author(s): Carsten Otte <cotte@de.ibm.com>
11  */
12
13 #ifndef __KVM_S390_GACCESS_H
14 #define __KVM_S390_GACCESS_H
15
16 #include <linux/compiler.h>
17 #include <linux/kvm_host.h>
18 #include <asm/uaccess.h>
19 #include "kvm-s390.h"
20
21 static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
22                                                unsigned long guestaddr)
23 {
24         unsigned long prefix  = vcpu->arch.sie_block->prefix;
25         unsigned long uaddress;
26
27         if (guestaddr < 2 * PAGE_SIZE)
28                 guestaddr += prefix;
29         else if ((guestaddr >= prefix) && (guestaddr < prefix + 2 * PAGE_SIZE))
30                 guestaddr -= prefix;
31         uaddress = gmap_fault(guestaddr, vcpu->arch.gmap);
32         if (IS_ERR_VALUE(uaddress))
33                 uaddress = -EFAULT;
34         return (void __user *)uaddress;
35 }
36
37 static inline int get_guest_u64(struct kvm_vcpu *vcpu, unsigned long guestaddr,
38                                 u64 *result)
39 {
40         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
41
42         BUG_ON(guestaddr & 7);
43
44         if (IS_ERR((void __force *) uptr))
45                 return PTR_ERR((void __force *) uptr);
46
47         return get_user(*result, (unsigned long __user *) uptr);
48 }
49
50 static inline int get_guest_u32(struct kvm_vcpu *vcpu, unsigned long guestaddr,
51                                 u32 *result)
52 {
53         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
54
55         BUG_ON(guestaddr & 3);
56
57         if (IS_ERR((void __force *) uptr))
58                 return PTR_ERR((void __force *) uptr);
59
60         return get_user(*result, (u32 __user *) uptr);
61 }
62
63 static inline int get_guest_u16(struct kvm_vcpu *vcpu, unsigned long guestaddr,
64                                 u16 *result)
65 {
66         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
67
68         BUG_ON(guestaddr & 1);
69
70         if (IS_ERR(uptr))
71                 return PTR_ERR(uptr);
72
73         return get_user(*result, (u16 __user *) uptr);
74 }
75
76 static inline int get_guest_u8(struct kvm_vcpu *vcpu, unsigned long guestaddr,
77                                u8 *result)
78 {
79         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
80
81         if (IS_ERR((void __force *) uptr))
82                 return PTR_ERR((void __force *) uptr);
83
84         return get_user(*result, (u8 __user *) uptr);
85 }
86
87 static inline int put_guest_u64(struct kvm_vcpu *vcpu, unsigned long guestaddr,
88                                 u64 value)
89 {
90         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
91
92         BUG_ON(guestaddr & 7);
93
94         if (IS_ERR((void __force *) uptr))
95                 return PTR_ERR((void __force *) uptr);
96
97         return put_user(value, (u64 __user *) uptr);
98 }
99
100 static inline int put_guest_u32(struct kvm_vcpu *vcpu, unsigned long guestaddr,
101                                 u32 value)
102 {
103         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
104
105         BUG_ON(guestaddr & 3);
106
107         if (IS_ERR((void __force *) uptr))
108                 return PTR_ERR((void __force *) uptr);
109
110         return put_user(value, (u32 __user *) uptr);
111 }
112
113 static inline int put_guest_u16(struct kvm_vcpu *vcpu, unsigned long guestaddr,
114                                 u16 value)
115 {
116         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
117
118         BUG_ON(guestaddr & 1);
119
120         if (IS_ERR((void __force *) uptr))
121                 return PTR_ERR((void __force *) uptr);
122
123         return put_user(value, (u16 __user *) uptr);
124 }
125
126 static inline int put_guest_u8(struct kvm_vcpu *vcpu, unsigned long guestaddr,
127                                u8 value)
128 {
129         void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
130
131         if (IS_ERR((void __force *) uptr))
132                 return PTR_ERR((void __force *) uptr);
133
134         return put_user(value, (u8 __user *) uptr);
135 }
136
137
138 static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
139                                        unsigned long guestdest,
140                                        void *from, unsigned long n)
141 {
142         int rc;
143         unsigned long i;
144         u8 *data = from;
145
146         for (i = 0; i < n; i++) {
147                 rc = put_guest_u8(vcpu, guestdest++, *(data++));
148                 if (rc < 0)
149                         return rc;
150         }
151         return 0;
152 }
153
154 static inline int __copy_to_guest_fast(struct kvm_vcpu *vcpu,
155                                        unsigned long guestdest,
156                                        void *from, unsigned long n)
157 {
158         int r;
159         void __user *uptr;
160         unsigned long size;
161
162         if (guestdest + n < guestdest)
163                 return -EFAULT;
164
165         /* simple case: all within one segment table entry? */
166         if ((guestdest & PMD_MASK) == ((guestdest+n) & PMD_MASK)) {
167                 uptr = (void __user *) gmap_fault(guestdest, vcpu->arch.gmap);
168
169                 if (IS_ERR((void __force *) uptr))
170                         return PTR_ERR((void __force *) uptr);
171
172                 r = copy_to_user(uptr, from, n);
173
174                 if (r)
175                         r = -EFAULT;
176
177                 goto out;
178         }
179
180         /* copy first segment */
181         uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
182
183         if (IS_ERR((void __force *) uptr))
184                 return PTR_ERR((void __force *) uptr);
185
186         size = PMD_SIZE - (guestdest & ~PMD_MASK);
187
188         r = copy_to_user(uptr, from, size);
189
190         if (r) {
191                 r = -EFAULT;
192                 goto out;
193         }
194         from += size;
195         n -= size;
196         guestdest += size;
197
198         /* copy full segments */
199         while (n >= PMD_SIZE) {
200                 uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
201
202                 if (IS_ERR((void __force *) uptr))
203                         return PTR_ERR((void __force *) uptr);
204
205                 r = copy_to_user(uptr, from, PMD_SIZE);
206
207                 if (r) {
208                         r = -EFAULT;
209                         goto out;
210                 }
211                 from += PMD_SIZE;
212                 n -= PMD_SIZE;
213                 guestdest += PMD_SIZE;
214         }
215
216         /* copy the tail segment */
217         if (n) {
218                 uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
219
220                 if (IS_ERR((void __force *) uptr))
221                         return PTR_ERR((void __force *) uptr);
222
223                 r = copy_to_user(uptr, from, n);
224
225                 if (r)
226                         r = -EFAULT;
227         }
228 out:
229         return r;
230 }
231
232 static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
233                                          unsigned long guestdest,
234                                          void *from, unsigned long n)
235 {
236         return __copy_to_guest_fast(vcpu, guestdest, from, n);
237 }
238
239 static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
240                                 void *from, unsigned long n)
241 {
242         unsigned long prefix  = vcpu->arch.sie_block->prefix;
243
244         if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
245                 goto slowpath;
246
247         if ((guestdest < prefix) && (guestdest + n > prefix))
248                 goto slowpath;
249
250         if ((guestdest < prefix + 2 * PAGE_SIZE)
251             && (guestdest + n > prefix + 2 * PAGE_SIZE))
252                 goto slowpath;
253
254         if (guestdest < 2 * PAGE_SIZE)
255                 guestdest += prefix;
256         else if ((guestdest >= prefix) && (guestdest < prefix + 2 * PAGE_SIZE))
257                 guestdest -= prefix;
258
259         return __copy_to_guest_fast(vcpu, guestdest, from, n);
260 slowpath:
261         return __copy_to_guest_slow(vcpu, guestdest, from, n);
262 }
263
264 static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
265                                          unsigned long guestsrc,
266                                          unsigned long n)
267 {
268         int rc;
269         unsigned long i;
270         u8 *data = to;
271
272         for (i = 0; i < n; i++) {
273                 rc = get_guest_u8(vcpu, guestsrc++, data++);
274                 if (rc < 0)
275                         return rc;
276         }
277         return 0;
278 }
279
280 static inline int __copy_from_guest_fast(struct kvm_vcpu *vcpu, void *to,
281                                          unsigned long guestsrc,
282                                          unsigned long n)
283 {
284         int r;
285         void __user *uptr;
286         unsigned long size;
287
288         if (guestsrc + n < guestsrc)
289                 return -EFAULT;
290
291         /* simple case: all within one segment table entry? */
292         if ((guestsrc & PMD_MASK) == ((guestsrc+n) & PMD_MASK)) {
293                 uptr = (void __user *) gmap_fault(guestsrc, vcpu->arch.gmap);
294
295                 if (IS_ERR((void __force *) uptr))
296                         return PTR_ERR((void __force *) uptr);
297
298                 r = copy_from_user(to, uptr, n);
299
300                 if (r)
301                         r = -EFAULT;
302
303                 goto out;
304         }
305
306         /* copy first segment */
307         uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
308
309         if (IS_ERR((void __force *) uptr))
310                 return PTR_ERR((void __force *) uptr);
311
312         size = PMD_SIZE - (guestsrc & ~PMD_MASK);
313
314         r = copy_from_user(to, uptr, size);
315
316         if (r) {
317                 r = -EFAULT;
318                 goto out;
319         }
320         to += size;
321         n -= size;
322         guestsrc += size;
323
324         /* copy full segments */
325         while (n >= PMD_SIZE) {
326                 uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
327
328                 if (IS_ERR((void __force *) uptr))
329                         return PTR_ERR((void __force *) uptr);
330
331                 r = copy_from_user(to, uptr, PMD_SIZE);
332
333                 if (r) {
334                         r = -EFAULT;
335                         goto out;
336                 }
337                 to += PMD_SIZE;
338                 n -= PMD_SIZE;
339                 guestsrc += PMD_SIZE;
340         }
341
342         /* copy the tail segment */
343         if (n) {
344                 uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
345
346                 if (IS_ERR((void __force *) uptr))
347                         return PTR_ERR((void __force *) uptr);
348
349                 r = copy_from_user(to, uptr, n);
350
351                 if (r)
352                         r = -EFAULT;
353         }
354 out:
355         return r;
356 }
357
358 static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
359                                            unsigned long guestsrc,
360                                            unsigned long n)
361 {
362         return __copy_from_guest_fast(vcpu, to, guestsrc, n);
363 }
364
365 static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
366                                   unsigned long guestsrc, unsigned long n)
367 {
368         unsigned long prefix  = vcpu->arch.sie_block->prefix;
369
370         if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
371                 goto slowpath;
372
373         if ((guestsrc < prefix) && (guestsrc + n > prefix))
374                 goto slowpath;
375
376         if ((guestsrc < prefix + 2 * PAGE_SIZE)
377             && (guestsrc + n > prefix + 2 * PAGE_SIZE))
378                 goto slowpath;
379
380         if (guestsrc < 2 * PAGE_SIZE)
381                 guestsrc += prefix;
382         else if ((guestsrc >= prefix) && (guestsrc < prefix + 2 * PAGE_SIZE))
383                 guestsrc -= prefix;
384
385         return __copy_from_guest_fast(vcpu, to, guestsrc, n);
386 slowpath:
387         return __copy_from_guest_slow(vcpu, to, guestsrc, n);
388 }
389 #endif