diff mbox series

[v4,4/5] xen/memory, tools: Avoid hardcoding GUEST_MAGIC_BASE in init-dom0less

Message ID 20240409045357.236802-5-xin.wang2@amd.com (mailing list archive)
State New
Headers show
Series DOMCTL-based guest magic region allocation for 11 domUs | expand

Commit Message

Henry Wang April 9, 2024, 4:53 a.m. UTC
Currently the GUEST_MAGIC_BASE in the init-dom0less application is
hardcoded, which will lead to failures for 1:1 direct-mapped Dom0less
DomUs.

Instead of hardcoding the guest magic pages region, use the
XEN_DOMCTL_get_mem_map domctl to get the start address of the guest
magic pages region. Add a new sub-op XENMEM_populate_physmap_heap_alloc
and the MEMF_force_heap_alloc flag to force populate_physmap() to
allocate page from domheap instead of using 1:1 or static allocated
pages to map the magic pages.

Reported-by: Alec Kwapis <alec.kwapis@medtronic.com>
Signed-off-by: Henry Wang <xin.wang2@amd.com>
---
v4:
- Use the new subop.
- Add assert to check the flag isn't already set when coming back from
  construct_memop_from_reservation().
- Use &= to clear the flag instead of clear_bit().
- Move the alias to xen/common/memory.c
v3:
- Don't ignore the error from xc_get_domain_mem_map().
- Re-purposing the _MEMF_no_refcount as _MEMF_force_heap_alloc to
  avoid introduction of a new, single-use flag.
- Reject other reservation sub-ops to use the newly added flag.
- Replace all the GUEST_MAGIC_BASE usages.
v2:
- New patch
---
 tools/helpers/init-dom0less.c | 35 +++++++++++-----
 tools/include/xenctrl.h       |  7 ++++
 tools/libs/ctrl/xc_domain.c   | 79 ++++++++++++++++++++++++++---------
 xen/common/memory.c           | 30 +++++++++++--
 xen/include/public/memory.h   |  3 +-
 5 files changed, 120 insertions(+), 34 deletions(-)

Comments

Jan Beulich April 18, 2024, 12:54 p.m. UTC | #1
On 09.04.2024 06:53, Henry Wang wrote:
> --- a/xen/common/memory.c
> +++ b/xen/common/memory.c
> @@ -155,6 +155,14 @@ static void increase_reservation(struct memop_args *a)
>      a->nr_done = i;
>  }
>  
> +/*
> + * Alias of _MEMF_no_refcount to avoid introduction of a new, single-use flag.
> + * This flag should be used for populate_physmap() only as a re-purposing of
> + * _MEMF_no_refcount to force a non-1:1 allocation from domheap.
> + */
> +#define _MEMF_force_heap_alloc _MEMF_no_refcount
> +#define  MEMF_force_heap_alloc (1U<<_MEMF_force_heap_alloc)

Nit (style): Blanks around << please.

Also do you really need both constants? I dont think so.

Plus please make sure to #undef the constant once no longer needed, to
help spotting / avoiding misuses.

> @@ -219,7 +227,8 @@ static void populate_physmap(struct memop_args *a)
>          }
>          else
>          {
> -            if ( is_domain_direct_mapped(d) )
> +            if ( is_domain_direct_mapped(d) &&
> +                 !(a->memflags & MEMF_force_heap_alloc) )
>              {
>                  mfn = _mfn(gpfn);
>  
> @@ -246,7 +255,8 @@ static void populate_physmap(struct memop_args *a)
>  
>                  mfn = _mfn(gpfn);
>              }
> -            else if ( is_domain_using_staticmem(d) )
> +            else if ( is_domain_using_staticmem(d) &&
> +                      !(a->memflags & MEMF_force_heap_alloc) )
>              {
>                  /*
>                   * No easy way to guarantee the retrieved pages are contiguous,
> @@ -271,6 +281,14 @@ static void populate_physmap(struct memop_args *a)
>              }
>              else
>              {
> +                /*
> +                 * Avoid passing MEMF_force_heap_alloc down to
> +                 * alloc_domheap_pages() where the meaning would be the
> +                 * original MEMF_no_refcount.
> +                 */
> +                if ( unlikely(a->memflags & MEMF_force_heap_alloc) )
> +                    a->memflags &= ~MEMF_force_heap_alloc;

As asked before: Why the if()?

> @@ -1404,6 +1422,7 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>      {
>      case XENMEM_increase_reservation:
>      case XENMEM_decrease_reservation:
> +    case XENMEM_populate_physmap_heap_alloc:
>      case XENMEM_populate_physmap:
>          if ( copy_from_guest(&reservation, arg, 1) )
>              return start_extent;

Nit or not: Please insert the new case label last.

> @@ -1433,6 +1452,11 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>               && (reservation.mem_flags & XENMEMF_populate_on_demand) )
>              args.memflags |= MEMF_populate_on_demand;
>  
> +        /* Assert flag is not set from construct_memop_from_reservation(). */
> +        ASSERT(!(args.memflags & MEMF_force_heap_alloc));
> +        if ( op == XENMEM_populate_physmap_heap_alloc )
> +            args.memflags |= MEMF_force_heap_alloc;

Wouldn't this more logically live ...

>          if ( xsm_memory_adjust_reservation(XSM_TARGET, curr_d, d) )
>          {
>              rcu_unlock_domain(d);
> @@ -1453,7 +1477,7 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>          case XENMEM_decrease_reservation:
>              decrease_reservation(&args);
>              break;

here, as

          case XENMEM_populate_physmap_heap_alloc:
              ...
              fallthrough;
> -        default: /* XENMEM_populate_physmap */
> +        default: /* XENMEM_populate_{physmap, physmap_heap_alloc} */

Otherwise: Just XENMEM_populate_physmap{,_heap_alloc} perhaps?

> --- a/xen/include/public/memory.h
> +++ b/xen/include/public/memory.h
> @@ -21,6 +21,7 @@
>  #define XENMEM_increase_reservation 0
>  #define XENMEM_decrease_reservation 1
>  #define XENMEM_populate_physmap     6
> +#define XENMEM_populate_physmap_heap_alloc 29

Without a comment, how is one supposed to know what the difference is of
this new sub-op compared to the "normal" one? I actually wonder whether
referring to a Xen internal (allocation requested to come from the heap)
is actually a good idea here. I'm inclined to suggest to name this after
the purpose it has from the guest or tool stack perspective.

Speaking of which: Is this supposed to be guest-accessible, or is it
intended for tool-stack use only (I have to admit I don't even know where
init-dom0less actually runs)? In the latter case that also wants enforcing.
This may require an adjustment to the XSM hook in use here. Cc-ing Daniel
for possible advice.

Jan
Henry Wang April 19, 2024, 2:31 a.m. UTC | #2
Hi Jan,

On 4/18/2024 8:54 PM, Jan Beulich wrote:
> On 09.04.2024 06:53, Henry Wang wrote:
>> --- a/xen/common/memory.c
>> +++ b/xen/common/memory.c
>> @@ -155,6 +155,14 @@ static void increase_reservation(struct memop_args *a)
>>       a->nr_done = i;
>>   }
>>   
>> +/*
>> + * Alias of _MEMF_no_refcount to avoid introduction of a new, single-use flag.
>> + * This flag should be used for populate_physmap() only as a re-purposing of
>> + * _MEMF_no_refcount to force a non-1:1 allocation from domheap.
>> + */
>> +#define _MEMF_force_heap_alloc _MEMF_no_refcount
>> +#define  MEMF_force_heap_alloc (1U<<_MEMF_force_heap_alloc)
> Nit (style): Blanks around << please.
>
> Also do you really need both constants? I dont think so.
>
> Plus please make sure to #undef the constant once no longer needed, to
> help spotting / avoiding misuses.

Sounds good, I will fix the NIT, drop the first #define and properly add 
#undef.

>> @@ -219,7 +227,8 @@ static void populate_physmap(struct memop_args *a)
>>           }
>>           else
>>           {
>> -            if ( is_domain_direct_mapped(d) )
>> +            if ( is_domain_direct_mapped(d) &&
>> +                 !(a->memflags & MEMF_force_heap_alloc) )
>>               {
>>                   mfn = _mfn(gpfn);
>>   
>> @@ -246,7 +255,8 @@ static void populate_physmap(struct memop_args *a)
>>   
>>                   mfn = _mfn(gpfn);
>>               }
>> -            else if ( is_domain_using_staticmem(d) )
>> +            else if ( is_domain_using_staticmem(d) &&
>> +                      !(a->memflags & MEMF_force_heap_alloc) )
>>               {
>>                   /*
>>                    * No easy way to guarantee the retrieved pages are contiguous,
>> @@ -271,6 +281,14 @@ static void populate_physmap(struct memop_args *a)
>>               }
>>               else
>>               {
>> +                /*
>> +                 * Avoid passing MEMF_force_heap_alloc down to
>> +                 * alloc_domheap_pages() where the meaning would be the
>> +                 * original MEMF_no_refcount.
>> +                 */
>> +                if ( unlikely(a->memflags & MEMF_force_heap_alloc) )
>> +                    a->memflags &= ~MEMF_force_heap_alloc;
> As asked before: Why the if()?

I think there is no need to clear the flag if it is not set. But you are 
correct, the if is not needed. I can drop it.

>> @@ -1404,6 +1422,7 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>>       {
>>       case XENMEM_increase_reservation:
>>       case XENMEM_decrease_reservation:
>> +    case XENMEM_populate_physmap_heap_alloc:
>>       case XENMEM_populate_physmap:
>>           if ( copy_from_guest(&reservation, arg, 1) )
>>               return start_extent;
> Nit or not: Please insert the new case label last.

Sure.

>> @@ -1433,6 +1452,11 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>>                && (reservation.mem_flags & XENMEMF_populate_on_demand) )
>>               args.memflags |= MEMF_populate_on_demand;
>>   
>> +        /* Assert flag is not set from construct_memop_from_reservation(). */
>> +        ASSERT(!(args.memflags & MEMF_force_heap_alloc));
>> +        if ( op == XENMEM_populate_physmap_heap_alloc )
>> +            args.memflags |= MEMF_force_heap_alloc;
> Wouldn't this more logically live ...
>
>>           if ( xsm_memory_adjust_reservation(XSM_TARGET, curr_d, d) )
>>           {
>>               rcu_unlock_domain(d);
>> @@ -1453,7 +1477,7 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>>           case XENMEM_decrease_reservation:
>>               decrease_reservation(&args);
>>               break;
> here, as
>
>            case XENMEM_populate_physmap_heap_alloc:
>                ...
>                fallthrough;

Ok.

>> -        default: /* XENMEM_populate_physmap */
>> +        default: /* XENMEM_populate_{physmap, physmap_heap_alloc} */
> Otherwise: Just XENMEM_populate_physmap{,_heap_alloc} perhaps?

Sounds good, thanks for the suggestion.

>> --- a/xen/include/public/memory.h
>> +++ b/xen/include/public/memory.h
>> @@ -21,6 +21,7 @@
>>   #define XENMEM_increase_reservation 0
>>   #define XENMEM_decrease_reservation 1
>>   #define XENMEM_populate_physmap     6
>> +#define XENMEM_populate_physmap_heap_alloc 29
> Without a comment, how is one supposed to know what the difference is of
> this new sub-op compared to the "normal" one? I actually wonder whether
> referring to a Xen internal (allocation requested to come from the heap)
> is actually a good idea here. I'm inclined to suggest to name this after
> the purpose it has from the guest or tool stack perspective.
>
> Speaking of which: Is this supposed to be guest-accessible, or is it
> intended for tool-stack use only (I have to admit I don't even know where
> init-dom0less actually runs)? In the latter case that also wants enforcing.
> This may require an adjustment to the XSM hook in use here. Cc-ing Daniel
> for possible advice.

This sub-op should be called by the init-dom0less application (toolstack 
side), which runs in Dom0. Daniel has proposed an alternative solution 
which is based on the hypfs. If we decide to go that route, I think I 
will rewrite the series. I will wait for the discussion settled. Thanks 
for looping in Daniel!

Kind regards,
Henry

>
> Jan
Jan Beulich April 19, 2024, 6:18 a.m. UTC | #3
On 19.04.2024 04:31, Henry Wang wrote:
> On 4/18/2024 8:54 PM, Jan Beulich wrote:
>> On 09.04.2024 06:53, Henry Wang wrote:
>>> --- a/xen/include/public/memory.h
>>> +++ b/xen/include/public/memory.h
>>> @@ -21,6 +21,7 @@
>>>   #define XENMEM_increase_reservation 0
>>>   #define XENMEM_decrease_reservation 1
>>>   #define XENMEM_populate_physmap     6
>>> +#define XENMEM_populate_physmap_heap_alloc 29
>> Without a comment, how is one supposed to know what the difference is of
>> this new sub-op compared to the "normal" one? I actually wonder whether
>> referring to a Xen internal (allocation requested to come from the heap)
>> is actually a good idea here. I'm inclined to suggest to name this after
>> the purpose it has from the guest or tool stack perspective.
>>
>> Speaking of which: Is this supposed to be guest-accessible, or is it
>> intended for tool-stack use only (I have to admit I don't even know where
>> init-dom0less actually runs)? In the latter case that also wants enforcing.
>> This may require an adjustment to the XSM hook in use here. Cc-ing Daniel
>> for possible advice.
> 
> This sub-op should be called by the init-dom0less application (toolstack 
> side), which runs in Dom0.

I'm puzzled: How can init-dom0less (note its name!) run in Dom0, when there
is none?

Jan

> Daniel has proposed an alternative solution 
> which is based on the hypfs. If we decide to go that route, I think I 
> will rewrite the series. I will wait for the discussion settled. Thanks 
> for looping in Daniel!
> 
> Kind regards,
> Henry
Henry Wang April 19, 2024, 6:30 a.m. UTC | #4
Hi Jan,

On 4/19/2024 2:18 PM, Jan Beulich wrote:
> On 19.04.2024 04:31, Henry Wang wrote:
>> On 4/18/2024 8:54 PM, Jan Beulich wrote:
>>> On 09.04.2024 06:53, Henry Wang wrote:
>>>> --- a/xen/include/public/memory.h
>>>> +++ b/xen/include/public/memory.h
>>>> @@ -21,6 +21,7 @@
>>>>    #define XENMEM_increase_reservation 0
>>>>    #define XENMEM_decrease_reservation 1
>>>>    #define XENMEM_populate_physmap     6
>>>> +#define XENMEM_populate_physmap_heap_alloc 29
>>> Without a comment, how is one supposed to know what the difference is of
>>> this new sub-op compared to the "normal" one? I actually wonder whether
>>> referring to a Xen internal (allocation requested to come from the heap)
>>> is actually a good idea here. I'm inclined to suggest to name this after
>>> the purpose it has from the guest or tool stack perspective.
>>>
>>> Speaking of which: Is this supposed to be guest-accessible, or is it
>>> intended for tool-stack use only (I have to admit I don't even know where
>>> init-dom0less actually runs)? In the latter case that also wants enforcing.
>>> This may require an adjustment to the XSM hook in use here. Cc-ing Daniel
>>> for possible advice.
>> This sub-op should be called by the init-dom0less application (toolstack
>> side), which runs in Dom0.
> I'm puzzled: How can init-dom0less (note its name!) run in Dom0, when there
> is none?

[1] is the original patch that introduced this application (More details 
can be found in the cover letter of the original series of [1]). I think 
the use case for this application is to make dom0less domains to use the 
PV driver when dom0 and dom0less domUs exist at the same time. There 
used to be a discussion regarding the naming confusion, see [2] commit 
message, but I cannot remember if this discussion has settled or not.

[1] 
https://lore.kernel.org/xen-devel/20220505001656.395419-6-sstabellini@kernel.org/
[2] 
https://lore.kernel.org/xen-devel/20230630091210.3742121-1-luca.fancellu@arm.com/

Kind regards,
Henry

> Jan
>
>> Daniel has proposed an alternative solution
>> which is based on the hypfs. If we decide to go that route, I think I
>> will rewrite the series. I will wait for the discussion settled. Thanks
>> for looping in Daniel!
>>
>> Kind regards,
>> Henry
diff mbox series

Patch

diff --git a/tools/helpers/init-dom0less.c b/tools/helpers/init-dom0less.c
index fee93459c4..dccab7b29b 100644
--- a/tools/helpers/init-dom0less.c
+++ b/tools/helpers/init-dom0less.c
@@ -19,24 +19,42 @@ 
 #define XENSTORE_PFN_OFFSET 1
 #define STR_MAX_LENGTH 128
 
+static xen_pfn_t xs_page_base;
+static xen_pfn_t xs_page_p2m;
+
 static int alloc_xs_page(struct xc_interface_core *xch,
                          libxl_dominfo *info,
                          uint64_t *xenstore_pfn)
 {
-    int rc;
-    const xen_pfn_t base = GUEST_MAGIC_BASE >> XC_PAGE_SHIFT;
-    xen_pfn_t p2m = (GUEST_MAGIC_BASE >> XC_PAGE_SHIFT) + XENSTORE_PFN_OFFSET;
+    int rc, i;
+    uint32_t nr_regions = XEN_MAX_MEM_REGIONS;
+    struct xen_mem_region mem_regions[XEN_MAX_MEM_REGIONS] = {0};
+
+    rc = xc_get_domain_mem_map(xch, info->domid, mem_regions, &nr_regions);
+    if (rc < 0)
+        return rc;
+
+    for ( i = 0; i < nr_regions; i++ )
+    {
+        if ( mem_regions[i].type == XEN_MEM_REGION_MAGIC )
+        {
+            xs_page_base = mem_regions[i].start >> XC_PAGE_SHIFT;
+            xs_page_p2m = (mem_regions[i].start >> XC_PAGE_SHIFT) +
+                          XENSTORE_PFN_OFFSET;
+        }
+    }
 
     rc = xc_domain_setmaxmem(xch, info->domid,
                              info->max_memkb + (XC_PAGE_SIZE/1024));
     if (rc < 0)
         return rc;
 
-    rc = xc_domain_populate_physmap_exact(xch, info->domid, 1, 0, 0, &p2m);
+    rc = xc_domain_populate_physmap_heap_exact(xch, info->domid, 1, 0, 0,
+                                               &xs_page_p2m);
     if (rc < 0)
         return rc;
 
-    *xenstore_pfn = base + XENSTORE_PFN_OFFSET;
+    *xenstore_pfn = xs_page_base + XENSTORE_PFN_OFFSET;
     rc = xc_clear_domain_page(xch, info->domid, *xenstore_pfn);
     if (rc < 0)
         return rc;
@@ -145,8 +163,7 @@  static int create_xenstore(struct xs_handle *xsh,
     rc = snprintf(target_memkb_str, STR_MAX_LENGTH, "%"PRIu64, info->current_memkb);
     if (rc < 0 || rc >= STR_MAX_LENGTH)
         return rc;
-    rc = snprintf(ring_ref_str, STR_MAX_LENGTH, "%lld",
-                  (GUEST_MAGIC_BASE >> XC_PAGE_SHIFT) + XENSTORE_PFN_OFFSET);
+    rc = snprintf(ring_ref_str, STR_MAX_LENGTH, "%"PRIu_xen_pfn, xs_page_p2m);
     if (rc < 0 || rc >= STR_MAX_LENGTH)
         return rc;
     rc = snprintf(xenstore_port_str, STR_MAX_LENGTH, "%u", xenstore_port);
@@ -282,9 +299,7 @@  static int init_domain(struct xs_handle *xsh,
     if (rc)
         err(1, "writing to xenstore");
 
-    rc = xs_introduce_domain(xsh, info->domid,
-            (GUEST_MAGIC_BASE >> XC_PAGE_SHIFT) + XENSTORE_PFN_OFFSET,
-            xenstore_evtchn);
+    rc = xs_introduce_domain(xsh, info->domid, xs_page_p2m, xenstore_evtchn);
     if (!rc)
         err(1, "xs_introduce_domain");
     return 0;
diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h
index b25e9772a2..c1a601813a 100644
--- a/tools/include/xenctrl.h
+++ b/tools/include/xenctrl.h
@@ -1342,6 +1342,13 @@  int xc_domain_populate_physmap(xc_interface *xch,
                                unsigned int mem_flags,
                                xen_pfn_t *extent_start);
 
+int xc_domain_populate_physmap_heap_exact(xc_interface *xch,
+                                          uint32_t domid,
+                                          unsigned long nr_extents,
+                                          unsigned int extent_order,
+                                          unsigned int mem_flags,
+                                          xen_pfn_t *extent_start);
+
 int xc_domain_populate_physmap_exact(xc_interface *xch,
                                      uint32_t domid,
                                      unsigned long nr_extents,
diff --git a/tools/libs/ctrl/xc_domain.c b/tools/libs/ctrl/xc_domain.c
index 4dba55d01d..82c1554613 100644
--- a/tools/libs/ctrl/xc_domain.c
+++ b/tools/libs/ctrl/xc_domain.c
@@ -916,6 +916,36 @@  int xc_domain_nr_gpfns(xc_interface *xch, uint32_t domid, xen_pfn_t *gpfns)
     return rc;
 }
 
+static int xc_populate_physmap_cmd(xc_interface *xch,
+                                   unsigned int cmd,
+                                   uint32_t domid,
+                                   unsigned long nr_extents,
+                                   unsigned int extent_order,
+                                   unsigned int mem_flags,
+                                   xen_pfn_t *extent_start)
+{
+    int err;
+    DECLARE_HYPERCALL_BOUNCE(extent_start, nr_extents * sizeof(*extent_start), XC_HYPERCALL_BUFFER_BOUNCE_BOTH);
+    struct xen_memory_reservation reservation = {
+        .nr_extents   = nr_extents,
+        .extent_order = extent_order,
+        .mem_flags    = mem_flags,
+        .domid        = domid
+    };
+
+    if ( xc_hypercall_bounce_pre(xch, extent_start) )
+    {
+        PERROR("Could not bounce memory for XENMEM_populate_physmap hypercall");
+        return -1;
+    }
+    set_xen_guest_handle(reservation.extent_start, extent_start);
+
+    err = xc_memory_op(xch, cmd, &reservation, sizeof(reservation));
+
+    xc_hypercall_bounce_post(xch, extent_start);
+    return err;
+}
+
 int xc_domain_increase_reservation(xc_interface *xch,
                                    uint32_t domid,
                                    unsigned long nr_extents,
@@ -1135,26 +1165,9 @@  int xc_domain_populate_physmap(xc_interface *xch,
                                unsigned int mem_flags,
                                xen_pfn_t *extent_start)
 {
-    int err;
-    DECLARE_HYPERCALL_BOUNCE(extent_start, nr_extents * sizeof(*extent_start), XC_HYPERCALL_BUFFER_BOUNCE_BOTH);
-    struct xen_memory_reservation reservation = {
-        .nr_extents   = nr_extents,
-        .extent_order = extent_order,
-        .mem_flags    = mem_flags,
-        .domid        = domid
-    };
-
-    if ( xc_hypercall_bounce_pre(xch, extent_start) )
-    {
-        PERROR("Could not bounce memory for XENMEM_populate_physmap hypercall");
-        return -1;
-    }
-    set_xen_guest_handle(reservation.extent_start, extent_start);
-
-    err = xc_memory_op(xch, XENMEM_populate_physmap, &reservation, sizeof(reservation));
-
-    xc_hypercall_bounce_post(xch, extent_start);
-    return err;
+    return xc_populate_physmap_cmd(xch, XENMEM_populate_physmap, domid,
+                                   nr_extents, extent_order, mem_flags,
+                                   extent_start);
 }
 
 int xc_domain_populate_physmap_exact(xc_interface *xch,
@@ -1182,6 +1195,32 @@  int xc_domain_populate_physmap_exact(xc_interface *xch,
     return err;
 }
 
+int xc_domain_populate_physmap_heap_exact(xc_interface *xch,
+                                          uint32_t domid,
+                                          unsigned long nr_extents,
+                                          unsigned int extent_order,
+                                          unsigned int mem_flags,
+                                          xen_pfn_t *extent_start)
+{
+    int err;
+
+    err = xc_populate_physmap_cmd(xch, XENMEM_populate_physmap_heap_alloc,
+                                  domid, nr_extents, extent_order, mem_flags,
+                                  extent_start);
+    if ( err == nr_extents )
+        return 0;
+
+    if ( err >= 0 )
+    {
+        DPRINTF("Failed allocation for dom %d: %ld extents of order %d\n",
+                domid, nr_extents, extent_order);
+        errno = EBUSY;
+        err = -1;
+    }
+
+    return err;
+}
+
 int xc_domain_memory_exchange_pages(xc_interface *xch,
                                     uint32_t domid,
                                     unsigned long nr_in_extents,
diff --git a/xen/common/memory.c b/xen/common/memory.c
index b4593f5f45..a4733869c2 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -155,6 +155,14 @@  static void increase_reservation(struct memop_args *a)
     a->nr_done = i;
 }
 
+/*
+ * Alias of _MEMF_no_refcount to avoid introduction of a new, single-use flag.
+ * This flag should be used for populate_physmap() only as a re-purposing of
+ * _MEMF_no_refcount to force a non-1:1 allocation from domheap.
+ */
+#define _MEMF_force_heap_alloc _MEMF_no_refcount
+#define  MEMF_force_heap_alloc (1U<<_MEMF_force_heap_alloc)
+
 static void populate_physmap(struct memop_args *a)
 {
     struct page_info *page;
@@ -219,7 +227,8 @@  static void populate_physmap(struct memop_args *a)
         }
         else
         {
-            if ( is_domain_direct_mapped(d) )
+            if ( is_domain_direct_mapped(d) &&
+                 !(a->memflags & MEMF_force_heap_alloc) )
             {
                 mfn = _mfn(gpfn);
 
@@ -246,7 +255,8 @@  static void populate_physmap(struct memop_args *a)
 
                 mfn = _mfn(gpfn);
             }
-            else if ( is_domain_using_staticmem(d) )
+            else if ( is_domain_using_staticmem(d) &&
+                      !(a->memflags & MEMF_force_heap_alloc) )
             {
                 /*
                  * No easy way to guarantee the retrieved pages are contiguous,
@@ -271,6 +281,14 @@  static void populate_physmap(struct memop_args *a)
             }
             else
             {
+                /*
+                 * Avoid passing MEMF_force_heap_alloc down to
+                 * alloc_domheap_pages() where the meaning would be the
+                 * original MEMF_no_refcount.
+                 */
+                if ( unlikely(a->memflags & MEMF_force_heap_alloc) )
+                    a->memflags &= ~MEMF_force_heap_alloc;
+
                 page = alloc_domheap_pages(d, a->extent_order, a->memflags);
 
                 if ( unlikely(!page) )
@@ -1404,6 +1422,7 @@  long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
     {
     case XENMEM_increase_reservation:
     case XENMEM_decrease_reservation:
+    case XENMEM_populate_physmap_heap_alloc:
     case XENMEM_populate_physmap:
         if ( copy_from_guest(&reservation, arg, 1) )
             return start_extent;
@@ -1433,6 +1452,11 @@  long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
              && (reservation.mem_flags & XENMEMF_populate_on_demand) )
             args.memflags |= MEMF_populate_on_demand;
 
+        /* Assert flag is not set from construct_memop_from_reservation(). */
+        ASSERT(!(args.memflags & MEMF_force_heap_alloc));
+        if ( op == XENMEM_populate_physmap_heap_alloc )
+            args.memflags |= MEMF_force_heap_alloc;
+
         if ( xsm_memory_adjust_reservation(XSM_TARGET, curr_d, d) )
         {
             rcu_unlock_domain(d);
@@ -1453,7 +1477,7 @@  long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
         case XENMEM_decrease_reservation:
             decrease_reservation(&args);
             break;
-        default: /* XENMEM_populate_physmap */
+        default: /* XENMEM_populate_{physmap, physmap_heap_alloc} */
             populate_physmap(&args);
             break;
         }
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index 5e545ae9a4..5e79992671 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -21,6 +21,7 @@ 
 #define XENMEM_increase_reservation 0
 #define XENMEM_decrease_reservation 1
 #define XENMEM_populate_physmap     6
+#define XENMEM_populate_physmap_heap_alloc 29
 
 #if __XEN_INTERFACE_VERSION__ >= 0x00030209
 /*
@@ -731,7 +732,7 @@  struct xen_vnuma_topology_info {
 typedef struct xen_vnuma_topology_info xen_vnuma_topology_info_t;
 DEFINE_XEN_GUEST_HANDLE(xen_vnuma_topology_info_t);
 
-/* Next available subop number is 29 */
+/* Next available subop number is 30 */
 
 #endif /* __XEN_PUBLIC_MEMORY_H__ */