[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen master] xen: Introduce non-broken hypercalls for the paging mempool size
commit 22b20bd98c025e06525410e3ab3494d5e63489f7 Author: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> AuthorDate: Fri Oct 21 14:13:00 2022 +0100 Commit: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> CommitDate: Thu Nov 17 16:51:51 2022 +0000 xen: Introduce non-broken hypercalls for the paging mempool size The existing XEN_DOMCTL_SHADOW_OP_{GET,SET}_ALLOCATION have problems: * All set_allocation() flavours have an overflow-before-widen bug when calculating "sc->mb << (20 - PAGE_SHIFT)". * All flavours have a granularity of 1M. This was tolerable when the size of the pool could only be set at the same granularity, but is broken now that ARM has a 16-page stopgap allocation in use. * All get_allocation() flavours round up, and in particular turn 0 into 1, meaning the get op returns junk before a successful set op. * The x86 flavours reject the hypercalls before the VM has vCPUs allocated, despite the pool size being a domain property. * Even the hypercall names are long-obsolete. Implement a better interface, which can be first used to unit test the behaviour, and subsequently correct a broken implementation. The old interface will be retired in due course. The unit of bytes (as opposed pages) is a deliberate API/ABI improvement to more easily support multiple page granularities. This is part of XSA-409 / CVE-2022-33747. Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx> Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx> Acked-by: Anthony PERARD <anthony.perard@xxxxxxxxxx> Release-acked-by: Henry Wang <Henry.Wang@xxxxxxx> --- tools/include/xenctrl.h | 3 +++ tools/libs/ctrl/xc_domain.c | 29 ++++++++++++++++++++++++++ xen/arch/arm/p2m.c | 26 +++++++++++++++++++++++ xen/arch/x86/include/asm/hap.h | 1 + xen/arch/x86/include/asm/shadow.h | 4 ++++ xen/arch/x86/mm/hap/hap.c | 11 ++++++++++ xen/arch/x86/mm/paging.c | 43 +++++++++++++++++++++++++++++++++++++++ xen/arch/x86/mm/shadow/common.c | 11 ++++++++++ xen/common/domctl.c | 14 +++++++++++++ xen/include/public/domctl.h | 24 +++++++++++++++++++++- xen/include/xen/domain.h | 3 +++ 11 files changed, 168 insertions(+), 1 deletion(-) diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h index 0c8b4c3aa7..23037874d3 100644 --- a/tools/include/xenctrl.h +++ b/tools/include/xenctrl.h @@ -893,6 +893,9 @@ long long xc_logdirty_control(xc_interface *xch, unsigned int mode, xc_shadow_op_stats_t *stats); +int xc_get_paging_mempool_size(xc_interface *xch, uint32_t domid, uint64_t *size); +int xc_set_paging_mempool_size(xc_interface *xch, uint32_t domid, uint64_t size); + int xc_sched_credit_domain_set(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit *sdom); diff --git a/tools/libs/ctrl/xc_domain.c b/tools/libs/ctrl/xc_domain.c index 14c0420c35..e939d07157 100644 --- a/tools/libs/ctrl/xc_domain.c +++ b/tools/libs/ctrl/xc_domain.c @@ -706,6 +706,35 @@ long long xc_logdirty_control(xc_interface *xch, return (rc == 0) ? domctl.u.shadow_op.pages : rc; } +int xc_get_paging_mempool_size(xc_interface *xch, uint32_t domid, uint64_t *size) +{ + int rc; + struct xen_domctl domctl = { + .cmd = XEN_DOMCTL_get_paging_mempool_size, + .domain = domid, + }; + + rc = do_domctl(xch, &domctl); + if ( rc ) + return rc; + + *size = domctl.u.paging_mempool.size; + return 0; +} + +int xc_set_paging_mempool_size(xc_interface *xch, uint32_t domid, uint64_t size) +{ + struct xen_domctl domctl = { + .cmd = XEN_DOMCTL_set_paging_mempool_size, + .domain = domid, + .u.paging_mempool = { + .size = size, + }, + }; + + return do_domctl(xch, &domctl); +} + int xc_domain_setmaxmem(xc_interface *xch, uint32_t domid, uint64_t max_memkb) diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c index 94d3b60b13..8c1972e582 100644 --- a/xen/arch/arm/p2m.c +++ b/xen/arch/arm/p2m.c @@ -100,6 +100,13 @@ unsigned int p2m_get_allocation(struct domain *d) return ROUNDUP(nr_pages, 1 << (20 - PAGE_SHIFT)) >> (20 - PAGE_SHIFT); } +/* Return the size of the pool, in bytes. */ +int arch_get_paging_mempool_size(struct domain *d, uint64_t *size) +{ + *size = (uint64_t)ACCESS_ONCE(d->arch.paging.p2m_total_pages) << PAGE_SHIFT; + return 0; +} + /* * Set the pool of pages to the required number of pages. * Returns 0 for success, non-zero for failure. @@ -157,6 +164,25 @@ int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted) return 0; } +int arch_set_paging_mempool_size(struct domain *d, uint64_t size) +{ + unsigned long pages = size >> PAGE_SHIFT; + bool preempted = false; + int rc; + + if ( (size & ~PAGE_MASK) || /* Non page-sized request? */ + pages != (size >> PAGE_SHIFT) ) /* 32-bit overflow? */ + return -EINVAL; + + spin_lock(&d->arch.paging.lock); + rc = p2m_set_allocation(d, pages, &preempted); + spin_unlock(&d->arch.paging.lock); + + ASSERT(preempted == (rc == -ERESTART)); + + return rc; +} + int p2m_teardown_allocation(struct domain *d) { int ret = 0; diff --git a/xen/arch/x86/include/asm/hap.h b/xen/arch/x86/include/asm/hap.h index 90dece29de..14d2f212da 100644 --- a/xen/arch/x86/include/asm/hap.h +++ b/xen/arch/x86/include/asm/hap.h @@ -47,6 +47,7 @@ int hap_track_dirty_vram(struct domain *d, extern const struct paging_mode *hap_paging_get_mode(struct vcpu *); int hap_set_allocation(struct domain *d, unsigned int pages, bool *preempted); unsigned int hap_get_allocation(struct domain *d); +int hap_get_allocation_bytes(struct domain *d, uint64_t *size); #endif /* XEN_HAP_H */ diff --git a/xen/arch/x86/include/asm/shadow.h b/xen/arch/x86/include/asm/shadow.h index 1365fe4805..dad876d294 100644 --- a/xen/arch/x86/include/asm/shadow.h +++ b/xen/arch/x86/include/asm/shadow.h @@ -97,6 +97,8 @@ void shadow_blow_tables_per_domain(struct domain *d); int shadow_set_allocation(struct domain *d, unsigned int pages, bool *preempted); +int shadow_get_allocation_bytes(struct domain *d, uint64_t *size); + #else /* !CONFIG_SHADOW_PAGING */ #define shadow_vcpu_teardown(v) ASSERT(is_pv_vcpu(v)) @@ -108,6 +110,8 @@ int shadow_set_allocation(struct domain *d, unsigned int pages, ({ ASSERT_UNREACHABLE(); -EOPNOTSUPP; }) #define shadow_set_allocation(d, pages, preempted) \ ({ ASSERT_UNREACHABLE(); -EOPNOTSUPP; }) +#define shadow_get_allocation_bytes(d, size) \ + ({ ASSERT_UNREACHABLE(); -EOPNOTSUPP; }) static inline void sh_remove_shadows(struct domain *d, mfn_t gmfn, int fast, int all) {} diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c index f809ea9aa6..0fc1b1d9ac 100644 --- a/xen/arch/x86/mm/hap/hap.c +++ b/xen/arch/x86/mm/hap/hap.c @@ -345,6 +345,17 @@ unsigned int hap_get_allocation(struct domain *d) + ((pg & ((1 << (20 - PAGE_SHIFT)) - 1)) ? 1 : 0)); } +int hap_get_allocation_bytes(struct domain *d, uint64_t *size) +{ + unsigned long pages = d->arch.paging.hap.total_pages; + + pages += d->arch.paging.hap.p2m_pages; + + *size = pages << PAGE_SHIFT; + + return 0; +} + /* Set the pool of pages to the required number of pages. * Returns 0 for success, non-zero for failure. */ int hap_set_allocation(struct domain *d, unsigned int pages, bool *preempted) diff --git a/xen/arch/x86/mm/paging.c b/xen/arch/x86/mm/paging.c index 3a355eee9c..8d579fa9a3 100644 --- a/xen/arch/x86/mm/paging.c +++ b/xen/arch/x86/mm/paging.c @@ -977,6 +977,49 @@ int __init paging_set_allocation(struct domain *d, unsigned int pages, } #endif +int arch_get_paging_mempool_size(struct domain *d, uint64_t *size) +{ + int rc; + + if ( is_pv_domain(d) ) /* TODO: Relax in due course */ + return -EOPNOTSUPP; + + if ( hap_enabled(d) ) + rc = hap_get_allocation_bytes(d, size); + else + rc = shadow_get_allocation_bytes(d, size); + + return rc; +} + +int arch_set_paging_mempool_size(struct domain *d, uint64_t size) +{ + unsigned long pages = size >> PAGE_SHIFT; + bool preempted = false; + int rc; + + if ( is_pv_domain(d) ) /* TODO: Relax in due course */ + return -EOPNOTSUPP; + + if ( size & ~PAGE_MASK || /* Non page-sized request? */ + pages != (unsigned int)pages ) /* Overflow $X_set_allocation()? */ + return -EINVAL; + + paging_lock(d); + if ( hap_enabled(d) ) + rc = hap_set_allocation(d, pages, &preempted); + else + rc = shadow_set_allocation(d, pages, &preempted); + paging_unlock(d); + + /* + * TODO: Adjust $X_set_allocation() so this is true. + ASSERT(preempted == (rc == -ERESTART)); + */ + + return preempted ? -ERESTART : rc; +} + /* * Local variables: * mode: C diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index badfd53c6b..a8404f97f6 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -1427,6 +1427,17 @@ static unsigned int shadow_get_allocation(struct domain *d) + ((pg & ((1 << (20 - PAGE_SHIFT)) - 1)) ? 1 : 0)); } +int shadow_get_allocation_bytes(struct domain *d, uint64_t *size) +{ + unsigned long pages = d->arch.paging.shadow.total_pages; + + pages += d->arch.paging.shadow.p2m_pages; + + *size = pages << PAGE_SHIFT; + + return 0; +} + /**************************************************************************/ /* Hash table for storing the guest->shadow mappings. * The table itself is an array of pointers to shadows; the shadows are then diff --git a/xen/common/domctl.c b/xen/common/domctl.c index 69fb9abd34..ad71ad8a4c 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -874,6 +874,20 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) ret = iommu_do_domctl(op, d, u_domctl); break; + case XEN_DOMCTL_get_paging_mempool_size: + ret = arch_get_paging_mempool_size(d, &op->u.paging_mempool.size); + if ( !ret ) + copyback = 1; + break; + + case XEN_DOMCTL_set_paging_mempool_size: + ret = arch_set_paging_mempool_size(d, op->u.paging_mempool.size); + + if ( ret == -ERESTART ) + ret = hypercall_create_continuation( + __HYPERVISOR_domctl, "h", u_domctl); + break; + default: ret = arch_do_domctl(op, d, u_domctl); break; diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 966bf4323c..51be28c3de 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -197,7 +197,10 @@ struct xen_domctl_getpageframeinfo3 { /* Return the bitmap but do not modify internal copy. */ #define XEN_DOMCTL_SHADOW_OP_PEEK 12 -/* Memory allocation accessors. */ +/* + * Memory allocation accessors. These APIs are broken and will be removed. + * Use XEN_DOMCTL_{get,set}_paging_mempool_size instead. + */ #define XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION 30 #define XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION 31 @@ -929,6 +932,22 @@ struct xen_domctl_cacheflush { xen_pfn_t start_pfn, nr_pfns; }; +/* + * XEN_DOMCTL_get_paging_mempool_size / XEN_DOMCTL_set_paging_mempool_size. + * + * Get or set the paging memory pool size. The size is in bytes. + * + * This is a dedicated pool of memory for Xen to use while managing the guest, + * typically containing pagetables. As such, there is an implementation + * specific minimum granularity. + * + * The set operation can fail mid-way through the request (e.g. Xen running + * out of memory, no free memory to reclaim from the pool, etc.). + */ +struct xen_domctl_paging_mempool { + uint64_aligned_t size; /* Size in bytes. */ +}; + #if defined(__i386__) || defined(__x86_64__) struct xen_domctl_vcpu_msr { uint32_t index; @@ -1257,6 +1276,8 @@ struct xen_domctl { #define XEN_DOMCTL_get_cpu_policy 82 #define XEN_DOMCTL_set_cpu_policy 83 #define XEN_DOMCTL_vmtrace_op 84 +#define XEN_DOMCTL_get_paging_mempool_size 85 +#define XEN_DOMCTL_set_paging_mempool_size 86 #define XEN_DOMCTL_gdbsx_guestmemio 1000 #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002 @@ -1318,6 +1339,7 @@ struct xen_domctl { struct xen_domctl_psr_alloc psr_alloc; struct xen_domctl_vuart_op vuart_op; struct xen_domctl_vmtrace_op vmtrace_op; + struct xen_domctl_paging_mempool paging_mempool; uint8_t pad[128]; } u; }; diff --git a/xen/include/xen/domain.h b/xen/include/xen/domain.h index 2c8116afba..0de9cbc169 100644 --- a/xen/include/xen/domain.h +++ b/xen/include/xen/domain.h @@ -98,6 +98,9 @@ void arch_get_info_guest(struct vcpu *, vcpu_guest_context_u); int arch_initialise_vcpu(struct vcpu *v, XEN_GUEST_HANDLE_PARAM(void) arg); int default_initialise_vcpu(struct vcpu *v, XEN_GUEST_HANDLE_PARAM(void) arg); +int arch_get_paging_mempool_size(struct domain *d, uint64_t *size /* bytes */); +int arch_set_paging_mempool_size(struct domain *d, uint64_t size /* bytes */); + int domain_relinquish_resources(struct domain *d); void dump_pageframe_info(struct domain *d); -- generated by git-patchbot for /home/xen/git/xen.git#master
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |