82f450ecb5853648f6bec34eb87f26891d124c7a
[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 *__gptr_to_uptr(struct kvm_vcpu *vcpu, void *gptr)
22 {
23         unsigned long prefix  = vcpu->arch.sie_block->prefix;
24         unsigned long gaddr = (unsigned long) gptr;
25         unsigned long uaddr;
26
27         if (gaddr < 2 * PAGE_SIZE)
28                 gaddr += prefix;
29         else if ((gaddr >= prefix) && (gaddr < prefix + 2 * PAGE_SIZE))
30                 gaddr -= prefix;
31         uaddr = gmap_fault(gaddr, vcpu->arch.gmap);
32         if (IS_ERR_VALUE(uaddr))
33                 uaddr = -EFAULT;
34         return (void *)uaddr;
35 }
36
37 #define get_guest(vcpu, x, gptr)                                \
38 ({                                                              \
39         __typeof__(gptr) __uptr = __gptr_to_uptr(vcpu, gptr);   \
40         int __mask = sizeof(__typeof__(*(gptr))) - 1;           \
41         int __ret = PTR_RET(__uptr);                            \
42                                                                 \
43         if (!__ret) {                                           \
44                 BUG_ON((unsigned long)__uptr & __mask);         \
45                 __ret = get_user(x, __uptr);                    \
46         }                                                       \
47         __ret;                                                  \
48 })
49
50 #define put_guest(vcpu, x, gptr)                                \
51 ({                                                              \
52         __typeof__(gptr) __uptr = __gptr_to_uptr(vcpu, gptr);   \
53         int __mask = sizeof(__typeof__(*(gptr))) - 1;           \
54         int __ret = PTR_RET(__uptr);                            \
55                                                                 \
56         if (!__ret) {                                           \
57                 BUG_ON((unsigned long)__uptr & __mask);         \
58                 __ret = put_user(x, __uptr);                    \
59         }                                                       \
60         __ret;                                                  \
61 })
62
63 static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
64                                        unsigned long guestdest,
65                                        void *from, unsigned long n)
66 {
67         int rc;
68         unsigned long i;
69         u8 *data = from;
70
71         for (i = 0; i < n; i++) {
72                 rc = put_guest(vcpu, *(data++), (u8 *)guestdest++);
73                 if (rc < 0)
74                         return rc;
75         }
76         return 0;
77 }
78
79 static inline int __copy_to_guest_fast(struct kvm_vcpu *vcpu,
80                                        unsigned long guestdest,
81                                        void *from, unsigned long n)
82 {
83         int r;
84         void __user *uptr;
85         unsigned long size;
86
87         if (guestdest + n < guestdest)
88                 return -EFAULT;
89
90         /* simple case: all within one segment table entry? */
91         if ((guestdest & PMD_MASK) == ((guestdest+n) & PMD_MASK)) {
92                 uptr = (void __user *) gmap_fault(guestdest, vcpu->arch.gmap);
93
94                 if (IS_ERR((void __force *) uptr))
95                         return PTR_ERR((void __force *) uptr);
96
97                 r = copy_to_user(uptr, from, n);
98
99                 if (r)
100                         r = -EFAULT;
101
102                 goto out;
103         }
104
105         /* copy first segment */
106         uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
107
108         if (IS_ERR((void __force *) uptr))
109                 return PTR_ERR((void __force *) uptr);
110
111         size = PMD_SIZE - (guestdest & ~PMD_MASK);
112
113         r = copy_to_user(uptr, from, size);
114
115         if (r) {
116                 r = -EFAULT;
117                 goto out;
118         }
119         from += size;
120         n -= size;
121         guestdest += size;
122
123         /* copy full segments */
124         while (n >= PMD_SIZE) {
125                 uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
126
127                 if (IS_ERR((void __force *) uptr))
128                         return PTR_ERR((void __force *) uptr);
129
130                 r = copy_to_user(uptr, from, PMD_SIZE);
131
132                 if (r) {
133                         r = -EFAULT;
134                         goto out;
135                 }
136                 from += PMD_SIZE;
137                 n -= PMD_SIZE;
138                 guestdest += PMD_SIZE;
139         }
140
141         /* copy the tail segment */
142         if (n) {
143                 uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
144
145                 if (IS_ERR((void __force *) uptr))
146                         return PTR_ERR((void __force *) uptr);
147
148                 r = copy_to_user(uptr, from, n);
149
150                 if (r)
151                         r = -EFAULT;
152         }
153 out:
154         return r;
155 }
156
157 static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
158                                          unsigned long guestdest,
159                                          void *from, unsigned long n)
160 {
161         return __copy_to_guest_fast(vcpu, guestdest, from, n);
162 }
163
164 static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
165                                 void *from, unsigned long n)
166 {
167         unsigned long prefix  = vcpu->arch.sie_block->prefix;
168
169         if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
170                 goto slowpath;
171
172         if ((guestdest < prefix) && (guestdest + n > prefix))
173                 goto slowpath;
174
175         if ((guestdest < prefix + 2 * PAGE_SIZE)
176             && (guestdest + n > prefix + 2 * PAGE_SIZE))
177                 goto slowpath;
178
179         if (guestdest < 2 * PAGE_SIZE)
180                 guestdest += prefix;
181         else if ((guestdest >= prefix) && (guestdest < prefix + 2 * PAGE_SIZE))
182                 guestdest -= prefix;
183
184         return __copy_to_guest_fast(vcpu, guestdest, from, n);
185 slowpath:
186         return __copy_to_guest_slow(vcpu, guestdest, from, n);
187 }
188
189 static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
190                                          unsigned long guestsrc,
191                                          unsigned long n)
192 {
193         int rc;
194         unsigned long i;
195         u8 *data = to;
196
197         for (i = 0; i < n; i++) {
198                 rc = get_guest(vcpu, *(data++), (u8 *)guestsrc++);
199                 if (rc < 0)
200                         return rc;
201         }
202         return 0;
203 }
204
205 static inline int __copy_from_guest_fast(struct kvm_vcpu *vcpu, void *to,
206                                          unsigned long guestsrc,
207                                          unsigned long n)
208 {
209         int r;
210         void __user *uptr;
211         unsigned long size;
212
213         if (guestsrc + n < guestsrc)
214                 return -EFAULT;
215
216         /* simple case: all within one segment table entry? */
217         if ((guestsrc & PMD_MASK) == ((guestsrc+n) & PMD_MASK)) {
218                 uptr = (void __user *) gmap_fault(guestsrc, vcpu->arch.gmap);
219
220                 if (IS_ERR((void __force *) uptr))
221                         return PTR_ERR((void __force *) uptr);
222
223                 r = copy_from_user(to, uptr, n);
224
225                 if (r)
226                         r = -EFAULT;
227
228                 goto out;
229         }
230
231         /* copy first segment */
232         uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
233
234         if (IS_ERR((void __force *) uptr))
235                 return PTR_ERR((void __force *) uptr);
236
237         size = PMD_SIZE - (guestsrc & ~PMD_MASK);
238
239         r = copy_from_user(to, uptr, size);
240
241         if (r) {
242                 r = -EFAULT;
243                 goto out;
244         }
245         to += size;
246         n -= size;
247         guestsrc += size;
248
249         /* copy full segments */
250         while (n >= PMD_SIZE) {
251                 uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
252
253                 if (IS_ERR((void __force *) uptr))
254                         return PTR_ERR((void __force *) uptr);
255
256                 r = copy_from_user(to, uptr, PMD_SIZE);
257
258                 if (r) {
259                         r = -EFAULT;
260                         goto out;
261                 }
262                 to += PMD_SIZE;
263                 n -= PMD_SIZE;
264                 guestsrc += PMD_SIZE;
265         }
266
267         /* copy the tail segment */
268         if (n) {
269                 uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
270
271                 if (IS_ERR((void __force *) uptr))
272                         return PTR_ERR((void __force *) uptr);
273
274                 r = copy_from_user(to, uptr, n);
275
276                 if (r)
277                         r = -EFAULT;
278         }
279 out:
280         return r;
281 }
282
283 static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
284                                            unsigned long guestsrc,
285                                            unsigned long n)
286 {
287         return __copy_from_guest_fast(vcpu, to, guestsrc, n);
288 }
289
290 static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
291                                   unsigned long guestsrc, unsigned long n)
292 {
293         unsigned long prefix  = vcpu->arch.sie_block->prefix;
294
295         if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
296                 goto slowpath;
297
298         if ((guestsrc < prefix) && (guestsrc + n > prefix))
299                 goto slowpath;
300
301         if ((guestsrc < prefix + 2 * PAGE_SIZE)
302             && (guestsrc + n > prefix + 2 * PAGE_SIZE))
303                 goto slowpath;
304
305         if (guestsrc < 2 * PAGE_SIZE)
306                 guestsrc += prefix;
307         else if ((guestsrc >= prefix) && (guestsrc < prefix + 2 * PAGE_SIZE))
308                 guestsrc -= prefix;
309
310         return __copy_from_guest_fast(vcpu, to, guestsrc, n);
311 slowpath:
312         return __copy_from_guest_slow(vcpu, to, guestsrc, n);
313 }
314 #endif