ia64/xen-unstable

view xen/common/compat/memory.c @ 17858:12ae02c09d1e

32-on-64: Fix error handling for XENMEM_decrease_reservation.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 13 13:47:28 2008 +0100 (2008-06-13)
parents ebbd0e8c3e72
children 2737293c761e
line source
1 #include <xen/config.h>
2 #include <xen/types.h>
3 #include <xen/hypercall.h>
4 #include <xen/guest_access.h>
5 #include <xen/sched.h>
6 #include <xen/event.h>
7 #include <asm/current.h>
8 #include <compat/memory.h>
10 int compat_memory_op(unsigned int cmd, XEN_GUEST_HANDLE(void) compat)
11 {
12 int rc, split, op = cmd & MEMOP_CMD_MASK;
13 unsigned int start_extent = cmd >> MEMOP_EXTENT_SHIFT;
15 do
16 {
17 unsigned int i, end_extent = 0;
18 union {
19 XEN_GUEST_HANDLE(void) hnd;
20 struct xen_memory_reservation *rsrv;
21 struct xen_memory_exchange *xchg;
22 struct xen_translate_gpfn_list *xlat;
23 } nat;
24 union {
25 struct compat_memory_reservation rsrv;
26 struct compat_memory_exchange xchg;
27 struct compat_translate_gpfn_list xlat;
28 } cmp;
30 set_xen_guest_handle(nat.hnd, (void *)COMPAT_ARG_XLAT_VIRT_BASE);
31 split = 0;
32 switch ( op )
33 {
34 xen_pfn_t *space;
36 case XENMEM_increase_reservation:
37 case XENMEM_decrease_reservation:
38 case XENMEM_populate_physmap:
39 if ( copy_from_guest(&cmp.rsrv, compat, 1) )
40 return start_extent;
42 /* Is size too large for us to encode a continuation? */
43 if ( cmp.rsrv.nr_extents > (UINT_MAX >> MEMOP_EXTENT_SHIFT) )
44 return start_extent;
46 if ( !compat_handle_is_null(cmp.rsrv.extent_start) &&
47 !compat_handle_okay(cmp.rsrv.extent_start, cmp.rsrv.nr_extents) )
48 return start_extent;
50 end_extent = start_extent + (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.rsrv)) /
51 sizeof(*space);
52 if ( end_extent > cmp.rsrv.nr_extents )
53 end_extent = cmp.rsrv.nr_extents;
55 space = (xen_pfn_t *)(nat.rsrv + 1);
56 #define XLAT_memory_reservation_HNDL_extent_start(_d_, _s_) \
57 do \
58 { \
59 if ( !compat_handle_is_null((_s_)->extent_start) ) \
60 { \
61 set_xen_guest_handle((_d_)->extent_start, space - start_extent); \
62 if ( op != XENMEM_increase_reservation ) \
63 { \
64 for ( i = start_extent; i < end_extent; ++i ) \
65 { \
66 compat_pfn_t pfn; \
67 if ( __copy_from_compat_offset(&pfn, (_s_)->extent_start, i, 1) ) \
68 { \
69 end_extent = i; \
70 split = -1; \
71 break; \
72 } \
73 *space++ = pfn; \
74 } \
75 } \
76 } \
77 else \
78 { \
79 set_xen_guest_handle((_d_)->extent_start, NULL); \
80 end_extent = cmp.rsrv.nr_extents; \
81 } \
82 } while (0)
83 XLAT_memory_reservation(nat.rsrv, &cmp.rsrv);
84 #undef XLAT_memory_reservation_HNDL_extent_start
86 if ( end_extent < cmp.rsrv.nr_extents )
87 {
88 nat.rsrv->nr_extents = end_extent;
89 ++split;
90 }
92 break;
94 case XENMEM_exchange:
95 {
96 int order_delta;
98 if ( copy_from_guest(&cmp.xchg, compat, 1) )
99 return -EFAULT;
101 order_delta = cmp.xchg.out.extent_order - cmp.xchg.in.extent_order;
102 /* Various sanity checks. */
103 if ( (cmp.xchg.nr_exchanged > cmp.xchg.in.nr_extents) ||
104 (order_delta > 0 && (cmp.xchg.nr_exchanged & ((1U << order_delta) - 1))) ||
105 /* Sizes of input and output lists do not overflow an int? */
106 ((~0U >> cmp.xchg.in.extent_order) < cmp.xchg.in.nr_extents) ||
107 ((~0U >> cmp.xchg.out.extent_order) < cmp.xchg.out.nr_extents) ||
108 /* Sizes of input and output lists match? */
109 ((cmp.xchg.in.nr_extents << cmp.xchg.in.extent_order) !=
110 (cmp.xchg.out.nr_extents << cmp.xchg.out.extent_order)) )
111 return -EINVAL;
113 start_extent = cmp.xchg.nr_exchanged;
114 end_extent = (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.xchg)) /
115 (((1U << __builtin_abs(order_delta)) + 1) *
116 sizeof(*space));
117 if ( end_extent == 0 )
118 {
119 printk("Cannot translate compatibility mode XENMEM_exchange extents (%u,%u)\n",
120 cmp.xchg.in.extent_order, cmp.xchg.out.extent_order);
121 return -E2BIG;
122 }
123 if ( order_delta > 0 )
124 end_extent <<= order_delta;
125 end_extent += start_extent;
126 if ( end_extent > cmp.xchg.in.nr_extents )
127 end_extent = cmp.xchg.in.nr_extents;
129 space = (xen_pfn_t *)(nat.xchg + 1);
130 /* Code below depends upon .in preceding .out. */
131 BUILD_BUG_ON(offsetof(xen_memory_exchange_t, in) > offsetof(xen_memory_exchange_t, out));
132 #define XLAT_memory_reservation_HNDL_extent_start(_d_, _s_) \
133 do \
134 { \
135 set_xen_guest_handle((_d_)->extent_start, space - start_extent); \
136 for ( i = start_extent; i < end_extent; ++i ) \
137 { \
138 compat_pfn_t pfn; \
139 if ( __copy_from_compat_offset(&pfn, (_s_)->extent_start, i, 1) ) \
140 return -EFAULT; \
141 *space++ = pfn; \
142 } \
143 if ( order_delta > 0 ) \
144 { \
145 start_extent >>= order_delta; \
146 end_extent >>= order_delta; \
147 } \
148 else \
149 { \
150 start_extent <<= -order_delta; \
151 end_extent <<= -order_delta; \
152 } \
153 order_delta = -order_delta; \
154 } while (0)
155 XLAT_memory_exchange(nat.xchg, &cmp.xchg);
156 #undef XLAT_memory_reservation_HNDL_extent_start
158 if ( end_extent < cmp.xchg.in.nr_extents )
159 {
160 nat.xchg->in.nr_extents = end_extent;
161 if ( order_delta >= 0 )
162 nat.xchg->out.nr_extents = end_extent >> order_delta;
163 else
164 nat.xchg->out.nr_extents = end_extent << order_delta;
165 ++split;
166 }
168 break;
169 }
171 case XENMEM_current_reservation:
172 case XENMEM_maximum_reservation:
173 case XENMEM_maximum_gpfn:
174 {
175 #define xen_domid_t domid_t
176 #define compat_domid_t domid_compat_t
177 CHECK_TYPE(domid);
178 #undef compat_domid_t
179 #undef xen_domid_t
180 }
181 case XENMEM_maximum_ram_page:
182 nat.hnd = compat;
183 break;
185 case XENMEM_translate_gpfn_list:
186 if ( copy_from_guest(&cmp.xlat, compat, 1) )
187 return -EFAULT;
189 /* Is size too large for us to encode a continuation? */
190 if ( cmp.xlat.nr_gpfns > (UINT_MAX >> MEMOP_EXTENT_SHIFT) )
191 return -EINVAL;
193 if ( !compat_handle_okay(cmp.xlat.gpfn_list, cmp.xlat.nr_gpfns) ||
194 !compat_handle_okay(cmp.xlat.mfn_list, cmp.xlat.nr_gpfns) )
195 return -EFAULT;
197 end_extent = start_extent + (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.xlat)) /
198 sizeof(*space);
199 if ( end_extent > cmp.xlat.nr_gpfns )
200 end_extent = cmp.xlat.nr_gpfns;
202 space = (xen_pfn_t *)(nat.xlat + 1);
203 /* Code below depends upon .gpfn_list preceding .mfn_list. */
204 BUILD_BUG_ON(offsetof(xen_translate_gpfn_list_t, gpfn_list) > offsetof(xen_translate_gpfn_list_t, mfn_list));
205 #define XLAT_translate_gpfn_list_HNDL_gpfn_list(_d_, _s_) \
206 do \
207 { \
208 set_xen_guest_handle((_d_)->gpfn_list, space - start_extent); \
209 for ( i = start_extent; i < end_extent; ++i ) \
210 { \
211 compat_pfn_t pfn; \
212 if ( __copy_from_compat_offset(&pfn, (_s_)->gpfn_list, i, 1) ) \
213 return -EFAULT; \
214 *space++ = pfn; \
215 } \
216 } while (0)
217 #define XLAT_translate_gpfn_list_HNDL_mfn_list(_d_, _s_) \
218 (_d_)->mfn_list = (_d_)->gpfn_list
219 XLAT_translate_gpfn_list(nat.xlat, &cmp.xlat);
220 #undef XLAT_translate_gpfn_list_HNDL_mfn_list
221 #undef XLAT_translate_gpfn_list_HNDL_gpfn_list
223 if ( end_extent < cmp.xlat.nr_gpfns )
224 {
225 nat.xlat->nr_gpfns = end_extent;
226 ++split;
227 }
229 break;
231 default:
232 return compat_arch_memory_op(cmd, compat);
233 }
235 rc = do_memory_op(cmd, nat.hnd);
236 if ( rc < 0 )
237 return rc;
239 cmd = 0;
240 if ( hypercall_xlat_continuation(&cmd, 0x02, nat.hnd, compat) )
241 {
242 BUG_ON(rc != __HYPERVISOR_memory_op);
243 BUG_ON((cmd & MEMOP_CMD_MASK) != op);
244 split = -1;
245 }
247 switch ( op )
248 {
249 case XENMEM_increase_reservation:
250 case XENMEM_decrease_reservation:
251 case XENMEM_populate_physmap:
252 end_extent = split >= 0 ? rc : cmd >> MEMOP_EXTENT_SHIFT;
253 if ( (op != XENMEM_decrease_reservation) &&
254 !guest_handle_is_null(nat.rsrv->extent_start) )
255 {
256 for ( ; start_extent < end_extent; ++start_extent )
257 {
258 compat_pfn_t pfn = nat.rsrv->extent_start.p[start_extent];
260 BUG_ON(pfn != nat.rsrv->extent_start.p[start_extent]);
261 if ( __copy_to_compat_offset(cmp.rsrv.extent_start,
262 start_extent, &pfn, 1) )
263 {
264 if ( split >= 0 )
265 {
266 rc = start_extent;
267 split = 0;
268 }
269 else
270 /*
271 * Short of being able to cancel the continuation,
272 * force it to restart here; eventually we shall
273 * get out of this state.
274 */
275 rc = (start_extent << MEMOP_EXTENT_SHIFT) | op;
276 break;
277 }
278 }
279 }
280 else
281 {
282 start_extent = end_extent;
283 }
284 /* Bail if there was an error. */
285 if ( (split >= 0) && (end_extent != nat.rsrv->nr_extents) )
286 split = 0;
287 break;
289 case XENMEM_exchange:
290 {
291 DEFINE_XEN_GUEST_HANDLE(compat_memory_exchange_t);
292 int order_delta;
294 BUG_ON(split >= 0 && rc);
295 BUG_ON(end_extent < nat.xchg->nr_exchanged);
296 end_extent = nat.xchg->nr_exchanged;
298 order_delta = cmp.xchg.out.extent_order - cmp.xchg.in.extent_order;
299 if ( order_delta > 0 )
300 {
301 start_extent >>= order_delta;
302 BUG_ON(end_extent & ((1U << order_delta) - 1));
303 end_extent >>= order_delta;
304 }
305 else
306 {
307 start_extent <<= -order_delta;
308 end_extent <<= -order_delta;
309 }
311 for ( ; start_extent < end_extent; ++start_extent )
312 {
313 compat_pfn_t pfn = nat.xchg->out.extent_start.p[start_extent];
315 BUG_ON(pfn != nat.xchg->out.extent_start.p[start_extent]);
316 /* Note that we ignore errors accessing the output extent list. */
317 __copy_to_compat_offset(cmp.xchg.out.extent_start, start_extent, &pfn, 1);
318 }
320 cmp.xchg.nr_exchanged = nat.xchg->nr_exchanged;
321 if ( copy_field_to_guest(guest_handle_cast(compat, compat_memory_exchange_t),
322 &cmp.xchg, nr_exchanged) )
323 {
324 if ( split < 0 )
325 /* Cannot cancel the continuation... */
326 domain_crash(current->domain);
327 return -EFAULT;
328 }
329 break;
330 }
332 case XENMEM_maximum_ram_page:
333 case XENMEM_current_reservation:
334 case XENMEM_maximum_reservation:
335 case XENMEM_maximum_gpfn:
336 break;
338 case XENMEM_translate_gpfn_list:
339 if ( split < 0 )
340 end_extent = cmd >> MEMOP_EXTENT_SHIFT;
341 else
342 BUG_ON(rc);
344 for ( ; start_extent < end_extent; ++start_extent )
345 {
346 compat_pfn_t pfn = nat.xlat->mfn_list.p[start_extent];
348 BUG_ON(pfn != nat.xlat->mfn_list.p[start_extent]);
349 if ( __copy_to_compat_offset(cmp.xlat.mfn_list, start_extent, &pfn, 1) )
350 {
351 if ( split < 0 )
352 /* Cannot cancel the continuation... */
353 domain_crash(current->domain);
354 return -EFAULT;
355 }
356 }
357 break;
359 default:
360 domain_crash(current->domain);
361 split = 0;
362 break;
363 }
365 cmd = op | (start_extent << MEMOP_EXTENT_SHIFT);
366 if ( split > 0 && hypercall_preempt_check() )
367 return hypercall_create_continuation(
368 __HYPERVISOR_memory_op, "ih", cmd, compat);
369 } while ( split > 0 );
371 return rc;
372 }