diff options
Diffstat (limited to 'drivers/xen/privcmd.c')
-rw-r--r-- | drivers/xen/privcmd.c | 89 |
1 files changed, 47 insertions, 42 deletions
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 0bbbccbb1f12..ca2b00e9d558 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c | |||
@@ -199,9 +199,6 @@ static long privcmd_ioctl_mmap(void __user *udata) | |||
199 | LIST_HEAD(pagelist); | 199 | LIST_HEAD(pagelist); |
200 | struct mmap_mfn_state state; | 200 | struct mmap_mfn_state state; |
201 | 201 | ||
202 | if (!xen_initial_domain()) | ||
203 | return -EPERM; | ||
204 | |||
205 | /* We only support privcmd_ioctl_mmap_batch for auto translated. */ | 202 | /* We only support privcmd_ioctl_mmap_batch for auto translated. */ |
206 | if (xen_feature(XENFEAT_auto_translated_physmap)) | 203 | if (xen_feature(XENFEAT_auto_translated_physmap)) |
207 | return -ENOSYS; | 204 | return -ENOSYS; |
@@ -261,11 +258,12 @@ struct mmap_batch_state { | |||
261 | * -ENOENT if at least 1 -ENOENT has happened. | 258 | * -ENOENT if at least 1 -ENOENT has happened. |
262 | */ | 259 | */ |
263 | int global_error; | 260 | int global_error; |
264 | /* An array for individual errors */ | 261 | int version; |
265 | int *err; | ||
266 | 262 | ||
267 | /* User-space mfn array to store errors in the second pass for V1. */ | 263 | /* User-space mfn array to store errors in the second pass for V1. */ |
268 | xen_pfn_t __user *user_mfn; | 264 | xen_pfn_t __user *user_mfn; |
265 | /* User-space int array to store errors in the second pass for V2. */ | ||
266 | int __user *user_err; | ||
269 | }; | 267 | }; |
270 | 268 | ||
271 | /* auto translated dom0 note: if domU being created is PV, then mfn is | 269 | /* auto translated dom0 note: if domU being created is PV, then mfn is |
@@ -288,7 +286,19 @@ static int mmap_batch_fn(void *data, void *state) | |||
288 | &cur_page); | 286 | &cur_page); |
289 | 287 | ||
290 | /* Store error code for second pass. */ | 288 | /* Store error code for second pass. */ |
291 | *(st->err++) = ret; | 289 | if (st->version == 1) { |
290 | if (ret < 0) { | ||
291 | /* | ||
292 | * V1 encodes the error codes in the 32bit top nibble of the | ||
293 | * mfn (with its known limitations vis-a-vis 64 bit callers). | ||
294 | */ | ||
295 | *mfnp |= (ret == -ENOENT) ? | ||
296 | PRIVCMD_MMAPBATCH_PAGED_ERROR : | ||
297 | PRIVCMD_MMAPBATCH_MFN_ERROR; | ||
298 | } | ||
299 | } else { /* st->version == 2 */ | ||
300 | *((int *) mfnp) = ret; | ||
301 | } | ||
292 | 302 | ||
293 | /* And see if it affects the global_error. */ | 303 | /* And see if it affects the global_error. */ |
294 | if (ret < 0) { | 304 | if (ret < 0) { |
@@ -305,20 +315,25 @@ static int mmap_batch_fn(void *data, void *state) | |||
305 | return 0; | 315 | return 0; |
306 | } | 316 | } |
307 | 317 | ||
308 | static int mmap_return_errors_v1(void *data, void *state) | 318 | static int mmap_return_errors(void *data, void *state) |
309 | { | 319 | { |
310 | xen_pfn_t *mfnp = data; | ||
311 | struct mmap_batch_state *st = state; | 320 | struct mmap_batch_state *st = state; |
312 | int err = *(st->err++); | ||
313 | 321 | ||
314 | /* | 322 | if (st->version == 1) { |
315 | * V1 encodes the error codes in the 32bit top nibble of the | 323 | xen_pfn_t mfnp = *((xen_pfn_t *) data); |
316 | * mfn (with its known limitations vis-a-vis 64 bit callers). | 324 | if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR) |
317 | */ | 325 | return __put_user(mfnp, st->user_mfn++); |
318 | *mfnp |= (err == -ENOENT) ? | 326 | else |
319 | PRIVCMD_MMAPBATCH_PAGED_ERROR : | 327 | st->user_mfn++; |
320 | PRIVCMD_MMAPBATCH_MFN_ERROR; | 328 | } else { /* st->version == 2 */ |
321 | return __put_user(*mfnp, st->user_mfn++); | 329 | int err = *((int *) data); |
330 | if (err) | ||
331 | return __put_user(err, st->user_err++); | ||
332 | else | ||
333 | st->user_err++; | ||
334 | } | ||
335 | |||
336 | return 0; | ||
322 | } | 337 | } |
323 | 338 | ||
324 | /* Allocate pfns that are then mapped with gmfns from foreign domid. Update | 339 | /* Allocate pfns that are then mapped with gmfns from foreign domid. Update |
@@ -357,12 +372,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) | |||
357 | struct vm_area_struct *vma; | 372 | struct vm_area_struct *vma; |
358 | unsigned long nr_pages; | 373 | unsigned long nr_pages; |
359 | LIST_HEAD(pagelist); | 374 | LIST_HEAD(pagelist); |
360 | int *err_array = NULL; | ||
361 | struct mmap_batch_state state; | 375 | struct mmap_batch_state state; |
362 | 376 | ||
363 | if (!xen_initial_domain()) | ||
364 | return -EPERM; | ||
365 | |||
366 | switch (version) { | 377 | switch (version) { |
367 | case 1: | 378 | case 1: |
368 | if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) | 379 | if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) |
@@ -396,10 +407,12 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) | |||
396 | goto out; | 407 | goto out; |
397 | } | 408 | } |
398 | 409 | ||
399 | err_array = kcalloc(m.num, sizeof(int), GFP_KERNEL); | 410 | if (version == 2) { |
400 | if (err_array == NULL) { | 411 | /* Zero error array now to only copy back actual errors. */ |
401 | ret = -ENOMEM; | 412 | if (clear_user(m.err, sizeof(int) * m.num)) { |
402 | goto out; | 413 | ret = -EFAULT; |
414 | goto out; | ||
415 | } | ||
403 | } | 416 | } |
404 | 417 | ||
405 | down_write(&mm->mmap_sem); | 418 | down_write(&mm->mmap_sem); |
@@ -427,7 +440,7 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) | |||
427 | state.va = m.addr; | 440 | state.va = m.addr; |
428 | state.index = 0; | 441 | state.index = 0; |
429 | state.global_error = 0; | 442 | state.global_error = 0; |
430 | state.err = err_array; | 443 | state.version = version; |
431 | 444 | ||
432 | /* mmap_batch_fn guarantees ret == 0 */ | 445 | /* mmap_batch_fn guarantees ret == 0 */ |
433 | BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), | 446 | BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), |
@@ -435,21 +448,14 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) | |||
435 | 448 | ||
436 | up_write(&mm->mmap_sem); | 449 | up_write(&mm->mmap_sem); |
437 | 450 | ||
438 | if (version == 1) { | 451 | if (state.global_error) { |
439 | if (state.global_error) { | 452 | /* Write back errors in second pass. */ |
440 | /* Write back errors in second pass. */ | 453 | state.user_mfn = (xen_pfn_t *)m.arr; |
441 | state.user_mfn = (xen_pfn_t *)m.arr; | 454 | state.user_err = m.err; |
442 | state.err = err_array; | 455 | ret = traverse_pages(m.num, sizeof(xen_pfn_t), |
443 | ret = traverse_pages(m.num, sizeof(xen_pfn_t), | 456 | &pagelist, mmap_return_errors, &state); |
444 | &pagelist, mmap_return_errors_v1, &state); | 457 | } else |
445 | } else | 458 | ret = 0; |
446 | ret = 0; | ||
447 | |||
448 | } else if (version == 2) { | ||
449 | ret = __copy_to_user(m.err, err_array, m.num * sizeof(int)); | ||
450 | if (ret) | ||
451 | ret = -EFAULT; | ||
452 | } | ||
453 | 459 | ||
454 | /* If we have not had any EFAULT-like global errors then set the global | 460 | /* If we have not had any EFAULT-like global errors then set the global |
455 | * error to -ENOENT if necessary. */ | 461 | * error to -ENOENT if necessary. */ |
@@ -457,7 +463,6 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) | |||
457 | ret = -ENOENT; | 463 | ret = -ENOENT; |
458 | 464 | ||
459 | out: | 465 | out: |
460 | kfree(err_array); | ||
461 | free_page_list(&pagelist); | 466 | free_page_list(&pagelist); |
462 | 467 | ||
463 | return ret; | 468 | return ret; |