|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [XEN PATCH v2 04/15] x86/p2m: move altp2m-related code to separate file
On Wed, 15 May 2024, Sergiy Kibrik wrote:
> Move altp2m code from generic p2m.c file to altp2m.c, so it is kept separately
> and can possibly be disabled in the build. We may want to disable it when
> building for specific platform only, that doesn't support alternate p2m.
>
> No functional change intended.
>
> Signed-off-by: Sergiy Kibrik <Sergiy_Kibrik@xxxxxxxx>
> CC: Tamas K Lengyel <tamas@xxxxxxxxxxxxx>
> CC: Jan Beulich <jbeulich@xxxxxxxx>
Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
> ---
> changes in v2:
> - no double blank lines
> - no unrelated re-formatting
> - header #include-s ordering
> - changed patch description
> ---
> xen/arch/x86/mm/altp2m.c | 630 ++++++++++++++++++++++++++++++++++++++
> xen/arch/x86/mm/p2m.c | 632 +--------------------------------------
> xen/arch/x86/mm/p2m.h | 3 +
> 3 files changed, 635 insertions(+), 630 deletions(-)
>
> diff --git a/xen/arch/x86/mm/altp2m.c b/xen/arch/x86/mm/altp2m.c
> index a04297b646..6fe1e9ed6b 100644
> --- a/xen/arch/x86/mm/altp2m.c
> +++ b/xen/arch/x86/mm/altp2m.c
> @@ -7,6 +7,8 @@
> #include <asm/hvm/hvm.h>
> #include <asm/p2m.h>
> #include <asm/altp2m.h>
> +#include <public/hvm/hvm_op.h>
> +#include <xen/event.h>
> #include "mm-locks.h"
> #include "p2m.h"
>
> @@ -151,6 +153,634 @@ void p2m_teardown_altp2m(struct domain *d)
> }
> }
>
> +int altp2m_get_effective_entry(struct p2m_domain *ap2m, gfn_t gfn, mfn_t
> *mfn,
> + p2m_type_t *t, p2m_access_t *a,
> + bool prepopulate)
> +{
> + *mfn = ap2m->get_entry(ap2m, gfn, t, a, 0, NULL, NULL);
> +
> + /* Check host p2m if no valid entry in alternate */
> + if ( !mfn_valid(*mfn) && !p2m_is_hostp2m(ap2m) )
> + {
> + struct p2m_domain *hp2m = p2m_get_hostp2m(ap2m->domain);
> + unsigned int page_order;
> + int rc;
> +
> + *mfn = p2m_get_gfn_type_access(hp2m, gfn, t, a, P2M_ALLOC |
> P2M_UNSHARE,
> + &page_order, 0);
> +
> + rc = -ESRCH;
> + if ( !mfn_valid(*mfn) || *t != p2m_ram_rw )
> + return rc;
> +
> + /* If this is a superpage, copy that first */
> + if ( prepopulate && page_order != PAGE_ORDER_4K )
> + {
> + unsigned long mask = ~((1UL << page_order) - 1);
> + gfn_t gfn_aligned = _gfn(gfn_x(gfn) & mask);
> + mfn_t mfn_aligned = _mfn(mfn_x(*mfn) & mask);
> +
> + rc = ap2m->set_entry(ap2m, gfn_aligned, mfn_aligned, page_order,
> *t, *a, 1);
> + if ( rc )
> + return rc;
> + }
> + }
> +
> + return 0;
> +}
> +
> +void p2m_altp2m_check(struct vcpu *v, uint16_t idx)
> +{
> + if ( altp2m_active(v->domain) )
> + p2m_switch_vcpu_altp2m_by_id(v, idx);
> +}
> +
> +bool p2m_switch_vcpu_altp2m_by_id(struct vcpu *v, unsigned int idx)
> +{
> + struct domain *d = v->domain;
> + bool rc = false;
> +
> + if ( idx >= MAX_ALTP2M )
> + return rc;
> +
> + altp2m_list_lock(d);
> +
> + if ( d->arch.altp2m_eptp[idx] != mfn_x(INVALID_MFN) )
> + {
> + if ( p2m_set_altp2m(v, idx) )
> + altp2m_vcpu_update_p2m(v);
> + rc = 1;
> + }
> +
> + altp2m_list_unlock(d);
> + return rc;
> +}
> +
> +/*
> + * Read info about the gfn in an altp2m, locking the gfn.
> + *
> + * If the entry is valid, pass the results back to the caller.
> + *
> + * If the entry was invalid, and the host's entry is also invalid,
> + * return to the caller without any changes.
> + *
> + * If the entry is invalid, and the host entry was valid, propagate
> + * the host's entry to the altp2m (retaining page order), and indicate
> + * that the caller should re-try the faulting instruction.
> + */
> +bool p2m_altp2m_get_or_propagate(struct p2m_domain *ap2m, unsigned long
> gfn_l,
> + mfn_t *mfn, p2m_type_t *p2mt,
> + p2m_access_t *p2ma, unsigned int
> *page_order)
> +{
> + p2m_type_t ap2mt;
> + p2m_access_t ap2ma;
> + unsigned int cur_order;
> + unsigned long mask;
> + gfn_t gfn;
> + mfn_t amfn;
> + int rc;
> +
> + /*
> + * NB we must get the full lock on the altp2m here, in addition to
> + * the lock on the individual gfn, since we may change a range of
> + * gfns below.
> + */
> + p2m_lock(ap2m);
> +
> + amfn = get_gfn_type_access(ap2m, gfn_l, &ap2mt, &ap2ma, 0, &cur_order);
> +
> + if ( cur_order > *page_order )
> + cur_order = *page_order;
> +
> + if ( !mfn_eq(amfn, INVALID_MFN) )
> + {
> + p2m_unlock(ap2m);
> + *mfn = amfn;
> + *p2mt = ap2mt;
> + *p2ma = ap2ma;
> + *page_order = cur_order;
> + return false;
> + }
> +
> + /* Host entry is also invalid; don't bother setting the altp2m entry. */
> + if ( mfn_eq(*mfn, INVALID_MFN) )
> + {
> + p2m_unlock(ap2m);
> + *page_order = cur_order;
> + return false;
> + }
> +
> + /*
> + * If this is a superpage mapping, round down both frame numbers
> + * to the start of the superpage. NB that we repupose `amfn`
> + * here.
> + */
> + mask = ~((1UL << cur_order) - 1);
> + amfn = _mfn(mfn_x(*mfn) & mask);
> + gfn = _gfn(gfn_l & mask);
> +
> + /* Override the altp2m entry with its default access. */
> + *p2ma = ap2m->default_access;
> +
> + rc = p2m_set_entry(ap2m, gfn, amfn, cur_order, *p2mt, *p2ma);
> + p2m_unlock(ap2m);
> +
> + if ( rc )
> + {
> + gprintk(XENLOG_ERR,
> + "failed to set entry for %"PRI_gfn" -> %"PRI_mfn" altp2m %u,
> rc %d\n",
> + gfn_l, mfn_x(amfn), vcpu_altp2m(current).p2midx, rc);
> + domain_crash(ap2m->domain);
> + }
> +
> + return true;
> +}
> +
> +enum altp2m_reset_type {
> + ALTP2M_RESET,
> + ALTP2M_DEACTIVATE
> +};
> +
> +static void p2m_reset_altp2m(struct domain *d, unsigned int idx,
> + enum altp2m_reset_type reset_type)
> +{
> + struct p2m_domain *p2m;
> +
> + ASSERT(idx < MAX_ALTP2M);
> + p2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> +
> + p2m_lock(p2m);
> +
> + p2m_flush_table_locked(p2m);
> +
> + if ( reset_type == ALTP2M_DEACTIVATE )
> + p2m_free_logdirty(p2m);
> +
> + /* Uninit and reinit ept to force TLB shootdown */
> + ept_p2m_uninit(p2m);
> + ept_p2m_init(p2m);
> +
> + p2m->min_remapped_gfn = gfn_x(INVALID_GFN);
> + p2m->max_remapped_gfn = 0;
> +
> + p2m_unlock(p2m);
> +}
> +
> +void p2m_flush_altp2m(struct domain *d)
> +{
> + unsigned int i;
> +
> + altp2m_list_lock(d);
> +
> + for ( i = 0; i < MAX_ALTP2M; i++ )
> + {
> + p2m_reset_altp2m(d, i, ALTP2M_DEACTIVATE);
> + d->arch.altp2m_eptp[i] = mfn_x(INVALID_MFN);
> + d->arch.altp2m_visible_eptp[i] = mfn_x(INVALID_MFN);
> + }
> +
> + altp2m_list_unlock(d);
> +}
> +
> +static int p2m_activate_altp2m(struct domain *d, unsigned int idx,
> + p2m_access_t hvmmem_default_access)
> +{
> + struct p2m_domain *hostp2m, *p2m;
> + int rc;
> +
> + ASSERT(idx < MAX_ALTP2M);
> +
> + p2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> + hostp2m = p2m_get_hostp2m(d);
> +
> + p2m_lock(p2m);
> +
> + rc = p2m_init_logdirty(p2m);
> +
> + if ( rc )
> + goto out;
> +
> + /* The following is really just a rangeset copy. */
> + rc = rangeset_merge(p2m->logdirty_ranges, hostp2m->logdirty_ranges);
> +
> + if ( rc )
> + {
> + p2m_free_logdirty(p2m);
> + goto out;
> + }
> +
> + p2m->default_access = hvmmem_default_access;
> + p2m->domain = hostp2m->domain;
> + p2m->global_logdirty = hostp2m->global_logdirty;
> + p2m->min_remapped_gfn = gfn_x(INVALID_GFN);
> + p2m->max_mapped_pfn = p2m->max_remapped_gfn = 0;
> +
> + p2m_init_altp2m_ept(d, idx);
> +
> + out:
> + p2m_unlock(p2m);
> +
> + return rc;
> +}
> +
> +int p2m_init_altp2m_by_id(struct domain *d, unsigned int idx)
> +{
> + int rc = -EINVAL;
> + struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> +
> + if ( idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) )
> + return rc;
> +
> + altp2m_list_lock(d);
> +
> + if ( d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] ==
> + mfn_x(INVALID_MFN) )
> + rc = p2m_activate_altp2m(d, idx, hostp2m->default_access);
> +
> + altp2m_list_unlock(d);
> + return rc;
> +}
> +
> +int p2m_init_next_altp2m(struct domain *d, uint16_t *idx,
> + xenmem_access_t hvmmem_default_access)
> +{
> + int rc = -EINVAL;
> + unsigned int i;
> + p2m_access_t a;
> + struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> +
> + if ( hvmmem_default_access > XENMEM_access_default ||
> + !xenmem_access_to_p2m_access(hostp2m, hvmmem_default_access, &a) )
> + return rc;
> +
> + altp2m_list_lock(d);
> +
> + for ( i = 0; i < MAX_ALTP2M; i++ )
> + {
> + if ( d->arch.altp2m_eptp[i] != mfn_x(INVALID_MFN) )
> + continue;
> +
> + rc = p2m_activate_altp2m(d, i, a);
> +
> + if ( !rc )
> + *idx = i;
> +
> + break;
> + }
> +
> + altp2m_list_unlock(d);
> + return rc;
> +}
> +
> +int p2m_destroy_altp2m_by_id(struct domain *d, unsigned int idx)
> +{
> + struct p2m_domain *p2m;
> + int rc = -EBUSY;
> +
> + if ( !idx || idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) )
> + return rc;
> +
> + rc = domain_pause_except_self(d);
> + if ( rc )
> + return rc;
> +
> + rc = -EBUSY;
> + altp2m_list_lock(d);
> +
> + if ( d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] !=
> + mfn_x(INVALID_MFN) )
> + {
> + p2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> +
> + if ( !_atomic_read(p2m->active_vcpus) )
> + {
> + p2m_reset_altp2m(d, idx, ALTP2M_DEACTIVATE);
> + d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] =
> + mfn_x(INVALID_MFN);
> + d->arch.altp2m_visible_eptp[array_index_nospec(idx, MAX_EPTP)] =
> + mfn_x(INVALID_MFN);
> + rc = 0;
> + }
> + }
> +
> + altp2m_list_unlock(d);
> +
> + domain_unpause_except_self(d);
> +
> + return rc;
> +}
> +
> +int p2m_switch_domain_altp2m_by_id(struct domain *d, unsigned int idx)
> +{
> + struct vcpu *v;
> + int rc = -EINVAL;
> +
> + if ( idx >= MAX_ALTP2M )
> + return rc;
> +
> + rc = domain_pause_except_self(d);
> + if ( rc )
> + return rc;
> +
> + rc = -EINVAL;
> + altp2m_list_lock(d);
> +
> + if ( d->arch.altp2m_visible_eptp[idx] != mfn_x(INVALID_MFN) )
> + {
> + for_each_vcpu( d, v )
> + if ( p2m_set_altp2m(v, idx) )
> + altp2m_vcpu_update_p2m(v);
> +
> + rc = 0;
> + }
> +
> + altp2m_list_unlock(d);
> +
> + domain_unpause_except_self(d);
> +
> + return rc;
> +}
> +
> +int p2m_change_altp2m_gfn(struct domain *d, unsigned int idx,
> + gfn_t old_gfn, gfn_t new_gfn)
> +{
> + struct p2m_domain *hp2m, *ap2m;
> + p2m_access_t a;
> + p2m_type_t t;
> + mfn_t mfn;
> + int rc = -EINVAL;
> +
> + if ( idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> + d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] ==
> + mfn_x(INVALID_MFN) )
> + return rc;
> +
> + hp2m = p2m_get_hostp2m(d);
> + ap2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> +
> + p2m_lock(hp2m);
> + p2m_lock(ap2m);
> +
> + if ( gfn_eq(new_gfn, INVALID_GFN) )
> + {
> + mfn = ap2m->get_entry(ap2m, old_gfn, &t, &a, 0, NULL, NULL);
> + rc = mfn_valid(mfn)
> + ? p2m_remove_entry(ap2m, old_gfn, mfn, PAGE_ORDER_4K)
> + : 0;
> + goto out;
> + }
> +
> + rc = altp2m_get_effective_entry(ap2m, old_gfn, &mfn, &t, &a,
> + AP2MGET_prepopulate);
> + if ( rc )
> + goto out;
> +
> + rc = altp2m_get_effective_entry(ap2m, new_gfn, &mfn, &t, &a,
> + AP2MGET_query);
> + if ( rc )
> + goto out;
> +
> + if ( !ap2m->set_entry(ap2m, old_gfn, mfn, PAGE_ORDER_4K, t, a,
> + (current->domain != d)) )
> + {
> + rc = 0;
> +
> + if ( gfn_x(new_gfn) < ap2m->min_remapped_gfn )
> + ap2m->min_remapped_gfn = gfn_x(new_gfn);
> + if ( gfn_x(new_gfn) > ap2m->max_remapped_gfn )
> + ap2m->max_remapped_gfn = gfn_x(new_gfn);
> + }
> +
> + out:
> + p2m_unlock(ap2m);
> + p2m_unlock(hp2m);
> + return rc;
> +}
> +
> +int p2m_altp2m_propagate_change(struct domain *d, gfn_t gfn,
> + mfn_t mfn, unsigned int page_order,
> + p2m_type_t p2mt, p2m_access_t p2ma)
> +{
> + struct p2m_domain *p2m;
> + unsigned int i;
> + unsigned int reset_count = 0;
> + unsigned int last_reset_idx = ~0;
> + int ret = 0;
> +
> + if ( !altp2m_active(d) )
> + return 0;
> +
> + altp2m_list_lock(d);
> +
> + for ( i = 0; i < MAX_ALTP2M; i++ )
> + {
> + p2m_type_t t;
> + p2m_access_t a;
> +
> + if ( d->arch.altp2m_eptp[i] == mfn_x(INVALID_MFN) )
> + continue;
> +
> + p2m = d->arch.altp2m_p2m[i];
> +
> + /* Check for a dropped page that may impact this altp2m */
> + if ( mfn_eq(mfn, INVALID_MFN) &&
> + gfn_x(gfn) + (1UL << page_order) > p2m->min_remapped_gfn &&
> + gfn_x(gfn) <= p2m->max_remapped_gfn )
> + {
> + if ( !reset_count++ )
> + {
> + p2m_reset_altp2m(d, i, ALTP2M_RESET);
> + last_reset_idx = i;
> + }
> + else
> + {
> + /* At least 2 altp2m's impacted, so reset everything */
> + for ( i = 0; i < MAX_ALTP2M; i++ )
> + {
> + if ( i == last_reset_idx ||
> + d->arch.altp2m_eptp[i] == mfn_x(INVALID_MFN) )
> + continue;
> +
> + p2m_reset_altp2m(d, i, ALTP2M_RESET);
> + }
> +
> + ret = 0;
> + break;
> + }
> + }
> + else if ( !mfn_eq(get_gfn_type_access(p2m, gfn_x(gfn), &t, &a, 0,
> + NULL), INVALID_MFN) )
> + {
> + int rc = p2m_set_entry(p2m, gfn, mfn, page_order, p2mt, p2ma);
> +
> + /* Best effort: Don't bail on error. */
> + if ( !ret )
> + ret = rc;
> +
> + p2m_put_gfn(p2m, gfn);
> + }
> + else
> + p2m_put_gfn(p2m, gfn);
> + }
> +
> + altp2m_list_unlock(d);
> +
> + return ret;
> +}
> +
> +/*
> + * Set/clear the #VE suppress bit for a page. Only available on VMX.
> + */
> +int p2m_set_suppress_ve(struct domain *d, gfn_t gfn, bool suppress_ve,
> + unsigned int altp2m_idx)
> +{
> + int rc;
> + struct xen_hvm_altp2m_suppress_ve_multi sve = {
> + altp2m_idx, suppress_ve, 0, 0, gfn_x(gfn), gfn_x(gfn), 0
> + };
> +
> + if ( !(rc = p2m_set_suppress_ve_multi(d, &sve)) )
> + rc = sve.first_error;
> +
> + return rc;
> +}
> +
> +/*
> + * Set/clear the #VE suppress bit for multiple pages. Only available on VMX.
> + */
> +int p2m_set_suppress_ve_multi(struct domain *d,
> + struct xen_hvm_altp2m_suppress_ve_multi *sve)
> +{
> + struct p2m_domain *host_p2m = p2m_get_hostp2m(d);
> + struct p2m_domain *ap2m = NULL;
> + struct p2m_domain *p2m = host_p2m;
> + uint64_t start = sve->first_gfn;
> + int rc = 0;
> +
> + if ( sve->view > 0 )
> + {
> + if ( sve->view >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> + d->arch.altp2m_eptp[array_index_nospec(sve->view, MAX_EPTP)] ==
> + mfn_x(INVALID_MFN) )
> + return -EINVAL;
> +
> + p2m = ap2m = array_access_nospec(d->arch.altp2m_p2m, sve->view);
> + }
> +
> + p2m_lock(host_p2m);
> +
> + if ( ap2m )
> + p2m_lock(ap2m);
> +
> + while ( sve->last_gfn >= start )
> + {
> + p2m_access_t a;
> + p2m_type_t t;
> + mfn_t mfn;
> + int err = 0;
> +
> + if ( (err = altp2m_get_effective_entry(p2m, _gfn(start), &mfn, &t,
> &a,
> + AP2MGET_query)) &&
> + !sve->first_error )
> + {
> + sve->first_error_gfn = start; /* Save the gfn of the first error
> */
> + sve->first_error = err; /* Save the first error code */
> + }
> +
> + if ( !err && (err = p2m->set_entry(p2m, _gfn(start), mfn,
> + PAGE_ORDER_4K, t, a,
> + sve->suppress_ve)) &&
> + !sve->first_error )
> + {
> + sve->first_error_gfn = start; /* Save the gfn of the first error
> */
> + sve->first_error = err; /* Save the first error code */
> + }
> +
> + /* Check for continuation if it's not the last iteration. */
> + if ( sve->last_gfn >= ++start && hypercall_preempt_check() )
> + {
> + rc = -ERESTART;
> + break;
> + }
> + }
> +
> + sve->first_gfn = start;
> +
> + if ( ap2m )
> + p2m_unlock(ap2m);
> +
> + p2m_unlock(host_p2m);
> +
> + return rc;
> +}
> +
> +int p2m_get_suppress_ve(struct domain *d, gfn_t gfn, bool *suppress_ve,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *host_p2m = p2m_get_hostp2m(d);
> + struct p2m_domain *ap2m = NULL;
> + struct p2m_domain *p2m;
> + mfn_t mfn;
> + p2m_access_t a;
> + p2m_type_t t;
> + int rc = 0;
> +
> + if ( altp2m_idx > 0 )
> + {
> + if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> + d->arch.altp2m_eptp[array_index_nospec(altp2m_idx, MAX_EPTP)] ==
> + mfn_x(INVALID_MFN) )
> + return -EINVAL;
> +
> + p2m = ap2m = array_access_nospec(d->arch.altp2m_p2m, altp2m_idx);
> + }
> + else
> + p2m = host_p2m;
> +
> + gfn_lock(host_p2m, gfn, 0);
> +
> + if ( ap2m )
> + p2m_lock(ap2m);
> +
> + mfn = p2m->get_entry(p2m, gfn, &t, &a, 0, NULL, suppress_ve);
> + if ( !mfn_valid(mfn) )
> + rc = -ESRCH;
> +
> + if ( ap2m )
> + p2m_unlock(ap2m);
> +
> + gfn_unlock(host_p2m, gfn, 0);
> +
> + return rc;
> +}
> +
> +int p2m_set_altp2m_view_visibility(struct domain *d, unsigned int altp2m_idx,
> + uint8_t visible)
> +{
> + int rc = 0;
> +
> + altp2m_list_lock(d);
> +
> + /*
> + * Eptp index is correlated with altp2m index and should not exceed
> + * min(MAX_ALTP2M, MAX_EPTP).
> + */
> + if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> + d->arch.altp2m_eptp[array_index_nospec(altp2m_idx, MAX_EPTP)] ==
> + mfn_x(INVALID_MFN) )
> + rc = -EINVAL;
> + else if ( visible )
> + d->arch.altp2m_visible_eptp[array_index_nospec(altp2m_idx,
> MAX_EPTP)] =
> + d->arch.altp2m_eptp[array_index_nospec(altp2m_idx, MAX_EPTP)];
> + else
> + d->arch.altp2m_visible_eptp[array_index_nospec(altp2m_idx,
> MAX_EPTP)] =
> + mfn_x(INVALID_MFN);
> +
> + altp2m_list_unlock(d);
> +
> + return rc;
> +}
> +
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
> index ce742c12e0..7c422a2d7e 100644
> --- a/xen/arch/x86/mm/p2m.c
> +++ b/xen/arch/x86/mm/p2m.c
> @@ -500,7 +500,7 @@ int p2m_alloc_table(struct p2m_domain *p2m)
> return 0;
> }
>
> -static int __must_check
> +int __must_check
> p2m_remove_entry(struct p2m_domain *p2m, gfn_t gfn, mfn_t mfn,
> unsigned int page_order)
> {
> @@ -1329,7 +1329,7 @@ p2m_getlru_nestedp2m(struct domain *d, struct
> p2m_domain *p2m)
> return p2m;
> }
>
> -static void
> +void
> p2m_flush_table_locked(struct p2m_domain *p2m)
> {
> struct page_info *top, *pg;
> @@ -1729,481 +1729,6 @@ int unmap_mmio_regions(struct domain *d,
> return i == nr ? 0 : i ?: ret;
> }
>
> -int altp2m_get_effective_entry(struct p2m_domain *ap2m, gfn_t gfn, mfn_t
> *mfn,
> - p2m_type_t *t, p2m_access_t *a,
> - bool prepopulate)
> -{
> - *mfn = ap2m->get_entry(ap2m, gfn, t, a, 0, NULL, NULL);
> -
> - /* Check host p2m if no valid entry in alternate */
> - if ( !mfn_valid(*mfn) && !p2m_is_hostp2m(ap2m) )
> - {
> - struct p2m_domain *hp2m = p2m_get_hostp2m(ap2m->domain);
> - unsigned int page_order;
> - int rc;
> -
> - *mfn = p2m_get_gfn_type_access(hp2m, gfn, t, a, P2M_ALLOC |
> P2M_UNSHARE,
> - &page_order, 0);
> -
> - rc = -ESRCH;
> - if ( !mfn_valid(*mfn) || *t != p2m_ram_rw )
> - return rc;
> -
> - /* If this is a superpage, copy that first */
> - if ( prepopulate && page_order != PAGE_ORDER_4K )
> - {
> - unsigned long mask = ~((1UL << page_order) - 1);
> - gfn_t gfn_aligned = _gfn(gfn_x(gfn) & mask);
> - mfn_t mfn_aligned = _mfn(mfn_x(*mfn) & mask);
> -
> - rc = ap2m->set_entry(ap2m, gfn_aligned, mfn_aligned, page_order,
> *t, *a, 1);
> - if ( rc )
> - return rc;
> - }
> - }
> -
> - return 0;
> -}
> -
> -void p2m_altp2m_check(struct vcpu *v, uint16_t idx)
> -{
> - if ( altp2m_active(v->domain) )
> - p2m_switch_vcpu_altp2m_by_id(v, idx);
> -}
> -
> -bool p2m_switch_vcpu_altp2m_by_id(struct vcpu *v, unsigned int idx)
> -{
> - struct domain *d = v->domain;
> - bool rc = false;
> -
> - if ( idx >= MAX_ALTP2M )
> - return rc;
> -
> - altp2m_list_lock(d);
> -
> - if ( d->arch.altp2m_eptp[idx] != mfn_x(INVALID_MFN) )
> - {
> - if ( p2m_set_altp2m(v, idx) )
> - altp2m_vcpu_update_p2m(v);
> - rc = 1;
> - }
> -
> - altp2m_list_unlock(d);
> - return rc;
> -}
> -
> -/*
> - * Read info about the gfn in an altp2m, locking the gfn.
> - *
> - * If the entry is valid, pass the results back to the caller.
> - *
> - * If the entry was invalid, and the host's entry is also invalid,
> - * return to the caller without any changes.
> - *
> - * If the entry is invalid, and the host entry was valid, propagate
> - * the host's entry to the altp2m (retaining page order), and indicate
> - * that the caller should re-try the faulting instruction.
> - */
> -bool p2m_altp2m_get_or_propagate(struct p2m_domain *ap2m, unsigned long
> gfn_l,
> - mfn_t *mfn, p2m_type_t *p2mt,
> - p2m_access_t *p2ma, unsigned int
> *page_order)
> -{
> - p2m_type_t ap2mt;
> - p2m_access_t ap2ma;
> - unsigned int cur_order;
> - unsigned long mask;
> - gfn_t gfn;
> - mfn_t amfn;
> - int rc;
> -
> - /*
> - * NB we must get the full lock on the altp2m here, in addition to
> - * the lock on the individual gfn, since we may change a range of
> - * gfns below.
> - */
> - p2m_lock(ap2m);
> -
> - amfn = get_gfn_type_access(ap2m, gfn_l, &ap2mt, &ap2ma, 0, &cur_order);
> -
> - if ( cur_order > *page_order )
> - cur_order = *page_order;
> -
> - if ( !mfn_eq(amfn, INVALID_MFN) )
> - {
> - p2m_unlock(ap2m);
> - *mfn = amfn;
> - *p2mt = ap2mt;
> - *p2ma = ap2ma;
> - *page_order = cur_order;
> - return false;
> - }
> -
> - /* Host entry is also invalid; don't bother setting the altp2m entry. */
> - if ( mfn_eq(*mfn, INVALID_MFN) )
> - {
> - p2m_unlock(ap2m);
> - *page_order = cur_order;
> - return false;
> - }
> -
> - /*
> - * If this is a superpage mapping, round down both frame numbers
> - * to the start of the superpage. NB that we repupose `amfn`
> - * here.
> - */
> - mask = ~((1UL << cur_order) - 1);
> - amfn = _mfn(mfn_x(*mfn) & mask);
> - gfn = _gfn(gfn_l & mask);
> -
> - /* Override the altp2m entry with its default access. */
> - *p2ma = ap2m->default_access;
> -
> - rc = p2m_set_entry(ap2m, gfn, amfn, cur_order, *p2mt, *p2ma);
> - p2m_unlock(ap2m);
> -
> - if ( rc )
> - {
> - gprintk(XENLOG_ERR,
> - "failed to set entry for %"PRI_gfn" -> %"PRI_mfn" altp2m %u,
> rc %d\n",
> - gfn_l, mfn_x(amfn), vcpu_altp2m(current).p2midx, rc);
> - domain_crash(ap2m->domain);
> - }
> -
> - return true;
> -}
> -
> -enum altp2m_reset_type {
> - ALTP2M_RESET,
> - ALTP2M_DEACTIVATE
> -};
> -
> -static void p2m_reset_altp2m(struct domain *d, unsigned int idx,
> - enum altp2m_reset_type reset_type)
> -{
> - struct p2m_domain *p2m;
> -
> - ASSERT(idx < MAX_ALTP2M);
> - p2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> -
> - p2m_lock(p2m);
> -
> - p2m_flush_table_locked(p2m);
> -
> - if ( reset_type == ALTP2M_DEACTIVATE )
> - p2m_free_logdirty(p2m);
> -
> - /* Uninit and reinit ept to force TLB shootdown */
> - ept_p2m_uninit(p2m);
> - ept_p2m_init(p2m);
> -
> - p2m->min_remapped_gfn = gfn_x(INVALID_GFN);
> - p2m->max_remapped_gfn = 0;
> -
> - p2m_unlock(p2m);
> -}
> -
> -void p2m_flush_altp2m(struct domain *d)
> -{
> - unsigned int i;
> -
> - altp2m_list_lock(d);
> -
> - for ( i = 0; i < MAX_ALTP2M; i++ )
> - {
> - p2m_reset_altp2m(d, i, ALTP2M_DEACTIVATE);
> - d->arch.altp2m_eptp[i] = mfn_x(INVALID_MFN);
> - d->arch.altp2m_visible_eptp[i] = mfn_x(INVALID_MFN);
> - }
> -
> - altp2m_list_unlock(d);
> -}
> -
> -static int p2m_activate_altp2m(struct domain *d, unsigned int idx,
> - p2m_access_t hvmmem_default_access)
> -{
> - struct p2m_domain *hostp2m, *p2m;
> - int rc;
> -
> - ASSERT(idx < MAX_ALTP2M);
> -
> - p2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> - hostp2m = p2m_get_hostp2m(d);
> -
> - p2m_lock(p2m);
> -
> - rc = p2m_init_logdirty(p2m);
> -
> - if ( rc )
> - goto out;
> -
> - /* The following is really just a rangeset copy. */
> - rc = rangeset_merge(p2m->logdirty_ranges, hostp2m->logdirty_ranges);
> -
> - if ( rc )
> - {
> - p2m_free_logdirty(p2m);
> - goto out;
> - }
> -
> - p2m->default_access = hvmmem_default_access;
> - p2m->domain = hostp2m->domain;
> - p2m->global_logdirty = hostp2m->global_logdirty;
> - p2m->min_remapped_gfn = gfn_x(INVALID_GFN);
> - p2m->max_mapped_pfn = p2m->max_remapped_gfn = 0;
> -
> - p2m_init_altp2m_ept(d, idx);
> -
> - out:
> - p2m_unlock(p2m);
> -
> - return rc;
> -}
> -
> -int p2m_init_altp2m_by_id(struct domain *d, unsigned int idx)
> -{
> - int rc = -EINVAL;
> - struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> -
> - if ( idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) )
> - return rc;
> -
> - altp2m_list_lock(d);
> -
> - if ( d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] ==
> - mfn_x(INVALID_MFN) )
> - rc = p2m_activate_altp2m(d, idx, hostp2m->default_access);
> -
> - altp2m_list_unlock(d);
> - return rc;
> -}
> -
> -int p2m_init_next_altp2m(struct domain *d, uint16_t *idx,
> - xenmem_access_t hvmmem_default_access)
> -{
> - int rc = -EINVAL;
> - unsigned int i;
> - p2m_access_t a;
> - struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> -
> - if ( hvmmem_default_access > XENMEM_access_default ||
> - !xenmem_access_to_p2m_access(hostp2m, hvmmem_default_access, &a) )
> - return rc;
> -
> - altp2m_list_lock(d);
> -
> - for ( i = 0; i < MAX_ALTP2M; i++ )
> - {
> - if ( d->arch.altp2m_eptp[i] != mfn_x(INVALID_MFN) )
> - continue;
> -
> - rc = p2m_activate_altp2m(d, i, a);
> -
> - if ( !rc )
> - *idx = i;
> -
> - break;
> - }
> -
> - altp2m_list_unlock(d);
> - return rc;
> -}
> -
> -int p2m_destroy_altp2m_by_id(struct domain *d, unsigned int idx)
> -{
> - struct p2m_domain *p2m;
> - int rc = -EBUSY;
> -
> - if ( !idx || idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) )
> - return rc;
> -
> - rc = domain_pause_except_self(d);
> - if ( rc )
> - return rc;
> -
> - rc = -EBUSY;
> - altp2m_list_lock(d);
> -
> - if ( d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] !=
> - mfn_x(INVALID_MFN) )
> - {
> - p2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> -
> - if ( !_atomic_read(p2m->active_vcpus) )
> - {
> - p2m_reset_altp2m(d, idx, ALTP2M_DEACTIVATE);
> - d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] =
> - mfn_x(INVALID_MFN);
> - d->arch.altp2m_visible_eptp[array_index_nospec(idx, MAX_EPTP)] =
> - mfn_x(INVALID_MFN);
> - rc = 0;
> - }
> - }
> -
> - altp2m_list_unlock(d);
> -
> - domain_unpause_except_self(d);
> -
> - return rc;
> -}
> -
> -int p2m_switch_domain_altp2m_by_id(struct domain *d, unsigned int idx)
> -{
> - struct vcpu *v;
> - int rc = -EINVAL;
> -
> - if ( idx >= MAX_ALTP2M )
> - return rc;
> -
> - rc = domain_pause_except_self(d);
> - if ( rc )
> - return rc;
> -
> - rc = -EINVAL;
> - altp2m_list_lock(d);
> -
> - if ( d->arch.altp2m_visible_eptp[idx] != mfn_x(INVALID_MFN) )
> - {
> - for_each_vcpu( d, v )
> - if ( p2m_set_altp2m(v, idx) )
> - altp2m_vcpu_update_p2m(v);
> -
> - rc = 0;
> - }
> -
> - altp2m_list_unlock(d);
> -
> - domain_unpause_except_self(d);
> -
> - return rc;
> -}
> -
> -int p2m_change_altp2m_gfn(struct domain *d, unsigned int idx,
> - gfn_t old_gfn, gfn_t new_gfn)
> -{
> - struct p2m_domain *hp2m, *ap2m;
> - p2m_access_t a;
> - p2m_type_t t;
> - mfn_t mfn;
> - int rc = -EINVAL;
> -
> - if ( idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> - d->arch.altp2m_eptp[array_index_nospec(idx, MAX_EPTP)] ==
> - mfn_x(INVALID_MFN) )
> - return rc;
> -
> - hp2m = p2m_get_hostp2m(d);
> - ap2m = array_access_nospec(d->arch.altp2m_p2m, idx);
> -
> - p2m_lock(hp2m);
> - p2m_lock(ap2m);
> -
> - if ( gfn_eq(new_gfn, INVALID_GFN) )
> - {
> - mfn = ap2m->get_entry(ap2m, old_gfn, &t, &a, 0, NULL, NULL);
> - rc = mfn_valid(mfn)
> - ? p2m_remove_entry(ap2m, old_gfn, mfn, PAGE_ORDER_4K)
> - : 0;
> - goto out;
> - }
> -
> - rc = altp2m_get_effective_entry(ap2m, old_gfn, &mfn, &t, &a,
> - AP2MGET_prepopulate);
> - if ( rc )
> - goto out;
> -
> - rc = altp2m_get_effective_entry(ap2m, new_gfn, &mfn, &t, &a,
> - AP2MGET_query);
> - if ( rc )
> - goto out;
> -
> - if ( !ap2m->set_entry(ap2m, old_gfn, mfn, PAGE_ORDER_4K, t, a,
> - (current->domain != d)) )
> - {
> - rc = 0;
> -
> - if ( gfn_x(new_gfn) < ap2m->min_remapped_gfn )
> - ap2m->min_remapped_gfn = gfn_x(new_gfn);
> - if ( gfn_x(new_gfn) > ap2m->max_remapped_gfn )
> - ap2m->max_remapped_gfn = gfn_x(new_gfn);
> - }
> -
> - out:
> - p2m_unlock(ap2m);
> - p2m_unlock(hp2m);
> - return rc;
> -}
> -
> -int p2m_altp2m_propagate_change(struct domain *d, gfn_t gfn,
> - mfn_t mfn, unsigned int page_order,
> - p2m_type_t p2mt, p2m_access_t p2ma)
> -{
> - struct p2m_domain *p2m;
> - unsigned int i;
> - unsigned int reset_count = 0;
> - unsigned int last_reset_idx = ~0;
> - int ret = 0;
> -
> - if ( !altp2m_active(d) )
> - return 0;
> -
> - altp2m_list_lock(d);
> -
> - for ( i = 0; i < MAX_ALTP2M; i++ )
> - {
> - p2m_type_t t;
> - p2m_access_t a;
> -
> - if ( d->arch.altp2m_eptp[i] == mfn_x(INVALID_MFN) )
> - continue;
> -
> - p2m = d->arch.altp2m_p2m[i];
> -
> - /* Check for a dropped page that may impact this altp2m */
> - if ( mfn_eq(mfn, INVALID_MFN) &&
> - gfn_x(gfn) + (1UL << page_order) > p2m->min_remapped_gfn &&
> - gfn_x(gfn) <= p2m->max_remapped_gfn )
> - {
> - if ( !reset_count++ )
> - {
> - p2m_reset_altp2m(d, i, ALTP2M_RESET);
> - last_reset_idx = i;
> - }
> - else
> - {
> - /* At least 2 altp2m's impacted, so reset everything */
> - for ( i = 0; i < MAX_ALTP2M; i++ )
> - {
> - if ( i == last_reset_idx ||
> - d->arch.altp2m_eptp[i] == mfn_x(INVALID_MFN) )
> - continue;
> -
> - p2m_reset_altp2m(d, i, ALTP2M_RESET);
> - }
> -
> - ret = 0;
> - break;
> - }
> - }
> - else if ( !mfn_eq(get_gfn_type_access(p2m, gfn_x(gfn), &t, &a, 0,
> - NULL), INVALID_MFN) )
> - {
> - int rc = p2m_set_entry(p2m, gfn, mfn, page_order, p2mt, p2ma);
> -
> - /* Best effort: Don't bail on error. */
> - if ( !ret )
> - ret = rc;
> -
> - p2m_put_gfn(p2m, gfn);
> - }
> - else
> - p2m_put_gfn(p2m, gfn);
> - }
> -
> - altp2m_list_unlock(d);
> -
> - return ret;
> -}
> -
> /*** Audit ***/
>
> #if P2M_AUDIT
> @@ -2540,159 +2065,6 @@ int xenmem_add_to_physmap_one(
> return rc;
> }
>
> -/*
> - * Set/clear the #VE suppress bit for a page. Only available on VMX.
> - */
> -int p2m_set_suppress_ve(struct domain *d, gfn_t gfn, bool suppress_ve,
> - unsigned int altp2m_idx)
> -{
> - int rc;
> - struct xen_hvm_altp2m_suppress_ve_multi sve = {
> - altp2m_idx, suppress_ve, 0, 0, gfn_x(gfn), gfn_x(gfn), 0
> - };
> -
> - if ( !(rc = p2m_set_suppress_ve_multi(d, &sve)) )
> - rc = sve.first_error;
> -
> - return rc;
> -}
> -
> -/*
> - * Set/clear the #VE suppress bit for multiple pages. Only available on VMX.
> - */
> -int p2m_set_suppress_ve_multi(struct domain *d,
> - struct xen_hvm_altp2m_suppress_ve_multi *sve)
> -{
> - struct p2m_domain *host_p2m = p2m_get_hostp2m(d);
> - struct p2m_domain *ap2m = NULL;
> - struct p2m_domain *p2m = host_p2m;
> - uint64_t start = sve->first_gfn;
> - int rc = 0;
> -
> - if ( sve->view > 0 )
> - {
> - if ( sve->view >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> - d->arch.altp2m_eptp[array_index_nospec(sve->view, MAX_EPTP)] ==
> - mfn_x(INVALID_MFN) )
> - return -EINVAL;
> -
> - p2m = ap2m = array_access_nospec(d->arch.altp2m_p2m, sve->view);
> - }
> -
> - p2m_lock(host_p2m);
> -
> - if ( ap2m )
> - p2m_lock(ap2m);
> -
> - while ( sve->last_gfn >= start )
> - {
> - p2m_access_t a;
> - p2m_type_t t;
> - mfn_t mfn;
> - int err = 0;
> -
> - if ( (err = altp2m_get_effective_entry(p2m, _gfn(start), &mfn, &t,
> &a,
> - AP2MGET_query)) &&
> - !sve->first_error )
> - {
> - sve->first_error_gfn = start; /* Save the gfn of the first error
> */
> - sve->first_error = err; /* Save the first error code */
> - }
> -
> - if ( !err && (err = p2m->set_entry(p2m, _gfn(start), mfn,
> - PAGE_ORDER_4K, t, a,
> - sve->suppress_ve)) &&
> - !sve->first_error )
> - {
> - sve->first_error_gfn = start; /* Save the gfn of the first error
> */
> - sve->first_error = err; /* Save the first error code */
> - }
> -
> - /* Check for continuation if it's not the last iteration. */
> - if ( sve->last_gfn >= ++start && hypercall_preempt_check() )
> - {
> - rc = -ERESTART;
> - break;
> - }
> - }
> -
> - sve->first_gfn = start;
> -
> - if ( ap2m )
> - p2m_unlock(ap2m);
> -
> - p2m_unlock(host_p2m);
> -
> - return rc;
> -}
> -
> -int p2m_get_suppress_ve(struct domain *d, gfn_t gfn, bool *suppress_ve,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *host_p2m = p2m_get_hostp2m(d);
> - struct p2m_domain *ap2m = NULL;
> - struct p2m_domain *p2m;
> - mfn_t mfn;
> - p2m_access_t a;
> - p2m_type_t t;
> - int rc = 0;
> -
> - if ( altp2m_idx > 0 )
> - {
> - if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> - d->arch.altp2m_eptp[array_index_nospec(altp2m_idx, MAX_EPTP)] ==
> - mfn_x(INVALID_MFN) )
> - return -EINVAL;
> -
> - p2m = ap2m = array_access_nospec(d->arch.altp2m_p2m, altp2m_idx);
> - }
> - else
> - p2m = host_p2m;
> -
> - gfn_lock(host_p2m, gfn, 0);
> -
> - if ( ap2m )
> - p2m_lock(ap2m);
> -
> - mfn = p2m->get_entry(p2m, gfn, &t, &a, 0, NULL, suppress_ve);
> - if ( !mfn_valid(mfn) )
> - rc = -ESRCH;
> -
> - if ( ap2m )
> - p2m_unlock(ap2m);
> -
> - gfn_unlock(host_p2m, gfn, 0);
> -
> - return rc;
> -}
> -
> -int p2m_set_altp2m_view_visibility(struct domain *d, unsigned int altp2m_idx,
> - uint8_t visible)
> -{
> - int rc = 0;
> -
> - altp2m_list_lock(d);
> -
> - /*
> - * Eptp index is correlated with altp2m index and should not exceed
> - * min(MAX_ALTP2M, MAX_EPTP).
> - */
> - if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
> - d->arch.altp2m_eptp[array_index_nospec(altp2m_idx, MAX_EPTP)] ==
> - mfn_x(INVALID_MFN) )
> - rc = -EINVAL;
> - else if ( visible )
> - d->arch.altp2m_visible_eptp[array_index_nospec(altp2m_idx,
> MAX_EPTP)] =
> - d->arch.altp2m_eptp[array_index_nospec(altp2m_idx, MAX_EPTP)];
> - else
> - d->arch.altp2m_visible_eptp[array_index_nospec(altp2m_idx,
> MAX_EPTP)] =
> - mfn_x(INVALID_MFN);
> -
> - altp2m_list_unlock(d);
> -
> - return rc;
> -}
> -
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/arch/x86/mm/p2m.h b/xen/arch/x86/mm/p2m.h
> index 04308cfb6d..635f5a7f45 100644
> --- a/xen/arch/x86/mm/p2m.h
> +++ b/xen/arch/x86/mm/p2m.h
> @@ -22,6 +22,9 @@ static inline void p2m_free_logdirty(struct p2m_domain
> *p2m) {}
> int p2m_init_altp2m(struct domain *d);
> void p2m_teardown_altp2m(struct domain *d);
>
> +void p2m_flush_table_locked(struct p2m_domain *p2m);
> +int __must_check p2m_remove_entry(struct p2m_domain *p2m, gfn_t gfn, mfn_t
> mfn,
> + unsigned int page_order);
> void p2m_nestedp2m_init(struct p2m_domain *p2m);
> int p2m_init_nestedp2m(struct domain *d);
> void p2m_teardown_nestedp2m(struct domain *d);
> --
> 2.25.1
>
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |