Xen Security Advisory 125 (CVE-2015-2752) - Long latency MMIO mapping operations are not preemptible
Xen.org security team <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.announce |
|---|---|
| Message-ID | <E1YcuzQ-0005LQ-PR__2121.22083544291$1427804466$gmane$org@xenbits.xen.org> |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Xen Security Advisory CVE-2015-2752 / XSA-125
version 3
Long latency MMIO mapping operations are not preemptible
UPDATES IN VERSION 3
====================
CVE assigned.
Public release.
ISSUE DESCRIPTION
=================
The XEN_DOMCTL_memory_mapping hypercall allows long running operations
without implementing preemption.
This hypercall is used by the device model as part of the emulation
associated with configuration of PCI devices passed through to HVM
guests and is therefore indirectly exposed to those guests.
This can cause a physical CPU to become busy for a significant period,
leading to a host denial of service in some cases.
If a host denial of service is not triggered then it may instead be
possible to deny service to the domain running the device model,
e.g. domain 0.
This hypercall is also exposed more generally to all
toolstacks. However the uses of it in libxl based toolstacks are not
believed to open up any avenue of attack from an untrusted
guest. Other toolstacks may be vulnerable however.
IMPACT
======
The vulnerability is exposed via HVM guests which have a PCI device
assigned to them. A malicious HVM guest in such a configuration can
mount a denial of service attack affecting the whole system via its
associated device model (qemu-dm).
A guest is able to trigger this hypercall via operations which it is
legitimately expected to perform, therefore running the device model
as a stub domain does not offer protection against the host denial of
service issue. However it does offer some protection against secondary
issues such as denial of service against dom0.
VULNERABLE SYSTEMS
==================
The issue is exposed via x86 HVM VMs which have been assigned a PCI
device.
x86 PV domains, x86 HVM domains without passthrough devices and ARM
domains do not expose this vulnerability.
Xen 3.2.x and later are vulnerable.
Xen 3.1.x and earlier have not been inspected.
MITIGATION
==========
Running only PV guests will avoid this issue.
This issue can be avoided by not assigning devices with large MMIO
regions to untrusted HVM guests.
CREDITS
=======
This issue was discovered by Konrad Rzeszutek Wilk of Oracle.
RESOLUTION
==========
Applying the appropriate attached patch resolves this issue.
xsa125.patch Xen 4.5.x, xen-unstable
xsa125-4.4.patch Xen 4.4.x
xsa125-4.3.patch Xen 4.3.x
xsa125-4.2.patch Xen 4.2.x
$ sha256sum xsa125*.patch
be0c7cceb1af4b7b1341f37c1e20cf804ea3ac7d3c2ca2e5599f936479d5e0de xsa125.patch
5f081407c2955787c6e40daa847f3c4131694dff3bb0bc0ee55495f555c7bb52 xsa125-4.2.patch
3b0641ef2a23f12872267940c408097cb353e57a6e0396a64cdf13592a14f65b xsa125-4.3.patch
2180e657b34d8628d4e0157adf2a36904bb6feaf55d53338e4457ef77d867a31 xsa125-4.4.patch
$
DEPLOYMENT DURING EMBARGO
=========================
Deployment of the patches and/or mitigations described above (or
others which are substantially similar) is permitted during the
embargo, even on public-facing systems with untrusted guest users and
administrators.
But: Distribution of updated software is prohibited (except to other
members of the predisclosure list).
Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.
(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable. This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)
For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)
iQEcBAEBAgAGBQJVGo5JAAoJEIP+FMlX6CvZlEAIAMdSMKpxum+J9IbUFCqcHFa4
F8zQDkz2hMCY3OjTAq9+n6KR2LLyKDn2hGDP0Mspbo67lRBEjSkp7KEXCoDrA294
YsVuJn8y0T3yPH9du3m0f2vi49MrhnxnUZLNyKCpkxTiClrC/7JX3OZxQTQIGpzf
EIsjYP+/w9ava5XYbGKorwlLvGpjRmnZpCDTrZlqKV2bK2O6pWzyvp5zD99FORcJ
YVRIGebKu8szbSHZs9ectt4xkZwYrzSjj0+PtryvwLSpSYi0zTWIu9rrgd/ZCXfL
tgD+i9zoc2E1ydPlvdKRXEdRHY9gGcaimfbTqYn1ttJ6qQcnbMoRQor4X+v92NU=
=m83F
-----END PGP SIGNATURE-----
_______________________________________________
Xen-announce mailing list
[email protected]
http://lists.xen.org/xen-announce
xsa125.patch
(application/octet-stream, 5.4 KB)
From 98670acc98cad5aee0e0714694a64d3b96675c36 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk <[email protected]> Date: Wed, 19 Nov 2014 12:57:11 -0500 Subject: [PATCH] Limit XEN_DOMCTL_memory_mapping hypercall to only process up to 64 GFNs (or less) Said hypercall for large BARs can take quite a while. As such we can require that the hypercall MUST break up the request in smaller values. Another approach is to add preemption to it - whether we do the preemption using hypercall_create_continuation or returning EAGAIN to userspace (and have it re-invocate the call) - either way the issue we cannot easily solve is that in 'map_mmio_regions' if we encounter an error we MUST call 'unmap_mmio_regions' for the whole BAR region. Since the preemption would re-use input fields such as nr_mfns, first_gfn, first_mfn - we would lose the original values - and only undo what was done in the current round (i.e. ignoring anything that was done prior to earlier preemptions). Unless we re-used the return value as 'EAGAIN|nr_mfns_done<<10' but that puts a limit (since the return value is a long) on the amount of nr_mfns that can provided. This patch sidesteps this problem by: - Setting an hard limit of nr_mfns having to be 64 or less. - Toolstack adjusts correspondingly to the nr_mfn limit. - If the there is an error when adding the toolstack will call the remove operation to remove the whole region. The need to break this hypercall down is for large BARs can take more than the guest (initial domain usually) time-slice. This has the negative result in that the guest is locked out for a long duration and is unable to act on any pending events. We also augment the code to return zero if nr_mfns instead of trying to the hypercall. Suggested-by: Jan Beulich <[email protected]> Acked-by: Jan Beulich <[email protected]> Signed-off-by: Konrad Rzeszutek Wilk <[email protected]> Acked-by: Ian Campbell <[email protected]> --- [v50: Simplify loop] [v51: If max_batch_sz 1 (or less) we would return zero. Fix that] [v52: Handle nr_mfns being zero] [v53: Fix up return value] --- tools/libxc/xc_domain.c | 46 +++++++++++++++++++++++++++++++++++++++++---- xen/common/domctl.c | 5 +++++ xen/include/public/domctl.h | 1 + 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index 845d1d7..bba7672 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -1988,6 +1988,8 @@ int xc_domain_memory_mapping( { DECLARE_DOMCTL; xc_dominfo_t info; + int ret = 0, err; + unsigned long done = 0, nr, max_batch_sz; if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || info.domid != domid ) @@ -1998,14 +2000,50 @@ int xc_domain_memory_mapping( if ( !xc_core_arch_auto_translated_physmap(&info) ) return 0; + if ( !nr_mfns ) + return 0; + domctl.cmd = XEN_DOMCTL_memory_mapping; domctl.domain = domid; - domctl.u.memory_mapping.first_gfn = first_gfn; - domctl.u.memory_mapping.first_mfn = first_mfn; - domctl.u.memory_mapping.nr_mfns = nr_mfns; domctl.u.memory_mapping.add_mapping = add_mapping; + max_batch_sz = nr_mfns; + do + { + nr = min(nr_mfns - done, max_batch_sz); + domctl.u.memory_mapping.nr_mfns = nr; + domctl.u.memory_mapping.first_gfn = first_gfn + done; + domctl.u.memory_mapping.first_mfn = first_mfn + done; + err = do_domctl(xch, &domctl); + if ( err && errno == E2BIG ) + { + if ( max_batch_sz <= 1 ) + break; + max_batch_sz >>= 1; + continue; + } + /* Save the first error... */ + if ( !ret ) + ret = err; + /* .. and ignore the rest of them when removing. */ + if ( err && add_mapping != DPCI_REMOVE_MAPPING ) + break; - return do_domctl(xch, &domctl); + done += nr; + } while ( done < nr_mfns ); + + /* + * Undo what we have done unless unmapping, by unmapping the entire region. + * Errors here are ignored. + */ + if ( ret && add_mapping != DPCI_REMOVE_MAPPING ) + xc_domain_memory_mapping(xch, domid, first_gfn, first_mfn, nr_mfns, + DPCI_REMOVE_MAPPING); + + /* We might get E2BIG so many times that we never advance. */ + if ( !done && !ret ) + ret = -1; + + return ret; } int xc_domain_ioport_mapping( diff --git a/xen/common/domctl.c b/xen/common/domctl.c index d396cc4..c2e60a7 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -1027,6 +1027,11 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) (gfn + nr_mfns - 1) < gfn ) /* wrap? */ break; + ret = -E2BIG; + /* Must break hypercall up as this could take a while. */ + if ( nr_mfns > 64 ) + break; + ret = -EPERM; if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || !iomem_access_permitted(d, mfn, mfn_end) ) diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index ca0e51e..0c9f474 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -543,6 +543,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_bind_pt_irq_t); /* Bind machine I/O address range -> HVM address range. */ +/* If this returns -E2BIG lower nr_mfns value. */ /* XEN_DOMCTL_memory_mapping */ #define DPCI_ADD_MAPPING 1 #define DPCI_REMOVE_MAPPING 0 -- 2.1.0
xsa125-4.2.patch
(application/octet-stream, 5.1 KB)
Limit XEN_DOMCTL_memory_mapping hypercall to only process up to 64 GFNs (or less) Said hypercall for large BARs can take quite a while. As such we can require that the hypercall MUST break up the request in smaller values. Another approach is to add preemption to it - whether we do the preemption using hypercall_create_continuation or returning EAGAIN to userspace (and have it re-invocate the call) - either way the issue we cannot easily solve is that in 'map_mmio_regions' if we encounter an error we MUST call 'unmap_mmio_regions' for the whole BAR region. Since the preemption would re-use input fields such as nr_mfns, first_gfn, first_mfn - we would lose the original values - and only undo what was done in the current round (i.e. ignoring anything that was done prior to earlier preemptions). Unless we re-used the return value as 'EAGAIN|nr_mfns_done<<10' but that puts a limit (since the return value is a long) on the amount of nr_mfns that can provided. This patch sidesteps this problem by: - Setting an hard limit of nr_mfns having to be 64 or less. - Toolstack adjusts correspondingly to the nr_mfn limit. - If the there is an error when adding the toolstack will call the remove operation to remove the whole region. The need to break this hypercall down is for large BARs can take more than the guest (initial domain usually) time-slice. This has the negative result in that the guest is locked out for a long duration and is unable to act on any pending events. We also augment the code to return zero if nr_mfns instead of trying to the hypercall. Suggested-by: Jan Beulich <[email protected]> Acked-by: Jan Beulich <[email protected]> Signed-off-by: Konrad Rzeszutek Wilk <[email protected]> Acked-by: Ian Campbell <[email protected]> diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index d98e68b..60f7137 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -1352,6 +1352,13 @@ int xc_domain_bind_pt_isa_irq( PT_IRQ_TYPE_ISA, 0, 0, 0, machine_irq)); } +#ifndef min +#define min(X, Y) ({ \ + const typeof (X) _x = (X); \ + const typeof (Y) _y = (Y); \ + (void) (&_x == &_y); \ + (_x < _y) ? _x : _y; }) +#endif int xc_domain_memory_mapping( xc_interface *xch, uint32_t domid, @@ -1361,17 +1368,55 @@ int xc_domain_memory_mapping( uint32_t add_mapping) { DECLARE_DOMCTL; + int ret = 0, err; + unsigned long done = 0, nr, max_batch_sz; + + if ( !nr_mfns ) + return 0; domctl.cmd = XEN_DOMCTL_memory_mapping; domctl.domain = domid; - domctl.u.memory_mapping.first_gfn = first_gfn; - domctl.u.memory_mapping.first_mfn = first_mfn; - domctl.u.memory_mapping.nr_mfns = nr_mfns; domctl.u.memory_mapping.add_mapping = add_mapping; + max_batch_sz = nr_mfns; + do + { + nr = min(nr_mfns - done, max_batch_sz); + domctl.u.memory_mapping.nr_mfns = nr; + domctl.u.memory_mapping.first_gfn = first_gfn + done; + domctl.u.memory_mapping.first_mfn = first_mfn + done; + err = do_domctl(xch, &domctl); + if ( err && errno == E2BIG ) + { + if ( max_batch_sz <= 1 ) + break; + max_batch_sz >>= 1; + continue; + } + /* Save the first error... */ + if ( !ret ) + ret = err; + /* .. and ignore the rest of them when removing. */ + if ( err && add_mapping != DPCI_REMOVE_MAPPING ) + break; - return do_domctl(xch, &domctl); -} + done += nr; + } while ( done < nr_mfns ); + /* + * Undo what we have done unless unmapping, by unmapping the entire region. + * Errors here are ignored. + */ + if ( ret && add_mapping != DPCI_REMOVE_MAPPING ) + xc_domain_memory_mapping(xch, domid, first_gfn, first_mfn, nr_mfns, + DPCI_REMOVE_MAPPING); + + /* We might get E2BIG so many times that we never advance. */ + if ( !done && !ret ) + ret = -1; + + return ret; +} +#undef min int xc_domain_ioport_mapping( xc_interface *xch, uint32_t domid, diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c index 8bf8f09..c9a97c9 100644 --- a/xen/arch/x86/domctl.c +++ b/xen/arch/x86/domctl.c @@ -867,6 +867,11 @@ long arch_do_domctl( (gfn + nr_mfns - 1) < gfn ) /* wrap? */ break; + ret = -E2BIG; + /* Must break hypercall up as this could take a while. */ + if ( nr_mfns > 64 ) + break; + ret = -EPERM; if ( !IS_PRIV(current->domain) && !iomem_access_permitted(current->domain, mfn, mfn + nr_mfns - 1) ) diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 240ceb9..b45911e 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -507,6 +507,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_bind_pt_irq_t); /* Bind machine I/O address range -> HVM address range. */ +/* If this returns -E2BIG lower nr_mfns value. */ /* XEN_DOMCTL_memory_mapping */ #define DPCI_ADD_MAPPING 1 #define DPCI_REMOVE_MAPPING 0
xsa125-4.3.patch
(application/octet-stream, 5.1 KB)
Limit XEN_DOMCTL_memory_mapping hypercall to only process up to 64 GFNs (or less) Said hypercall for large BARs can take quite a while. As such we can require that the hypercall MUST break up the request in smaller values. Another approach is to add preemption to it - whether we do the preemption using hypercall_create_continuation or returning EAGAIN to userspace (and have it re-invocate the call) - either way the issue we cannot easily solve is that in 'map_mmio_regions' if we encounter an error we MUST call 'unmap_mmio_regions' for the whole BAR region. Since the preemption would re-use input fields such as nr_mfns, first_gfn, first_mfn - we would lose the original values - and only undo what was done in the current round (i.e. ignoring anything that was done prior to earlier preemptions). Unless we re-used the return value as 'EAGAIN|nr_mfns_done<<10' but that puts a limit (since the return value is a long) on the amount of nr_mfns that can provided. This patch sidesteps this problem by: - Setting an hard limit of nr_mfns having to be 64 or less. - Toolstack adjusts correspondingly to the nr_mfn limit. - If the there is an error when adding the toolstack will call the remove operation to remove the whole region. The need to break this hypercall down is for large BARs can take more than the guest (initial domain usually) time-slice. This has the negative result in that the guest is locked out for a long duration and is unable to act on any pending events. We also augment the code to return zero if nr_mfns instead of trying to the hypercall. Suggested-by: Jan Beulich <[email protected]> Acked-by: Jan Beulich <[email protected]> Signed-off-by: Konrad Rzeszutek Wilk <[email protected]> Acked-by: Ian Campbell <[email protected]> diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index 3257e2a..7386e58 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -1467,6 +1467,13 @@ int xc_domain_bind_pt_isa_irq( PT_IRQ_TYPE_ISA, 0, 0, 0, machine_irq)); } +#ifndef min +#define min(X, Y) ({ \ + const typeof (X) _x = (X); \ + const typeof (Y) _y = (Y); \ + (void) (&_x == &_y); \ + (_x < _y) ? _x : _y; }) +#endif int xc_domain_memory_mapping( xc_interface *xch, uint32_t domid, @@ -1476,17 +1483,55 @@ int xc_domain_memory_mapping( uint32_t add_mapping) { DECLARE_DOMCTL; + int ret = 0, err; + unsigned long done = 0, nr, max_batch_sz; + + if ( !nr_mfns ) + return 0; domctl.cmd = XEN_DOMCTL_memory_mapping; domctl.domain = domid; - domctl.u.memory_mapping.first_gfn = first_gfn; - domctl.u.memory_mapping.first_mfn = first_mfn; - domctl.u.memory_mapping.nr_mfns = nr_mfns; domctl.u.memory_mapping.add_mapping = add_mapping; + max_batch_sz = nr_mfns; + do + { + nr = min(nr_mfns - done, max_batch_sz); + domctl.u.memory_mapping.nr_mfns = nr; + domctl.u.memory_mapping.first_gfn = first_gfn + done; + domctl.u.memory_mapping.first_mfn = first_mfn + done; + err = do_domctl(xch, &domctl); + if ( err && errno == E2BIG ) + { + if ( max_batch_sz <= 1 ) + break; + max_batch_sz >>= 1; + continue; + } + /* Save the first error... */ + if ( !ret ) + ret = err; + /* .. and ignore the rest of them when removing. */ + if ( err && add_mapping != DPCI_REMOVE_MAPPING ) + break; - return do_domctl(xch, &domctl); -} + done += nr; + } while ( done < nr_mfns ); + /* + * Undo what we have done unless unmapping, by unmapping the entire region. + * Errors here are ignored. + */ + if ( ret && add_mapping != DPCI_REMOVE_MAPPING ) + xc_domain_memory_mapping(xch, domid, first_gfn, first_mfn, nr_mfns, + DPCI_REMOVE_MAPPING); + + /* We might get E2BIG so many times that we never advance. */ + if ( !done && !ret ) + ret = -1; + + return ret; +} +#undef min int xc_domain_ioport_mapping( xc_interface *xch, uint32_t domid, diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c index 173bf01..306297a 100644 --- a/xen/arch/x86/domctl.c +++ b/xen/arch/x86/domctl.c @@ -655,6 +655,11 @@ long arch_do_domctl( (gfn + nr_mfns - 1) < gfn ) /* wrap? */ break; + ret = -E2BIG; + /* Must break hypercall up as this could take a while. */ + if ( nr_mfns > 64 ) + break; + ret = -EPERM; if ( !iomem_access_permitted(current->domain, mfn, mfn + nr_mfns - 1) ) break; diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index d381903..8f9d5c0 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -513,6 +513,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_bind_pt_irq_t); /* Bind machine I/O address range -> HVM address range. */ +/* If this returns -E2BIG lower nr_mfns value. */ /* XEN_DOMCTL_memory_mapping */ #define DPCI_ADD_MAPPING 1 #define DPCI_REMOVE_MAPPING 0
xsa125-4.4.patch
(application/octet-stream, 5.4 KB)
From df2922ce672cc35500e2f3ba041441021f44b41c Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk <[email protected]> Date: Wed, 19 Nov 2014 12:57:11 -0500 Subject: [PATCH] Limit XEN_DOMCTL_memory_mapping hypercall to only process up to 64 GFNs (or less) Said hypercall for large BARs can take quite a while. As such we can require that the hypercall MUST break up the request in smaller values. Another approach is to add preemption to it - whether we do the preemption using hypercall_create_continuation or returning EAGAIN to userspace (and have it re-invocate the call) - either way the issue we cannot easily solve is that in 'map_mmio_regions' if we encounter an error we MUST call 'unmap_mmio_regions' for the whole BAR region. Since the preemption would re-use input fields such as nr_mfns, first_gfn, first_mfn - we would lose the original values - and only undo what was done in the current round (i.e. ignoring anything that was done prior to earlier preemptions). Unless we re-used the return value as 'EAGAIN|nr_mfns_done<<10' but that puts a limit (since the return value is a long) on the amount of nr_mfns that can provided. This patch sidesteps this problem by: - Setting an hard limit of nr_mfns having to be 64 or less. - Toolstack adjusts correspondingly to the nr_mfn limit. - If the there is an error when adding the toolstack will call the remove operation to remove the whole region. The need to break this hypercall down is for large BARs can take more than the guest (initial domain usually) time-slice. This has the negative result in that the guest is locked out for a long duration and is unable to act on any pending events. We also augment the code to return zero if nr_mfns instead of trying to the hypercall. Suggested-by: Jan Beulich <[email protected]> Acked-by: Jan Beulich <[email protected]> Signed-off-by: Konrad Rzeszutek Wilk <[email protected]> Acked-by: Ian Campbell <[email protected]> --- tools/libxc/xc_domain.c | 55 ++++++++++++++++++++++++++++++++++++++++----- xen/arch/x86/domctl.c | 5 +++++ xen/include/public/domctl.h | 1 + 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index 369c3f3..40ca771 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -1641,6 +1641,13 @@ failed: return -1; } +#ifndef min +#define min(X, Y) ({ \ + const typeof (X) _x = (X); \ + const typeof (Y) _y = (Y); \ + (void) (&_x == &_y); \ + (_x < _y) ? _x : _y; }) +#endif int xc_domain_memory_mapping( xc_interface *xch, uint32_t domid, @@ -1650,17 +1657,55 @@ int xc_domain_memory_mapping( uint32_t add_mapping) { DECLARE_DOMCTL; + int ret = 0, err; + unsigned long done = 0, nr, max_batch_sz; + + if ( !nr_mfns ) + return 0; domctl.cmd = XEN_DOMCTL_memory_mapping; domctl.domain = domid; - domctl.u.memory_mapping.first_gfn = first_gfn; - domctl.u.memory_mapping.first_mfn = first_mfn; - domctl.u.memory_mapping.nr_mfns = nr_mfns; domctl.u.memory_mapping.add_mapping = add_mapping; + max_batch_sz = nr_mfns; + do + { + nr = min(nr_mfns - done, max_batch_sz); + domctl.u.memory_mapping.nr_mfns = nr; + domctl.u.memory_mapping.first_gfn = first_gfn + done; + domctl.u.memory_mapping.first_mfn = first_mfn + done; + err = do_domctl(xch, &domctl); + if ( err && errno == E2BIG ) + { + if ( max_batch_sz <= 1 ) + break; + max_batch_sz >>= 1; + continue; + } + /* Save the first error... */ + if ( !ret ) + ret = err; + /* .. and ignore the rest of them when removing. */ + if ( err && add_mapping != DPCI_REMOVE_MAPPING ) + break; - return do_domctl(xch, &domctl); -} + done += nr; + } while ( done < nr_mfns ); + /* + * Undo what we have done unless unmapping, by unmapping the entire region. + * Errors here are ignored. + */ + if ( ret && add_mapping != DPCI_REMOVE_MAPPING ) + xc_domain_memory_mapping(xch, domid, first_gfn, first_mfn, nr_mfns, + DPCI_REMOVE_MAPPING); + + /* We might get E2BIG so many times that we never advance. */ + if ( !done && !ret ) + ret = -1; + + return ret; +} +#undef min int xc_domain_ioport_mapping( xc_interface *xch, uint32_t domid, diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c index a967b65..9b72c22 100644 --- a/xen/arch/x86/domctl.c +++ b/xen/arch/x86/domctl.c @@ -653,6 +653,11 @@ long arch_do_domctl( (gfn + nr_mfns - 1) < gfn ) /* wrap? */ break; + ret = -E2BIG; + /* Must break hypercall up as this could take a while. */ + if ( nr_mfns > 64 ) + break; + ret = -EPERM; if ( !iomem_access_permitted(current->domain, mfn, mfn + nr_mfns - 1) ) break; diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index f22fe2e..c45bc59 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -518,6 +518,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_bind_pt_irq_t); /* Bind machine I/O address range -> HVM address range. */ +/* If this returns -E2BIG lower nr_mfns value. */ /* XEN_DOMCTL_memory_mapping */ #define DPCI_ADD_MAPPING 1 #define DPCI_REMOVE_MAPPING 0 -- 2.1.0