diff mbox

[v2,3/9] xen/mm: move modify_identity_mmio to global file and drop __init

Message ID 20170420151743.90889-4-roger.pau@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Roger Pau Monné April 20, 2017, 3:17 p.m. UTC
And also allow it to do non-identity mappings by adding a new parameter. This
function will be needed in other parts apart from PVH Dom0 build.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
---
 xen/arch/x86/hvm/dom0_build.c | 22 +---------------------
 xen/common/memory.c           | 34 ++++++++++++++++++++++++++++++++++
 xen/include/xen/p2m-common.h  |  4 ++++
 3 files changed, 39 insertions(+), 21 deletions(-)

Comments

Julien Grall April 24, 2017, 2:42 p.m. UTC | #1
Hi Roger,

On 20/04/17 16:17, Roger Pau Monne wrote:
> And also allow it to do non-identity mappings by adding a new parameter. This
> function will be needed in other parts apart from PVH Dom0 build.
>
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> ---
> Cc: Jan Beulich <jbeulich@suse.com>
> Cc: Andrew Cooper <andrew.cooper3@citrix.com>
> ---
>  xen/arch/x86/hvm/dom0_build.c | 22 +---------------------
>  xen/common/memory.c           | 34 ++++++++++++++++++++++++++++++++++
>  xen/include/xen/p2m-common.h  |  4 ++++
>  3 files changed, 39 insertions(+), 21 deletions(-)
>
> diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
> index ca88c5835e..65f606d33a 100644
> --- a/xen/arch/x86/hvm/dom0_build.c
> +++ b/xen/arch/x86/hvm/dom0_build.c
> @@ -64,27 +64,7 @@ static struct acpi_madt_nmi_source __initdata *nmisrc;
>  static int __init modify_identity_mmio(struct domain *d, unsigned long pfn,
>                                         unsigned long nr_pages, const bool map)
>  {
> -    int rc;
> -
> -    for ( ; ; )
> -    {
> -        rc = (map ? map_mmio_regions : unmap_mmio_regions)
> -             (d, _gfn(pfn), nr_pages, _mfn(pfn));
> -        if ( rc == 0 )
> -            break;
> -        if ( rc < 0 )
> -        {
> -            printk(XENLOG_WARNING
> -                   "Failed to identity %smap [%#lx,%#lx) for d%d: %d\n",
> -                   map ? "" : "un", pfn, pfn + nr_pages, d->domain_id, rc);
> -            break;
> -        }
> -        nr_pages -= rc;
> -        pfn += rc;
> -        process_pending_softirqs();
> -    }
> -
> -    return rc;
> +    return modify_mmio(d, pfn, pfn, nr_pages, map);
>  }
>
>  /* Populate a HVM memory range using the biggest possible order. */
> diff --git a/xen/common/memory.c b/xen/common/memory.c
> index 52879e7438..0d970482cb 100644
> --- a/xen/common/memory.c
> +++ b/xen/common/memory.c
> @@ -1438,6 +1438,40 @@ int prepare_ring_for_helper(
>      return 0;
>  }
>
> +int modify_mmio(struct domain *d, unsigned long gfn, unsigned long pfn,

Whilst you introduce this new function, please use mfn_t and gfn_t.

Also s/pfn/mfn/

> +                unsigned long nr_pages, const bool map)
> +{
> +    int rc;
> +
> +    /*
> +     * Make sure this function is only used by the hardware domain, because it
> +     * can take an arbitrary long time, and could DoS the whole system.
> +     */
> +    ASSERT(is_hardware_domain(d));

What would be the plan for guest if we decide to use vpci?

> +
> +    for ( ; ; )
> +    {
> +        rc = (map ? map_mmio_regions : unmap_mmio_regions)

On ARM, map_mmio_regions and unmap_mmio_regions will map the MMIO with 
very strict attribute. I think we would need an extra argument to know 
the wanted memory attribute (maybe p2m_type_t?).

> +             (d, _gfn(gfn), nr_pages, _mfn(pfn));
> +        if ( rc == 0 )
> +            break;
> +        if ( rc < 0 )
> +        {
> +            printk(XENLOG_WARNING

I would probably use XENLOG_G_WARNING.

> +                   "Failed to %smap [%#lx, %#lx) -> [%#lx,%#lx) for d%d: %d\n",
> +                   map ? "" : "un", gfn, gfn + nr_pages, pfn, pfn + nr_pages,
> +                   d->domain_id, rc);
> +            break;
> +        }
> +        nr_pages -= rc;
> +        pfn += rc;
> +        gfn += rc;
> +        process_pending_softirqs();
> +    }
> +
> +    return rc;
> +}
> +
>  /*
>   * Local variables:
>   * mode: C
> diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
> index 8cd5a6b503..1308da44e7 100644
> --- a/xen/include/xen/p2m-common.h
> +++ b/xen/include/xen/p2m-common.h
> @@ -13,4 +13,8 @@ int unmap_mmio_regions(struct domain *d,
>                         unsigned long nr,
>                         mfn_t mfn);
>
> +

Spurious newline.

> +int modify_mmio(struct domain *d, unsigned long gfn, unsigned long pfn,
> +                unsigned long nr_pages, const bool map);
> +
>  #endif /* _XEN_P2M_COMMON_H */
>

Cheers,
Roger Pau Monné April 25, 2017, 8:01 a.m. UTC | #2
On Mon, Apr 24, 2017 at 03:42:08PM +0100, Julien Grall wrote:
> On 20/04/17 16:17, Roger Pau Monne wrote:
> >  /* Populate a HVM memory range using the biggest possible order. */
> > diff --git a/xen/common/memory.c b/xen/common/memory.c
> > index 52879e7438..0d970482cb 100644
> > --- a/xen/common/memory.c
> > +++ b/xen/common/memory.c
> > @@ -1438,6 +1438,40 @@ int prepare_ring_for_helper(
> >      return 0;
> >  }
> > 
> > +int modify_mmio(struct domain *d, unsigned long gfn, unsigned long pfn,
> 
> Whilst you introduce this new function, please use mfn_t and gfn_t.
> 
> Also s/pfn/mfn/

Done.

> > +                unsigned long nr_pages, const bool map)
> > +{
> > +    int rc;
> > +
> > +    /*
> > +     * Make sure this function is only used by the hardware domain, because it
> > +     * can take an arbitrary long time, and could DoS the whole system.
> > +     */
> > +    ASSERT(is_hardware_domain(d));
> 
> What would be the plan for guest if we decide to use vpci?

One option would be to not allow the DomU to relocate it's BARs and ignore
writes to the 2nd bit of the command register (PCI_COMMAND_MEMORY), thus always
having the BARs mapped. The other is to somehow allow VMExit (and the ARM
equivalent) continuation (something similar to what we do with hypercalls).

> > +
> > +    for ( ; ; )
> > +    {
> > +        rc = (map ? map_mmio_regions : unmap_mmio_regions)
> 
> On ARM, map_mmio_regions and unmap_mmio_regions will map the MMIO with very
> strict attribute. I think we would need an extra argument to know the wanted
> memory attribute (maybe p2m_type_t?).

I'm not sure I can do anything regarding this ATM. Sorry for my ignorance, but
map_mmio_regions on ARM maps the region as p2m_mmio_direct_dev, and according
to the comment that's "Read/write mapping of genuine Device MMIO area", which
fits exactly into my usage (I'm using it to map BARs).

> > +             (d, _gfn(gfn), nr_pages, _mfn(pfn));
> > +        if ( rc == 0 )
> > +            break;
> > +        if ( rc < 0 )
> > +        {
> > +            printk(XENLOG_WARNING
> 
> I would probably use XENLOG_G_WARNING.

Done.

> > +                   "Failed to %smap [%#lx, %#lx) -> [%#lx,%#lx) for d%d: %d\n",
> > +                   map ? "" : "un", gfn, gfn + nr_pages, pfn, pfn + nr_pages,
> > +                   d->domain_id, rc);
> > +            break;
> > +        }
> > +        nr_pages -= rc;
> > +        pfn += rc;
> > +        gfn += rc;
> > +        process_pending_softirqs();
> > +    }
> > +
> > +    return rc;
> > +}
> > +
> >  /*
> >   * Local variables:
> >   * mode: C
> > diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
> > index 8cd5a6b503..1308da44e7 100644
> > --- a/xen/include/xen/p2m-common.h
> > +++ b/xen/include/xen/p2m-common.h
> > @@ -13,4 +13,8 @@ int unmap_mmio_regions(struct domain *d,
> >                         unsigned long nr,
> >                         mfn_t mfn);
> > 
> > +
> 
> Spurious newline.

Done.

Thanks for the comments.
Julien Grall April 25, 2017, 9:09 a.m. UTC | #3
Hi Roger,

On 25/04/2017 09:01, Roger Pau Monne wrote:
> On Mon, Apr 24, 2017 at 03:42:08PM +0100, Julien Grall wrote:
>> On 20/04/17 16:17, Roger Pau Monne wrote:
>>>  /* Populate a HVM memory range using the biggest possible order. */
>>> diff --git a/xen/common/memory.c b/xen/common/memory.c
>>> index 52879e7438..0d970482cb 100644
>>> --- a/xen/common/memory.c
>>> +++ b/xen/common/memory.c
>>> @@ -1438,6 +1438,40 @@ int prepare_ring_for_helper(
>>>      return 0;
>>>  }
>>>
>>> +int modify_mmio(struct domain *d, unsigned long gfn, unsigned long pfn,
>>
>> Whilst you introduce this new function, please use mfn_t and gfn_t.
>>
>> Also s/pfn/mfn/
>
> Done.
>
>>> +                unsigned long nr_pages, const bool map)
>>> +{
>>> +    int rc;
>>> +
>>> +    /*
>>> +     * Make sure this function is only used by the hardware domain, because it
>>> +     * can take an arbitrary long time, and could DoS the whole system.
>>> +     */
>>> +    ASSERT(is_hardware_domain(d));
>>
>> What would be the plan for guest if we decide to use vpci?
>
> One option would be to not allow the DomU to relocate it's BARs and ignore
> writes to the 2nd bit of the command register (PCI_COMMAND_MEMORY), thus always
> having the BARs mapped. The other is to somehow allow VMExit (and the ARM
> equivalent) continuation (something similar to what we do with hypercalls).

My understanding is BARs may be allocated by the kernel because the 
firmware didn't do it. This is the current case on ARM (and I guess x86) 
where Linux will always go through the BARs.

So if you do the first option, who would decide the position of the BARs?

For the second option, we can take advantage of superpage (4K, 2M, 1G) 
mapping on ARM, so the number of actual mapping would be really limited.

Also, we are looking at MMIO continuation for ARM for other part of the 
hypervisor. We might be able to leverage that for this function.

>
>>> +
>>> +    for ( ; ; )
>>> +    {
>>> +        rc = (map ? map_mmio_regions : unmap_mmio_regions)
>>
>> On ARM, map_mmio_regions and unmap_mmio_regions will map the MMIO with very
>> strict attribute. I think we would need an extra argument to know the wanted
>> memory attribute (maybe p2m_type_t?).
>
> I'm not sure I can do anything regarding this ATM. Sorry for my ignorance, but
> map_mmio_regions on ARM maps the region as p2m_mmio_direct_dev, and according
> to the comment that's "Read/write mapping of genuine Device MMIO area", which
> fits exactly into my usage (I'm using it to map BARs).

We have few p2m_mmio_direct_* p2m_type because the architecture allows 
us to have fine grain memory attribute.

The p2m type p2m_mmio_direct_dev is very restrictive (unaligned access 
forbid, non-cacheable, non-gatherable). This should be used for MMIO 
region that have side-effects and will affect performances.

We use this one by default as it will restrict the memory attribute used 
by the guest. However, this will be an issue for at least cacheable 
BARs. We had similar issue recently on ARM with SRAM device as driver 
may do unaligned access and cacheable one.

For DOM0 we are using p2m_mmio_direct_c and rely on the OS to restrict 
the memory attribute when necessary. We cannot do that for guest as this 
may have some security implications.

So for the guest we will do on the case by case basis. For instance we 
you map BAR, you know the kind and can decide of a proper memory attribute.

Cheers,
Roger Pau Monné April 25, 2017, 9:25 a.m. UTC | #4
On Tue, Apr 25, 2017 at 10:09:34AM +0100, Julien Grall wrote:
> On 25/04/2017 09:01, Roger Pau Monne wrote:
> > On Mon, Apr 24, 2017 at 03:42:08PM +0100, Julien Grall wrote:
> > > On 20/04/17 16:17, Roger Pau Monne wrote:
> > > > +                unsigned long nr_pages, const bool map)
> > > > +{
> > > > +    int rc;
> > > > +
> > > > +    /*
> > > > +     * Make sure this function is only used by the hardware domain, because it
> > > > +     * can take an arbitrary long time, and could DoS the whole system.
> > > > +     */
> > > > +    ASSERT(is_hardware_domain(d));
> > > 
> > > What would be the plan for guest if we decide to use vpci?
> > 
> > One option would be to not allow the DomU to relocate it's BARs and ignore
> > writes to the 2nd bit of the command register (PCI_COMMAND_MEMORY), thus always
> > having the BARs mapped. The other is to somehow allow VMExit (and the ARM
> > equivalent) continuation (something similar to what we do with hypercalls).
> 
> My understanding is BARs may be allocated by the kernel because the firmware
> didn't do it. This is the current case on ARM (and I guess x86) where Linux
> will always go through the BARs.

No, on x86 BARs are allocated by the firmware. Linux or whatever OS will scan
the BARs in order to get it's position/size, but will not try to move them
AFAIK.

> So if you do the first option, who would decide the position of the BARs?

On x86 that would be what the firmware has set.

> For the second option, we can take advantage of superpage (4K, 2M, 1G)
> mapping on ARM, so the number of actual mapping would be really limited.

IIRC x86 should also do MMIO mappings with superpages. Maybe we should time how
long this takes and then make a decision.

> Also, we are looking at MMIO continuation for ARM for other part of the
> hypervisor. We might be able to leverage that for this function.

Indeed

> > 
> > > > +
> > > > +    for ( ; ; )
> > > > +    {
> > > > +        rc = (map ? map_mmio_regions : unmap_mmio_regions)
> > > 
> > > On ARM, map_mmio_regions and unmap_mmio_regions will map the MMIO with very
> > > strict attribute. I think we would need an extra argument to know the wanted
> > > memory attribute (maybe p2m_type_t?).
> > 
> > I'm not sure I can do anything regarding this ATM. Sorry for my ignorance, but
> > map_mmio_regions on ARM maps the region as p2m_mmio_direct_dev, and according
> > to the comment that's "Read/write mapping of genuine Device MMIO area", which
> > fits exactly into my usage (I'm using it to map BARs).
> 
> We have few p2m_mmio_direct_* p2m_type because the architecture allows us to
> have fine grain memory attribute.
> 
> The p2m type p2m_mmio_direct_dev is very restrictive (unaligned access
> forbid, non-cacheable, non-gatherable). This should be used for MMIO region
> that have side-effects and will affect performances.
> 
> We use this one by default as it will restrict the memory attribute used by
> the guest. However, this will be an issue for at least cacheable BARs. We
> had similar issue recently on ARM with SRAM device as driver may do
> unaligned access and cacheable one.
> 
> For DOM0 we are using p2m_mmio_direct_c and rely on the OS to restrict the
> memory attribute when necessary. We cannot do that for guest as this may
> have some security implications.
> 
> So for the guest we will do on the case by case basis. For instance we you
> map BAR, you know the kind and can decide of a proper memory attribute.

Oh, OK. I guess you can add a new parameter to pass whether the BAR is
prefetchable or not.

Roger.
Jan Beulich April 25, 2017, 9:32 a.m. UTC | #5
>>> On 25.04.17 at 11:25, <roger.pau@citrix.com> wrote:
> On Tue, Apr 25, 2017 at 10:09:34AM +0100, Julien Grall wrote:
>> My understanding is BARs may be allocated by the kernel because the firmware
>> didn't do it. This is the current case on ARM (and I guess x86) where Linux
>> will always go through the BARs.
> 
> No, on x86 BARs are allocated by the firmware. Linux or whatever OS will scan
> the BARs in order to get it's position/size, but will not try to move them
> AFAIK.

That depends. Firmware is not required to set up all of them (only
such on devices needed for booting obviously need to be set up).
And Linux may (voluntarily or forced via command line option) still
move BARs.

Jan
Roger Pau Monné April 26, 2017, 8:26 a.m. UTC | #6
On Tue, Apr 25, 2017 at 03:32:50AM -0600, Jan Beulich wrote:
> >>> On 25.04.17 at 11:25, <roger.pau@citrix.com> wrote:
> > On Tue, Apr 25, 2017 at 10:09:34AM +0100, Julien Grall wrote:
> >> My understanding is BARs may be allocated by the kernel because the firmware
> >> didn't do it. This is the current case on ARM (and I guess x86) where Linux
> >> will always go through the BARs.
> > 
> > No, on x86 BARs are allocated by the firmware. Linux or whatever OS will scan
> > the BARs in order to get it's position/size, but will not try to move them
> > AFAIK.
> 
> That depends. Firmware is not required to set up all of them (only
> such on devices needed for booting obviously need to be set up).
> And Linux may (voluntarily or forced via command line option) still
> move BARs.

Right. In this series I allow the guest to change the position where the BARs
are mapped into the guest p2m, but I don't allow it to change the physical
address where the BAR is actually mapped. This might work well if all BARs
where positioned by the firmware, but if there are unset BARs I don't think Xen
is capable to make a good decision about it's position (because it lacks
information like ACPI OperationRegions), so I guess I should allow Dom0 to
write directly to the BAR and position it.

BTW, how does Xen deal with MSI-X tables inside of BARs? AFAICT a PV Dom0 is
able to move the BARs around as much as it wants.

Roger.
Jan Beulich April 26, 2017, 8:51 a.m. UTC | #7
>>> On 26.04.17 at 10:26, <roger.pau@citrix.com> wrote:
> BTW, how does Xen deal with MSI-X tables inside of BARs? AFAICT a PV Dom0 is
> able to move the BARs around as much as it wants.

The current expectation is that this doesn't happen with (pre) set up
MSI-X interrupts (i.e. do BAR placement first, then set up interrupts).

Jan
Roger Pau Monné April 27, 2017, 8:58 a.m. UTC | #8
On Tue, Apr 25, 2017 at 03:32:50AM -0600, Jan Beulich wrote:
> >>> On 25.04.17 at 11:25, <roger.pau@citrix.com> wrote:
> > On Tue, Apr 25, 2017 at 10:09:34AM +0100, Julien Grall wrote:
> >> My understanding is BARs may be allocated by the kernel because the firmware
> >> didn't do it. This is the current case on ARM (and I guess x86) where Linux
> >> will always go through the BARs.
> > 
> > No, on x86 BARs are allocated by the firmware. Linux or whatever OS will scan
> > the BARs in order to get it's position/size, but will not try to move them
> > AFAIK.
> 
> That depends. Firmware is not required to set up all of them (only
> such on devices needed for booting obviously need to be set up).
> And Linux may (voluntarily or forced via command line option) still
> move BARs.

The spec seems more strict here:

"Power-up software needs to build a consistent address map before booting the
machine to an operating system. This means it has to determine how much memory
is in the system, and how much address space the I/O controllers in the system
require. After determining this information, power-up software can map the I/O
controllers into reasonable locations and proceed with system boot."

This is from PCI LOCAL BUS SPECIFICATION, REV. 3.0. I read that as "firmware
will position all the BARs".

Moving the BARs is not a huge problem, this series already allows the guest to
move where the BARs are mapped in it's p2m, allowing a guest to set the initial
BAR position would also be feasible, but I haven't been able to find any device
on my boxes that's not initialized by the firmware, hence it would be hard for
me to test that.

Roger.
Julien Grall April 27, 2017, 9:08 a.m. UTC | #9
Hi roger,

On Thu, 27 Apr 2017, 11:02 Roger Pau Monne, <roger.pau@citrix.com> wrote:

> On Tue, Apr 25, 2017 at 03:32:50AM -0600, Jan Beulich wrote:
> > >>> On 25.04.17 at 11:25, <roger.pau@citrix.com> wrote:
> > > On Tue, Apr 25, 2017 at 10:09:34AM +0100, Julien Grall wrote:
> > >> My understanding is BARs may be allocated by the kernel because the
> firmware
> > >> didn't do it. This is the current case on ARM (and I guess x86) where
> Linux
> > >> will always go through the BARs.
> > >
> > > No, on x86 BARs are allocated by the firmware. Linux or whatever OS
> will scan
> > > the BARs in order to get it's position/size, but will not try to move
> them
> > > AFAIK.
> >
> > That depends. Firmware is not required to set up all of them (only
> > such on devices needed for booting obviously need to be set up).
> > And Linux may (voluntarily or forced via command line option) still
> > move BARs.
>
> The spec seems more strict here:
>
> "Power-up software needs to build a consistent address map before booting
> the
> machine to an operating system. This means it has to determine how much
> memory
> is in the system, and how much address space the I/O controllers in the
> system
> require. After determining this information, power-up software can map the
> I/O
> controllers into reasonable locations and proceed with system boot."
>

It does not seem that strict to me. The spec says "power-up software can
map".

It is neither must nor should. So it may or may not.

As we spoke on the PCI passthrough design document the firmware is only
required to initialize device at used for boot.

So it does not cover hotplug devices nor devices not used for boot.


> This is from PCI LOCAL BUS SPECIFICATION, REV. 3.0. I read that as
> "firmware
> will position all the BARs".
>
> Moving the BARs is not a huge problem, this series already allows the
> guest to
> move where the BARs are mapped in it's p2m, allowing a guest to set the
> initial
> BAR position would also be feasible, but I haven't been able to find any
> device
> on my boxes that's not initialized by the firmware, hence it would be hard
> for
> me to test that.
>

I will have a look on my ARM box when I am back to test it.

Cheers,

>
Jan Beulich April 27, 2017, 9:29 a.m. UTC | #10
>>> On 27.04.17 at 10:58, <roger.pau@citrix.com> wrote:
> On Tue, Apr 25, 2017 at 03:32:50AM -0600, Jan Beulich wrote:
>> >>> On 25.04.17 at 11:25, <roger.pau@citrix.com> wrote:
>> > On Tue, Apr 25, 2017 at 10:09:34AM +0100, Julien Grall wrote:
>> >> My understanding is BARs may be allocated by the kernel because the 
> firmware
>> >> didn't do it. This is the current case on ARM (and I guess x86) where Linux
>> >> will always go through the BARs.
>> > 
>> > No, on x86 BARs are allocated by the firmware. Linux or whatever OS will 
> scan
>> > the BARs in order to get it's position/size, but will not try to move them
>> > AFAIK.
>> 
>> That depends. Firmware is not required to set up all of them (only
>> such on devices needed for booting obviously need to be set up).
>> And Linux may (voluntarily or forced via command line option) still
>> move BARs.
> 
> The spec seems more strict here:
> 
> "Power-up software needs to build a consistent address map before booting the
> machine to an operating system. This means it has to determine how much memory
> is in the system, and how much address space the I/O controllers in the system
> require. After determining this information, power-up software can map the I/O
> controllers into reasonable locations and proceed with system boot."

I don't view this as more strict: Note how the last sentence says
"can map", not "has to" or "will". There are actually downsides to
firmware doing it for all devices: Firmware can't know whether the
OS is capable of dealing with 64-bit BARs, yet it is undesirable
(and perhaps impossible) to place all of them below 4Gb.

> Moving the BARs is not a huge problem, this series already allows the guest to
> move where the BARs are mapped in it's p2m, allowing a guest to set the initial
> BAR position would also be feasible, but I haven't been able to find any device
> on my boxes that's not initialized by the firmware, hence it would be hard for
> me to test that.

Well, you could zap some instead of mapping them into Dom0's p2m,
you'd just need to be careful not to zap any which Dom0 needs for
booting (in Linux this may be no more than the graphics card and, if
used, a plug-in serial card, as everything else ought to come from
the initrd).

Jan
diff mbox

Patch

diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
index ca88c5835e..65f606d33a 100644
--- a/xen/arch/x86/hvm/dom0_build.c
+++ b/xen/arch/x86/hvm/dom0_build.c
@@ -64,27 +64,7 @@  static struct acpi_madt_nmi_source __initdata *nmisrc;
 static int __init modify_identity_mmio(struct domain *d, unsigned long pfn,
                                        unsigned long nr_pages, const bool map)
 {
-    int rc;
-
-    for ( ; ; )
-    {
-        rc = (map ? map_mmio_regions : unmap_mmio_regions)
-             (d, _gfn(pfn), nr_pages, _mfn(pfn));
-        if ( rc == 0 )
-            break;
-        if ( rc < 0 )
-        {
-            printk(XENLOG_WARNING
-                   "Failed to identity %smap [%#lx,%#lx) for d%d: %d\n",
-                   map ? "" : "un", pfn, pfn + nr_pages, d->domain_id, rc);
-            break;
-        }
-        nr_pages -= rc;
-        pfn += rc;
-        process_pending_softirqs();
-    }
-
-    return rc;
+    return modify_mmio(d, pfn, pfn, nr_pages, map);
 }
 
 /* Populate a HVM memory range using the biggest possible order. */
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 52879e7438..0d970482cb 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -1438,6 +1438,40 @@  int prepare_ring_for_helper(
     return 0;
 }
 
+int modify_mmio(struct domain *d, unsigned long gfn, unsigned long pfn,
+                unsigned long nr_pages, const bool map)
+{
+    int rc;
+
+    /*
+     * Make sure this function is only used by the hardware domain, because it
+     * can take an arbitrary long time, and could DoS the whole system.
+     */
+    ASSERT(is_hardware_domain(d));
+
+    for ( ; ; )
+    {
+        rc = (map ? map_mmio_regions : unmap_mmio_regions)
+             (d, _gfn(gfn), nr_pages, _mfn(pfn));
+        if ( rc == 0 )
+            break;
+        if ( rc < 0 )
+        {
+            printk(XENLOG_WARNING
+                   "Failed to %smap [%#lx, %#lx) -> [%#lx,%#lx) for d%d: %d\n",
+                   map ? "" : "un", gfn, gfn + nr_pages, pfn, pfn + nr_pages,
+                   d->domain_id, rc);
+            break;
+        }
+        nr_pages -= rc;
+        pfn += rc;
+        gfn += rc;
+        process_pending_softirqs();
+    }
+
+    return rc;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
index 8cd5a6b503..1308da44e7 100644
--- a/xen/include/xen/p2m-common.h
+++ b/xen/include/xen/p2m-common.h
@@ -13,4 +13,8 @@  int unmap_mmio_regions(struct domain *d,
                        unsigned long nr,
                        mfn_t mfn);
 
+
+int modify_mmio(struct domain *d, unsigned long gfn, unsigned long pfn,
+                unsigned long nr_pages, const bool map);
+
 #endif /* _XEN_P2M_COMMON_H */