@@ -1046,10 +1046,12 @@ static int acquire_grant_table(struct domain *d, unsigned int id,
}
static int acquire_resource(
- XEN_GUEST_HANDLE_PARAM(xen_mem_acquire_resource_t) arg)
+ XEN_GUEST_HANDLE_PARAM(xen_mem_acquire_resource_t) arg,
+ unsigned long *start_extent)
{
struct domain *d, *currd = current->domain;
xen_mem_acquire_resource_t xmar;
+ uint32_t total_frames;
/*
* The mfn_list and gfn_list (below) arrays are ok on stack for the
* moment since they are small, but if they need to grow in future
@@ -1069,7 +1071,7 @@ static int acquire_resource(
if ( xmar.nr_frames )
return -EINVAL;
- xmar.nr_frames = ARRAY_SIZE(mfn_list);
+ xmar.nr_frames = UINT_MAX >> MEMOP_EXTENT_SHIFT;
if ( __copy_field_to_guest(arg, &xmar, nr_frames) )
return -EFAULT;
@@ -1077,8 +1079,28 @@ static int acquire_resource(
return 0;
}
+ total_frames = xmar.nr_frames;
+
+ /* Is the size too large for us to encode a continuation? */
+ if ( unlikely(xmar.nr_frames > (UINT_MAX >> MEMOP_EXTENT_SHIFT)) )
+ return -EINVAL;
+
+ if ( *start_extent )
+ {
+ /*
+ * Check whether start_extent is in bounds, as this
+ * value if visible to the calling domain.
+ */
+ if ( *start_extent > xmar.nr_frames )
+ return -EINVAL;
+
+ xmar.frame += *start_extent;
+ xmar.nr_frames -= *start_extent;
+ guest_handle_add_offset(xmar.frame_list, *start_extent);
+ }
+
if ( xmar.nr_frames > ARRAY_SIZE(mfn_list) )
- return -E2BIG;
+ xmar.nr_frames = ARRAY_SIZE(mfn_list);
rc = rcu_lock_remote_domain_by_id(xmar.domid, &d);
if ( rc )
@@ -1135,6 +1157,14 @@ static int acquire_resource(
}
}
+ if ( !rc )
+ {
+ *start_extent += xmar.nr_frames;
+
+ if ( *start_extent != total_frames )
+ rc = -ERESTART;
+ }
+
out:
rcu_unlock_domain(d);
@@ -1599,8 +1629,17 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
#endif
case XENMEM_acquire_resource:
- rc = acquire_resource(
- guest_handle_cast(arg, xen_mem_acquire_resource_t));
+ do {
+ rc = acquire_resource(
+ guest_handle_cast(arg, xen_mem_acquire_resource_t),
+ &start_extent);
+
+ if ( hypercall_preempt_check() )
+ return hypercall_create_continuation(
+ __HYPERVISOR_memory_op, "lh",
+ op | (start_extent << MEMOP_EXTENT_SHIFT), arg);
+ } while ( rc == -ERESTART );
+
break;
default: