* Re: [PATCH v2 2/2] p2m: split mem_access into separate files
2016-12-09 19:59 ` [PATCH v2 2/2] p2m: split mem_access into separate files Tamas K Lengyel
@ 2017-01-03 15:31 ` Tamas K Lengyel
2017-01-11 16:33 ` Konrad Rzeszutek Wilk
2017-01-30 21:11 ` Stefano Stabellini
1 sibling, 1 reply; 24+ messages in thread
From: Tamas K Lengyel @ 2017-01-03 15:31 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org, Stefano Stabellini
Cc: George Dunlap, Andrew Cooper, Julien Grall, Jan Beulich
On Fri, Dec 9, 2016 at 12:59 PM, Tamas K Lengyel
<tamas.lengyel@zentific.com> wrote:
> This patch relocates mem_access components that are currently mixed with p2m
> code into separate files. This better aligns the code with similar subsystems,
> such as mem_sharing and mem_paging, which are already in separate files. There
> are no code-changes introduced, the patch is mechanical code movement.
>
> On ARM we also relocate the static inline gfn_next_boundary function to p2m.h
> as it is a function the mem_access code needs access to.
>
> Signed-off-by: Tamas K Lengyel <tamas.lengyel@zentific.com>
> Acked-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
Acked-by: George Dunlap <george.dunlap@citrix.com>
> ---
> Cc: Stefano Stabellini <sstabellini@kernel.org>
> Cc: Julien Grall <julien.grall@arm.com>
> Cc: Jan Beulich <jbeulich@suse.com>
> Cc: Andrew Cooper <andrew.cooper3@citrix.com>
> Cc: George Dunlap <george.dunlap@eu.citrix.com>
>
> v2: Don't move ARM radix tree functions
> Include asm/mem_accesss.h in xen/mem_access.h
Patch ping. I think this only needs an ARM-side ack.
> ---
> MAINTAINERS | 2 +
> xen/arch/arm/Makefile | 1 +
> xen/arch/arm/mem_access.c | 431 ++++++++++++++++++++++++++++++++++++
> xen/arch/arm/p2m.c | 414 +----------------------------------
> xen/arch/arm/traps.c | 1 +
> xen/arch/x86/mm/Makefile | 1 +
> xen/arch/x86/mm/mem_access.c | 462 +++++++++++++++++++++++++++++++++++++++
> xen/arch/x86/mm/p2m.c | 421 -----------------------------------
> xen/arch/x86/vm_event.c | 3 +-
> xen/common/mem_access.c | 2 +-
> xen/include/asm-arm/mem_access.h | 53 +++++
> xen/include/asm-arm/p2m.h | 31 ++-
> xen/include/asm-x86/mem_access.h | 61 ++++++
> xen/include/asm-x86/p2m.h | 24 +-
> xen/include/xen/mem_access.h | 67 +++++-
> xen/include/xen/p2m-common.h | 52 -----
> 16 files changed, 1089 insertions(+), 937 deletions(-)
> create mode 100644 xen/arch/arm/mem_access.c
> create mode 100644 xen/arch/x86/mm/mem_access.c
> create mode 100644 xen/include/asm-arm/mem_access.h
> create mode 100644 xen/include/asm-x86/mem_access.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f0d0202..fb26be3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -402,6 +402,8 @@ S: Supported
> F: tools/tests/xen-access
> F: xen/arch/*/monitor.c
> F: xen/arch/*/vm_event.c
> +F: xen/arch/arm/mem_access.c
> +F: xen/arch/x86/mm/mem_access.c
> F: xen/arch/x86/hvm/monitor.c
> F: xen/common/mem_access.c
> F: xen/common/monitor.c
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index da39d39..b095e8a 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -24,6 +24,7 @@ obj-y += io.o
> obj-y += irq.o
> obj-y += kernel.o
> obj-$(CONFIG_LIVEPATCH) += livepatch.o
> +obj-y += mem_access.o
> obj-y += mm.o
> obj-y += monitor.o
> obj-y += p2m.o
> diff --git a/xen/arch/arm/mem_access.c b/xen/arch/arm/mem_access.c
> new file mode 100644
> index 0000000..a6e5bcd
> --- /dev/null
> +++ b/xen/arch/arm/mem_access.c
> @@ -0,0 +1,431 @@
> +/*
> + * arch/arm/mem_access.c
> + *
> + * Architecture-specific mem_access handling routines
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/config.h>
> +#include <xen/mem_access.h>
> +#include <xen/monitor.h>
> +#include <xen/sched.h>
> +#include <xen/vm_event.h>
> +#include <public/vm_event.h>
> +#include <asm/event.h>
> +
> +static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
> + xenmem_access_t *access)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + void *i;
> + unsigned int index;
> +
> + static const xenmem_access_t memaccess[] = {
> +#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + ASSERT(p2m_is_locked(p2m));
> +
> + /* If no setting was ever set, just return rwx. */
> + if ( !p2m->mem_access_enabled )
> + {
> + *access = XENMEM_access_rwx;
> + return 0;
> + }
> +
> + /* If request to get default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + *access = memaccess[p2m->default_access];
> + return 0;
> + }
> +
> + i = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> +
> + if ( !i )
> + {
> + /*
> + * No setting was found in the Radix tree. Check if the
> + * entry exists in the page-tables.
> + */
> + mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
> +
> + if ( mfn_eq(mfn, INVALID_MFN) )
> + return -ESRCH;
> +
> + /* If entry exists then its rwx. */
> + *access = XENMEM_access_rwx;
> + }
> + else
> + {
> + /* Setting was found in the Radix tree. */
> + index = radix_tree_ptr_to_int(i);
> + if ( index >= ARRAY_SIZE(memaccess) )
> + return -ERANGE;
> +
> + *access = memaccess[index];
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * If mem_access is in use it might have been the reason why get_page_from_gva
> + * failed to fetch the page, as it uses the MMU for the permission checking.
> + * Only in these cases we do a software-based type check and fetch the page if
> + * we indeed found a conflicting mem_access setting.
> + */
> +struct page_info*
> +p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag,
> + const struct vcpu *v)
> +{
> + long rc;
> + paddr_t ipa;
> + gfn_t gfn;
> + mfn_t mfn;
> + xenmem_access_t xma;
> + p2m_type_t t;
> + struct page_info *page = NULL;
> + struct p2m_domain *p2m = &v->domain->arch.p2m;
> +
> + rc = gva_to_ipa(gva, &ipa, flag);
> + if ( rc < 0 )
> + goto err;
> +
> + gfn = _gfn(paddr_to_pfn(ipa));
> +
> + /*
> + * We do this first as this is faster in the default case when no
> + * permission is set on the page.
> + */
> + rc = __p2m_get_mem_access(v->domain, gfn, &xma);
> + if ( rc < 0 )
> + goto err;
> +
> + /* Let's check if mem_access limited the access. */
> + switch ( xma )
> + {
> + default:
> + case XENMEM_access_rwx:
> + case XENMEM_access_rw:
> + /*
> + * If mem_access contains no rw perm restrictions at all then the original
> + * fault was correct.
> + */
> + goto err;
> + case XENMEM_access_n2rwx:
> + case XENMEM_access_n:
> + case XENMEM_access_x:
> + /*
> + * If no r/w is permitted by mem_access, this was a fault caused by mem_access.
> + */
> + break;
> + case XENMEM_access_wx:
> + case XENMEM_access_w:
> + /*
> + * If this was a read then it was because of mem_access, but if it was
> + * a write then the original get_page_from_gva fault was correct.
> + */
> + if ( flag == GV2M_READ )
> + break;
> + else
> + goto err;
> + case XENMEM_access_rx2rw:
> + case XENMEM_access_rx:
> + case XENMEM_access_r:
> + /*
> + * If this was a write then it was because of mem_access, but if it was
> + * a read then the original get_page_from_gva fault was correct.
> + */
> + if ( flag == GV2M_WRITE )
> + break;
> + else
> + goto err;
> + }
> +
> + /*
> + * We had a mem_access permission limiting the access, but the page type
> + * could also be limiting, so we need to check that as well.
> + */
> + mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL);
> + if ( mfn_eq(mfn, INVALID_MFN) )
> + goto err;
> +
> + if ( !mfn_valid(mfn_x(mfn)) )
> + goto err;
> +
> + /*
> + * Base type doesn't allow r/w
> + */
> + if ( t != p2m_ram_rw )
> + goto err;
> +
> + page = mfn_to_page(mfn_x(mfn));
> +
> + if ( unlikely(!get_page(page, v->domain)) )
> + page = NULL;
> +
> +err:
> + return page;
> +}
> +
> +bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec)
> +{
> + int rc;
> + bool_t violation;
> + xenmem_access_t xma;
> + vm_event_request_t *req;
> + struct vcpu *v = current;
> + struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
> +
> + /* Mem_access is not in use. */
> + if ( !p2m->mem_access_enabled )
> + return true;
> +
> + rc = p2m_get_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), &xma);
> + if ( rc )
> + return true;
> +
> + /* Now check for mem_access violation. */
> + switch ( xma )
> + {
> + case XENMEM_access_rwx:
> + violation = false;
> + break;
> + case XENMEM_access_rw:
> + violation = npfec.insn_fetch;
> + break;
> + case XENMEM_access_wx:
> + violation = npfec.read_access;
> + break;
> + case XENMEM_access_rx:
> + case XENMEM_access_rx2rw:
> + violation = npfec.write_access;
> + break;
> + case XENMEM_access_x:
> + violation = npfec.read_access || npfec.write_access;
> + break;
> + case XENMEM_access_w:
> + violation = npfec.read_access || npfec.insn_fetch;
> + break;
> + case XENMEM_access_r:
> + violation = npfec.write_access || npfec.insn_fetch;
> + break;
> + default:
> + case XENMEM_access_n:
> + case XENMEM_access_n2rwx:
> + violation = true;
> + break;
> + }
> +
> + if ( !violation )
> + return true;
> +
> + /* First, handle rx2rw and n2rwx conversion automatically. */
> + if ( npfec.write_access && xma == XENMEM_access_rx2rw )
> + {
> + rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> + 0, ~0, XENMEM_access_rw, 0);
> + return false;
> + }
> + else if ( xma == XENMEM_access_n2rwx )
> + {
> + rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> + 0, ~0, XENMEM_access_rwx, 0);
> + }
> +
> + /* Otherwise, check if there is a vm_event monitor subscriber */
> + if ( !vm_event_check_ring(&v->domain->vm_event->monitor) )
> + {
> + /* No listener */
> + if ( p2m->access_required )
> + {
> + gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> + "no vm_event listener VCPU %d, dom %d\n",
> + v->vcpu_id, v->domain->domain_id);
> + domain_crash(v->domain);
> + }
> + else
> + {
> + /* n2rwx was already handled */
> + if ( xma != XENMEM_access_n2rwx )
> + {
> + /* A listener is not required, so clear the access
> + * restrictions. */
> + rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> + 0, ~0, XENMEM_access_rwx, 0);
> + }
> + }
> +
> + /* No need to reinject */
> + return false;
> + }
> +
> + req = xzalloc(vm_event_request_t);
> + if ( req )
> + {
> + req->reason = VM_EVENT_REASON_MEM_ACCESS;
> +
> + /* Send request to mem access subscriber */
> + req->u.mem_access.gfn = gpa >> PAGE_SHIFT;
> + req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> + if ( npfec.gla_valid )
> + {
> + req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> + req->u.mem_access.gla = gla;
> +
> + if ( npfec.kind == npfec_kind_with_gla )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> + else if ( npfec.kind == npfec_kind_in_gpt )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> + }
> + req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> + req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> + req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> +
> + if ( monitor_traps(v, (xma != XENMEM_access_n2rwx), req) < 0 )
> + domain_crash(v->domain);
> +
> + xfree(req);
> + }
> +
> + return false;
> +}
> +
> +/*
> + * Set access type for a region of pfns.
> + * If gfn == INVALID_GFN, sets the default access type.
> + */
> +long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + p2m_access_t a;
> + unsigned int order;
> + long rc = 0;
> +
> + static const p2m_access_t memaccess[] = {
> +#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + switch ( access )
> + {
> + case 0 ... ARRAY_SIZE(memaccess) - 1:
> + a = memaccess[access];
> + break;
> + case XENMEM_access_default:
> + a = p2m->default_access;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /*
> + * Flip mem_access_enabled to true when a permission is set, as to prevent
> + * allocating or inserting super-pages.
> + */
> + p2m->mem_access_enabled = true;
> +
> + /* If request to set default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + p2m->default_access = a;
> + return 0;
> + }
> +
> + p2m_write_lock(p2m);
> +
> + for ( gfn = gfn_add(gfn, start); nr > start;
> + gfn = gfn_next_boundary(gfn, order) )
> + {
> + p2m_type_t t;
> + mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order);
> +
> +
> + if ( !mfn_eq(mfn, INVALID_MFN) )
> + {
> + order = 0;
> + rc = p2m_set_entry(p2m, gfn, 1, mfn, t, a);
> + if ( rc )
> + break;
> + }
> +
> + start += gfn_x(gfn_next_boundary(gfn, order)) - gfn_x(gfn);
> + /* Check for continuation if it is not the last iteration */
> + if ( nr > start && !(start & mask) && hypercall_preempt_check() )
> + {
> + rc = start;
> + break;
> + }
> + }
> +
> + p2m_write_unlock(p2m);
> +
> + return rc;
> +}
> +
> +long p2m_set_mem_access_multi(struct domain *d,
> + const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> + const XEN_GUEST_HANDLE(const_uint8) access_list,
> + uint32_t nr, uint32_t start, uint32_t mask,
> + unsigned int altp2m_idx)
> +{
> + /* Not yet implemented on ARM. */
> + return -EOPNOTSUPP;
> +}
> +
> +int p2m_get_mem_access(struct domain *d, gfn_t gfn,
> + xenmem_access_t *access)
> +{
> + int ret;
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> +
> + p2m_read_lock(p2m);
> + ret = __p2m_get_mem_access(d, gfn, access);
> + p2m_read_unlock(p2m);
> +
> + return ret;
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 837be1d..4e7ce3d 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -7,6 +7,7 @@
> #include <xen/vm_event.h>
> #include <xen/monitor.h>
> #include <xen/iocap.h>
> +#include <xen/mem_access.h>
> #include <public/vm_event.h>
> #include <asm/flushtlb.h>
> #include <asm/gic.h>
> @@ -58,22 +59,6 @@ static inline bool p2m_is_superpage(lpae_t pte, unsigned int level)
> return (level < 3) && p2m_mapping(pte);
> }
>
> -/*
> - * Return the start of the next mapping based on the order of the
> - * current one.
> - */
> -static inline gfn_t gfn_next_boundary(gfn_t gfn, unsigned int order)
> -{
> - /*
> - * The order corresponds to the order of the mapping (or invalid
> - * range) in the page table. So we need to align the GFN before
> - * incrementing.
> - */
> - gfn = _gfn(gfn_x(gfn) & ~((1UL << order) - 1));
> -
> - return gfn_add(gfn, 1UL << order);
> -}
> -
> static void p2m_flush_tlb(struct p2m_domain *p2m);
>
> /* Unlock the flush and do a P2M TLB flush if necessary */
> @@ -602,73 +587,6 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry)
> return 0;
> }
>
> -static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
> - xenmem_access_t *access)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> - void *i;
> - unsigned int index;
> -
> - static const xenmem_access_t memaccess[] = {
> -#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - ASSERT(p2m_is_locked(p2m));
> -
> - /* If no setting was ever set, just return rwx. */
> - if ( !p2m->mem_access_enabled )
> - {
> - *access = XENMEM_access_rwx;
> - return 0;
> - }
> -
> - /* If request to get default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - *access = memaccess[p2m->default_access];
> - return 0;
> - }
> -
> - i = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> -
> - if ( !i )
> - {
> - /*
> - * No setting was found in the Radix tree. Check if the
> - * entry exists in the page-tables.
> - */
> - mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
> -
> - if ( mfn_eq(mfn, INVALID_MFN) )
> - return -ESRCH;
> -
> - /* If entry exists then its rwx. */
> - *access = XENMEM_access_rwx;
> - }
> - else
> - {
> - /* Setting was found in the Radix tree. */
> - index = radix_tree_ptr_to_int(i);
> - if ( index >= ARRAY_SIZE(memaccess) )
> - return -ERANGE;
> -
> - *access = memaccess[index];
> - }
> -
> - return 0;
> -}
> -
> static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
> p2m_access_t a)
> {
> @@ -1454,106 +1372,6 @@ mfn_t gfn_to_mfn(struct domain *d, gfn_t gfn)
> return p2m_lookup(d, gfn, NULL);
> }
>
> -/*
> - * If mem_access is in use it might have been the reason why get_page_from_gva
> - * failed to fetch the page, as it uses the MMU for the permission checking.
> - * Only in these cases we do a software-based type check and fetch the page if
> - * we indeed found a conflicting mem_access setting.
> - */
> -static struct page_info*
> -p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag,
> - const struct vcpu *v)
> -{
> - long rc;
> - paddr_t ipa;
> - gfn_t gfn;
> - mfn_t mfn;
> - xenmem_access_t xma;
> - p2m_type_t t;
> - struct page_info *page = NULL;
> - struct p2m_domain *p2m = &v->domain->arch.p2m;
> -
> - rc = gva_to_ipa(gva, &ipa, flag);
> - if ( rc < 0 )
> - goto err;
> -
> - gfn = _gfn(paddr_to_pfn(ipa));
> -
> - /*
> - * We do this first as this is faster in the default case when no
> - * permission is set on the page.
> - */
> - rc = __p2m_get_mem_access(v->domain, gfn, &xma);
> - if ( rc < 0 )
> - goto err;
> -
> - /* Let's check if mem_access limited the access. */
> - switch ( xma )
> - {
> - default:
> - case XENMEM_access_rwx:
> - case XENMEM_access_rw:
> - /*
> - * If mem_access contains no rw perm restrictions at all then the original
> - * fault was correct.
> - */
> - goto err;
> - case XENMEM_access_n2rwx:
> - case XENMEM_access_n:
> - case XENMEM_access_x:
> - /*
> - * If no r/w is permitted by mem_access, this was a fault caused by mem_access.
> - */
> - break;
> - case XENMEM_access_wx:
> - case XENMEM_access_w:
> - /*
> - * If this was a read then it was because of mem_access, but if it was
> - * a write then the original get_page_from_gva fault was correct.
> - */
> - if ( flag == GV2M_READ )
> - break;
> - else
> - goto err;
> - case XENMEM_access_rx2rw:
> - case XENMEM_access_rx:
> - case XENMEM_access_r:
> - /*
> - * If this was a write then it was because of mem_access, but if it was
> - * a read then the original get_page_from_gva fault was correct.
> - */
> - if ( flag == GV2M_WRITE )
> - break;
> - else
> - goto err;
> - }
> -
> - /*
> - * We had a mem_access permission limiting the access, but the page type
> - * could also be limiting, so we need to check that as well.
> - */
> - mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL);
> - if ( mfn_eq(mfn, INVALID_MFN) )
> - goto err;
> -
> - if ( !mfn_valid(mfn_x(mfn)) )
> - goto err;
> -
> - /*
> - * Base type doesn't allow r/w
> - */
> - if ( t != p2m_ram_rw )
> - goto err;
> -
> - page = mfn_to_page(mfn_x(mfn));
> -
> - if ( unlikely(!get_page(page, v->domain)) )
> - page = NULL;
> -
> -err:
> - return page;
> -}
> -
> struct page_info *get_page_from_gva(struct vcpu *v, vaddr_t va,
> unsigned long flags)
> {
> @@ -1666,236 +1484,6 @@ void __init setup_virt_paging(void)
> smp_call_function(setup_virt_paging_one, (void *)val, 1);
> }
>
> -bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec)
> -{
> - int rc;
> - bool_t violation;
> - xenmem_access_t xma;
> - vm_event_request_t *req;
> - struct vcpu *v = current;
> - struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
> -
> - /* Mem_access is not in use. */
> - if ( !p2m->mem_access_enabled )
> - return true;
> -
> - rc = p2m_get_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), &xma);
> - if ( rc )
> - return true;
> -
> - /* Now check for mem_access violation. */
> - switch ( xma )
> - {
> - case XENMEM_access_rwx:
> - violation = false;
> - break;
> - case XENMEM_access_rw:
> - violation = npfec.insn_fetch;
> - break;
> - case XENMEM_access_wx:
> - violation = npfec.read_access;
> - break;
> - case XENMEM_access_rx:
> - case XENMEM_access_rx2rw:
> - violation = npfec.write_access;
> - break;
> - case XENMEM_access_x:
> - violation = npfec.read_access || npfec.write_access;
> - break;
> - case XENMEM_access_w:
> - violation = npfec.read_access || npfec.insn_fetch;
> - break;
> - case XENMEM_access_r:
> - violation = npfec.write_access || npfec.insn_fetch;
> - break;
> - default:
> - case XENMEM_access_n:
> - case XENMEM_access_n2rwx:
> - violation = true;
> - break;
> - }
> -
> - if ( !violation )
> - return true;
> -
> - /* First, handle rx2rw and n2rwx conversion automatically. */
> - if ( npfec.write_access && xma == XENMEM_access_rx2rw )
> - {
> - rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> - 0, ~0, XENMEM_access_rw, 0);
> - return false;
> - }
> - else if ( xma == XENMEM_access_n2rwx )
> - {
> - rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> - 0, ~0, XENMEM_access_rwx, 0);
> - }
> -
> - /* Otherwise, check if there is a vm_event monitor subscriber */
> - if ( !vm_event_check_ring(&v->domain->vm_event->monitor) )
> - {
> - /* No listener */
> - if ( p2m->access_required )
> - {
> - gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> - "no vm_event listener VCPU %d, dom %d\n",
> - v->vcpu_id, v->domain->domain_id);
> - domain_crash(v->domain);
> - }
> - else
> - {
> - /* n2rwx was already handled */
> - if ( xma != XENMEM_access_n2rwx )
> - {
> - /* A listener is not required, so clear the access
> - * restrictions. */
> - rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> - 0, ~0, XENMEM_access_rwx, 0);
> - }
> - }
> -
> - /* No need to reinject */
> - return false;
> - }
> -
> - req = xzalloc(vm_event_request_t);
> - if ( req )
> - {
> - req->reason = VM_EVENT_REASON_MEM_ACCESS;
> -
> - /* Send request to mem access subscriber */
> - req->u.mem_access.gfn = gpa >> PAGE_SHIFT;
> - req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> - if ( npfec.gla_valid )
> - {
> - req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> - req->u.mem_access.gla = gla;
> -
> - if ( npfec.kind == npfec_kind_with_gla )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> - else if ( npfec.kind == npfec_kind_in_gpt )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> - }
> - req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> - req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> - req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> -
> - if ( monitor_traps(v, (xma != XENMEM_access_n2rwx), req) < 0 )
> - domain_crash(v->domain);
> -
> - xfree(req);
> - }
> -
> - return false;
> -}
> -
> -/*
> - * Set access type for a region of pfns.
> - * If gfn == INVALID_GFN, sets the default access type.
> - */
> -long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> - uint32_t start, uint32_t mask, xenmem_access_t access,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> - p2m_access_t a;
> - unsigned int order;
> - long rc = 0;
> -
> - static const p2m_access_t memaccess[] = {
> -#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - switch ( access )
> - {
> - case 0 ... ARRAY_SIZE(memaccess) - 1:
> - a = memaccess[access];
> - break;
> - case XENMEM_access_default:
> - a = p2m->default_access;
> - break;
> - default:
> - return -EINVAL;
> - }
> -
> - /*
> - * Flip mem_access_enabled to true when a permission is set, as to prevent
> - * allocating or inserting super-pages.
> - */
> - p2m->mem_access_enabled = true;
> -
> - /* If request to set default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - p2m->default_access = a;
> - return 0;
> - }
> -
> - p2m_write_lock(p2m);
> -
> - for ( gfn = gfn_add(gfn, start); nr > start;
> - gfn = gfn_next_boundary(gfn, order) )
> - {
> - p2m_type_t t;
> - mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order);
> -
> -
> - if ( !mfn_eq(mfn, INVALID_MFN) )
> - {
> - order = 0;
> - rc = __p2m_set_entry(p2m, gfn, 0, mfn, t, a);
> - if ( rc )
> - break;
> - }
> -
> - start += gfn_x(gfn_next_boundary(gfn, order)) - gfn_x(gfn);
> - /* Check for continuation if it is not the last iteration */
> - if ( nr > start && !(start & mask) && hypercall_preempt_check() )
> - {
> - rc = start;
> - break;
> - }
> - }
> -
> - p2m_write_unlock(p2m);
> -
> - return rc;
> -}
> -
> -long p2m_set_mem_access_multi(struct domain *d,
> - const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> - const XEN_GUEST_HANDLE(const_uint8) access_list,
> - uint32_t nr, uint32_t start, uint32_t mask,
> - unsigned int altp2m_idx)
> -{
> - /* Not yet implemented on ARM. */
> - return -EOPNOTSUPP;
> -}
> -
> -int p2m_get_mem_access(struct domain *d, gfn_t gfn,
> - xenmem_access_t *access)
> -{
> - int ret;
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> -
> - p2m_read_lock(p2m);
> - ret = __p2m_get_mem_access(d, gfn, access);
> - p2m_read_unlock(p2m);
> -
> - return ret;
> -}
> -
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> index 8ff73fe..f2ea083 100644
> --- a/xen/arch/arm/traps.c
> +++ b/xen/arch/arm/traps.c
> @@ -32,6 +32,7 @@
> #include <xen/domain_page.h>
> #include <xen/perfc.h>
> #include <xen/virtual_region.h>
> +#include <xen/mem_access.h>
> #include <public/sched.h>
> #include <public/xen.h>
> #include <asm/debugger.h>
> diff --git a/xen/arch/x86/mm/Makefile b/xen/arch/x86/mm/Makefile
> index 9804c3a..e977dd8 100644
> --- a/xen/arch/x86/mm/Makefile
> +++ b/xen/arch/x86/mm/Makefile
> @@ -9,6 +9,7 @@ obj-y += guest_walk_3.o
> obj-y += guest_walk_4.o
> obj-y += mem_paging.o
> obj-y += mem_sharing.o
> +obj-y += mem_access.o
>
> guest_walk_%.o: guest_walk.c Makefile
> $(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
> diff --git a/xen/arch/x86/mm/mem_access.c b/xen/arch/x86/mm/mem_access.c
> new file mode 100644
> index 0000000..34a994d
> --- /dev/null
> +++ b/xen/arch/x86/mm/mem_access.c
> @@ -0,0 +1,462 @@
> +/******************************************************************************
> + * arch/x86/mm/mem_access.c
> + *
> + * Parts of this code are Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp)
> + * Parts of this code are Copyright (c) 2007 by Advanced Micro Devices.
> + * Parts of this code are Copyright (c) 2006-2007 by XenSource Inc.
> + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman
> + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/guest_access.h> /* copy_from_guest() */
> +#include <xen/mem_access.h>
> +#include <xen/vm_event.h>
> +#include <xen/event.h>
> +#include <public/vm_event.h>
> +#include <asm/p2m.h>
> +#include <asm/altp2m.h>
> +#include <asm/vm_event.h>
> +
> +#include "mm-locks.h"
> +
> +bool p2m_mem_access_emulate_check(struct vcpu *v,
> + const vm_event_response_t *rsp)
> +{
> + xenmem_access_t access;
> + bool violation = 1;
> + const struct vm_event_mem_access *data = &rsp->u.mem_access;
> +
> + if ( p2m_get_mem_access(v->domain, _gfn(data->gfn), &access) == 0 )
> + {
> + switch ( access )
> + {
> + case XENMEM_access_n:
> + case XENMEM_access_n2rwx:
> + default:
> + violation = data->flags & MEM_ACCESS_RWX;
> + break;
> +
> + case XENMEM_access_r:
> + violation = data->flags & MEM_ACCESS_WX;
> + break;
> +
> + case XENMEM_access_w:
> + violation = data->flags & MEM_ACCESS_RX;
> + break;
> +
> + case XENMEM_access_x:
> + violation = data->flags & MEM_ACCESS_RW;
> + break;
> +
> + case XENMEM_access_rx:
> + case XENMEM_access_rx2rw:
> + violation = data->flags & MEM_ACCESS_W;
> + break;
> +
> + case XENMEM_access_wx:
> + violation = data->flags & MEM_ACCESS_R;
> + break;
> +
> + case XENMEM_access_rw:
> + violation = data->flags & MEM_ACCESS_X;
> + break;
> +
> + case XENMEM_access_rwx:
> + violation = 0;
> + break;
> + }
> + }
> +
> + return violation;
> +}
> +
> +bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> + struct npfec npfec,
> + vm_event_request_t **req_ptr)
> +{
> + struct vcpu *v = current;
> + unsigned long gfn = gpa >> PAGE_SHIFT;
> + struct domain *d = v->domain;
> + struct p2m_domain *p2m = NULL;
> + mfn_t mfn;
> + p2m_type_t p2mt;
> + p2m_access_t p2ma;
> + vm_event_request_t *req;
> + int rc;
> +
> + if ( altp2m_active(d) )
> + p2m = p2m_get_altp2m(v);
> + if ( !p2m )
> + p2m = p2m_get_hostp2m(d);
> +
> + /* First, handle rx2rw conversion automatically.
> + * These calls to p2m->set_entry() must succeed: we have the gfn
> + * locked and just did a successful get_entry(). */
> + gfn_lock(p2m, gfn, 0);
> + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> +
> + if ( npfec.write_access && p2ma == p2m_access_rx2rw )
> + {
> + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw, -1);
> + ASSERT(rc == 0);
> + gfn_unlock(p2m, gfn, 0);
> + return 1;
> + }
> + else if ( p2ma == p2m_access_n2rwx )
> + {
> + ASSERT(npfec.write_access || npfec.read_access || npfec.insn_fetch);
> + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> + p2mt, p2m_access_rwx, -1);
> + ASSERT(rc == 0);
> + }
> + gfn_unlock(p2m, gfn, 0);
> +
> + /* Otherwise, check if there is a memory event listener, and send the message along */
> + if ( !vm_event_check_ring(&d->vm_event->monitor) || !req_ptr )
> + {
> + /* No listener */
> + if ( p2m->access_required )
> + {
> + gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> + "no vm_event listener VCPU %d, dom %d\n",
> + v->vcpu_id, d->domain_id);
> + domain_crash(v->domain);
> + return 0;
> + }
> + else
> + {
> + gfn_lock(p2m, gfn, 0);
> + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> + if ( p2ma != p2m_access_n2rwx )
> + {
> + /* A listener is not required, so clear the access
> + * restrictions. This set must succeed: we have the
> + * gfn locked and just did a successful get_entry(). */
> + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> + p2mt, p2m_access_rwx, -1);
> + ASSERT(rc == 0);
> + }
> + gfn_unlock(p2m, gfn, 0);
> + return 1;
> + }
> + }
> +
> + *req_ptr = NULL;
> + req = xzalloc(vm_event_request_t);
> + if ( req )
> + {
> + *req_ptr = req;
> +
> + req->reason = VM_EVENT_REASON_MEM_ACCESS;
> + req->u.mem_access.gfn = gfn;
> + req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> + if ( npfec.gla_valid )
> + {
> + req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> + req->u.mem_access.gla = gla;
> +
> + if ( npfec.kind == npfec_kind_with_gla )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> + else if ( npfec.kind == npfec_kind_in_gpt )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> + }
> + req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> + req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> + req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> + }
> +
> + /* Return whether vCPU pause is required (aka. sync event) */
> + return (p2ma != p2m_access_n2rwx);
> +}
> +
> +int p2m_set_altp2m_mem_access(struct domain *d, struct p2m_domain *hp2m,
> + struct p2m_domain *ap2m, p2m_access_t a,
> + gfn_t gfn)
> +{
> + mfn_t mfn;
> + p2m_type_t t;
> + p2m_access_t old_a;
> + unsigned int page_order;
> + unsigned long gfn_l = gfn_x(gfn);
> + int rc;
> +
> + mfn = ap2m->get_entry(ap2m, gfn_l, &t, &old_a, 0, NULL, NULL);
> +
> + /* Check host p2m if no valid entry in alternate */
> + if ( !mfn_valid(mfn_x(mfn)) )
> + {
> +
> + mfn = __get_gfn_type_access(hp2m, gfn_l, &t, &old_a,
> + P2M_ALLOC | P2M_UNSHARE, &page_order, 0);
> +
> + rc = -ESRCH;
> + if ( !mfn_valid(mfn_x(mfn)) || t != p2m_ram_rw )
> + return rc;
> +
> + /* If this is a superpage, copy that first */
> + if ( page_order != PAGE_ORDER_4K )
> + {
> + unsigned long mask = ~((1UL << page_order) - 1);
> + unsigned long gfn2_l = gfn_l & mask;
> + mfn_t mfn2 = _mfn(mfn_x(mfn) & mask);
> +
> + rc = ap2m->set_entry(ap2m, gfn2_l, mfn2, page_order, t, old_a, 1);
> + if ( rc )
> + return rc;
> + }
> + }
> +
> + return ap2m->set_entry(ap2m, gfn_l, mfn, PAGE_ORDER_4K, t, a,
> + (current->domain != d));
> +}
> +
> +static int set_mem_access(struct domain *d, struct p2m_domain *p2m,
> + struct p2m_domain *ap2m, p2m_access_t a,
> + gfn_t gfn)
> +{
> + int rc = 0;
> +
> + if ( ap2m )
> + {
> + rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, gfn);
> + /* If the corresponding mfn is invalid we will want to just skip it */
> + if ( rc == -ESRCH )
> + rc = 0;
> + }
> + else
> + {
> + mfn_t mfn;
> + p2m_access_t _a;
> + p2m_type_t t;
> + unsigned long gfn_l = gfn_x(gfn);
> +
> + mfn = p2m->get_entry(p2m, gfn_l, &t, &_a, 0, NULL, NULL);
> + rc = p2m->set_entry(p2m, gfn_l, mfn, PAGE_ORDER_4K, t, a, -1);
> + }
> +
> + return rc;
> +}
> +
> +static bool xenmem_access_to_p2m_access(struct p2m_domain *p2m,
> + xenmem_access_t xaccess,
> + p2m_access_t *paccess)
> +{
> + static const p2m_access_t memaccess[] = {
> +#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + switch ( xaccess )
> + {
> + case 0 ... ARRAY_SIZE(memaccess) - 1:
> + *paccess = memaccess[xaccess];
> + break;
> + case XENMEM_access_default:
> + *paccess = p2m->default_access;
> + break;
> + default:
> + return false;
> + }
> +
> + return true;
> +}
> +
> +/*
> + * Set access type for a region of gfns.
> + * If gfn == INVALID_GFN, sets the default access type.
> + */
> +long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> + p2m_access_t a;
> + unsigned long gfn_l;
> + long rc = 0;
> +
> + /* altp2m view 0 is treated as the hostp2m */
> + if ( altp2m_idx )
> + {
> + if ( altp2m_idx >= MAX_ALTP2M ||
> + d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> + return -EINVAL;
> +
> + ap2m = d->arch.altp2m_p2m[altp2m_idx];
> + }
> +
> + if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> + return -EINVAL;
> +
> + /* If request to set default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + p2m->default_access = a;
> + return 0;
> + }
> +
> + p2m_lock(p2m);
> + if ( ap2m )
> + p2m_lock(ap2m);
> +
> + for ( gfn_l = gfn_x(gfn) + start; nr > start; ++gfn_l )
> + {
> + rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> +
> + if ( rc )
> + break;
> +
> + /* Check for continuation if it's not the last iteration. */
> + if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> + {
> + rc = start;
> + break;
> + }
> + }
> +
> + if ( ap2m )
> + p2m_unlock(ap2m);
> + p2m_unlock(p2m);
> +
> + return rc;
> +}
> +
> +long p2m_set_mem_access_multi(struct domain *d,
> + const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> + const XEN_GUEST_HANDLE(const_uint8) access_list,
> + uint32_t nr, uint32_t start, uint32_t mask,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> + long rc = 0;
> +
> + /* altp2m view 0 is treated as the hostp2m */
> + if ( altp2m_idx )
> + {
> + if ( altp2m_idx >= MAX_ALTP2M ||
> + d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> + return -EINVAL;
> +
> + ap2m = d->arch.altp2m_p2m[altp2m_idx];
> + }
> +
> + p2m_lock(p2m);
> + if ( ap2m )
> + p2m_lock(ap2m);
> +
> + while ( start < nr )
> + {
> + p2m_access_t a;
> + uint8_t access;
> + uint64_t gfn_l;
> +
> + if ( copy_from_guest_offset(&gfn_l, pfn_list, start, 1) ||
> + copy_from_guest_offset(&access, access_list, start, 1) )
> + {
> + rc = -EFAULT;
> + break;
> + }
> +
> + if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> + {
> + rc = -EINVAL;
> + break;
> + }
> +
> + rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> +
> + if ( rc )
> + break;
> +
> + /* Check for continuation if it's not the last iteration. */
> + if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> + {
> + rc = start;
> + break;
> + }
> + }
> +
> + if ( ap2m )
> + p2m_unlock(ap2m);
> + p2m_unlock(p2m);
> +
> + return rc;
> +}
> +
> +/*
> + * Get access type for a gfn.
> + * If gfn == INVALID_GFN, gets the default access type.
> + */
> +int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + p2m_type_t t;
> + p2m_access_t a;
> + mfn_t mfn;
> +
> + static const xenmem_access_t memaccess[] = {
> +#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + /* If request to get default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + *access = memaccess[p2m->default_access];
> + return 0;
> + }
> +
> + gfn_lock(p2m, gfn, 0);
> + mfn = p2m->get_entry(p2m, gfn_x(gfn), &t, &a, 0, NULL, NULL);
> + gfn_unlock(p2m, gfn, 0);
> +
> + if ( mfn_eq(mfn, INVALID_MFN) )
> + return -ESRCH;
> +
> + if ( (unsigned) a >= ARRAY_SIZE(memaccess) )
> + return -ERANGE;
> +
> + *access = memaccess[a];
> + return 0;
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
> index 6a45185..6299d5a 100644
> --- a/xen/arch/x86/mm/p2m.c
> +++ b/xen/arch/x86/mm/p2m.c
> @@ -1589,433 +1589,12 @@ void p2m_mem_paging_resume(struct domain *d, vm_event_response_t *rsp)
> }
> }
>
> -bool p2m_mem_access_emulate_check(struct vcpu *v,
> - const vm_event_response_t *rsp)
> -{
> - xenmem_access_t access;
> - bool violation = 1;
> - const struct vm_event_mem_access *data = &rsp->u.mem_access;
> -
> - if ( p2m_get_mem_access(v->domain, _gfn(data->gfn), &access) == 0 )
> - {
> - switch ( access )
> - {
> - case XENMEM_access_n:
> - case XENMEM_access_n2rwx:
> - default:
> - violation = data->flags & MEM_ACCESS_RWX;
> - break;
> -
> - case XENMEM_access_r:
> - violation = data->flags & MEM_ACCESS_WX;
> - break;
> -
> - case XENMEM_access_w:
> - violation = data->flags & MEM_ACCESS_RX;
> - break;
> -
> - case XENMEM_access_x:
> - violation = data->flags & MEM_ACCESS_RW;
> - break;
> -
> - case XENMEM_access_rx:
> - case XENMEM_access_rx2rw:
> - violation = data->flags & MEM_ACCESS_W;
> - break;
> -
> - case XENMEM_access_wx:
> - violation = data->flags & MEM_ACCESS_R;
> - break;
> -
> - case XENMEM_access_rw:
> - violation = data->flags & MEM_ACCESS_X;
> - break;
> -
> - case XENMEM_access_rwx:
> - violation = 0;
> - break;
> - }
> - }
> -
> - return violation;
> -}
> -
> void p2m_altp2m_check(struct vcpu *v, uint16_t idx)
> {
> if ( altp2m_active(v->domain) )
> p2m_switch_vcpu_altp2m_by_id(v, idx);
> }
>
> -bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> - struct npfec npfec,
> - vm_event_request_t **req_ptr)
> -{
> - struct vcpu *v = current;
> - unsigned long gfn = gpa >> PAGE_SHIFT;
> - struct domain *d = v->domain;
> - struct p2m_domain *p2m = NULL;
> - mfn_t mfn;
> - p2m_type_t p2mt;
> - p2m_access_t p2ma;
> - vm_event_request_t *req;
> - int rc;
> -
> - if ( altp2m_active(d) )
> - p2m = p2m_get_altp2m(v);
> - if ( !p2m )
> - p2m = p2m_get_hostp2m(d);
> -
> - /* First, handle rx2rw conversion automatically.
> - * These calls to p2m->set_entry() must succeed: we have the gfn
> - * locked and just did a successful get_entry(). */
> - gfn_lock(p2m, gfn, 0);
> - mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> -
> - if ( npfec.write_access && p2ma == p2m_access_rx2rw )
> - {
> - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw, -1);
> - ASSERT(rc == 0);
> - gfn_unlock(p2m, gfn, 0);
> - return 1;
> - }
> - else if ( p2ma == p2m_access_n2rwx )
> - {
> - ASSERT(npfec.write_access || npfec.read_access || npfec.insn_fetch);
> - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> - p2mt, p2m_access_rwx, -1);
> - ASSERT(rc == 0);
> - }
> - gfn_unlock(p2m, gfn, 0);
> -
> - /* Otherwise, check if there is a memory event listener, and send the message along */
> - if ( !vm_event_check_ring(&d->vm_event->monitor) || !req_ptr )
> - {
> - /* No listener */
> - if ( p2m->access_required )
> - {
> - gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> - "no vm_event listener VCPU %d, dom %d\n",
> - v->vcpu_id, d->domain_id);
> - domain_crash(v->domain);
> - return 0;
> - }
> - else
> - {
> - gfn_lock(p2m, gfn, 0);
> - mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> - if ( p2ma != p2m_access_n2rwx )
> - {
> - /* A listener is not required, so clear the access
> - * restrictions. This set must succeed: we have the
> - * gfn locked and just did a successful get_entry(). */
> - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> - p2mt, p2m_access_rwx, -1);
> - ASSERT(rc == 0);
> - }
> - gfn_unlock(p2m, gfn, 0);
> - return 1;
> - }
> - }
> -
> - *req_ptr = NULL;
> - req = xzalloc(vm_event_request_t);
> - if ( req )
> - {
> - *req_ptr = req;
> -
> - req->reason = VM_EVENT_REASON_MEM_ACCESS;
> - req->u.mem_access.gfn = gfn;
> - req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> - if ( npfec.gla_valid )
> - {
> - req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> - req->u.mem_access.gla = gla;
> -
> - if ( npfec.kind == npfec_kind_with_gla )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> - else if ( npfec.kind == npfec_kind_in_gpt )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> - }
> - req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> - req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> - req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> - }
> -
> - /* Return whether vCPU pause is required (aka. sync event) */
> - return (p2ma != p2m_access_n2rwx);
> -}
> -
> -static inline
> -int p2m_set_altp2m_mem_access(struct domain *d, struct p2m_domain *hp2m,
> - struct p2m_domain *ap2m, p2m_access_t a,
> - gfn_t gfn)
> -{
> - mfn_t mfn;
> - p2m_type_t t;
> - p2m_access_t old_a;
> - unsigned int page_order;
> - unsigned long gfn_l = gfn_x(gfn);
> - int rc;
> -
> - mfn = ap2m->get_entry(ap2m, gfn_l, &t, &old_a, 0, NULL, NULL);
> -
> - /* Check host p2m if no valid entry in alternate */
> - if ( !mfn_valid(mfn) )
> - {
> -
> - mfn = __get_gfn_type_access(hp2m, gfn_l, &t, &old_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 ( page_order != PAGE_ORDER_4K )
> - {
> - unsigned long mask = ~((1UL << page_order) - 1);
> - unsigned long gfn2_l = gfn_l & mask;
> - mfn_t mfn2 = _mfn(mfn_x(mfn) & mask);
> -
> - rc = ap2m->set_entry(ap2m, gfn2_l, mfn2, page_order, t, old_a, 1);
> - if ( rc )
> - return rc;
> - }
> - }
> -
> - return ap2m->set_entry(ap2m, gfn_l, mfn, PAGE_ORDER_4K, t, a,
> - (current->domain != d));
> -}
> -
> -static int set_mem_access(struct domain *d, struct p2m_domain *p2m,
> - struct p2m_domain *ap2m, p2m_access_t a,
> - gfn_t gfn)
> -{
> - int rc = 0;
> -
> - if ( ap2m )
> - {
> - rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, gfn);
> - /* If the corresponding mfn is invalid we will want to just skip it */
> - if ( rc == -ESRCH )
> - rc = 0;
> - }
> - else
> - {
> - mfn_t mfn;
> - p2m_access_t _a;
> - p2m_type_t t;
> - unsigned long gfn_l = gfn_x(gfn);
> -
> - mfn = p2m->get_entry(p2m, gfn_l, &t, &_a, 0, NULL, NULL);
> - rc = p2m->set_entry(p2m, gfn_l, mfn, PAGE_ORDER_4K, t, a, -1);
> - }
> -
> - return rc;
> -}
> -
> -static bool xenmem_access_to_p2m_access(struct p2m_domain *p2m,
> - xenmem_access_t xaccess,
> - p2m_access_t *paccess)
> -{
> - static const p2m_access_t memaccess[] = {
> -#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - switch ( xaccess )
> - {
> - case 0 ... ARRAY_SIZE(memaccess) - 1:
> - *paccess = memaccess[xaccess];
> - break;
> - case XENMEM_access_default:
> - *paccess = p2m->default_access;
> - break;
> - default:
> - return false;
> - }
> -
> - return true;
> -}
> -
> -/*
> - * Set access type for a region of gfns.
> - * If gfn == INVALID_GFN, sets the default access type.
> - */
> -long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> - uint32_t start, uint32_t mask, xenmem_access_t access,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> - p2m_access_t a;
> - unsigned long gfn_l;
> - long rc = 0;
> -
> - /* altp2m view 0 is treated as the hostp2m */
> - if ( altp2m_idx )
> - {
> - if ( altp2m_idx >= MAX_ALTP2M ||
> - d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> - return -EINVAL;
> -
> - ap2m = d->arch.altp2m_p2m[altp2m_idx];
> - }
> -
> - if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> - return -EINVAL;
> -
> - /* If request to set default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - p2m->default_access = a;
> - return 0;
> - }
> -
> - p2m_lock(p2m);
> - if ( ap2m )
> - p2m_lock(ap2m);
> -
> - for ( gfn_l = gfn_x(gfn) + start; nr > start; ++gfn_l )
> - {
> - rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> -
> - if ( rc )
> - break;
> -
> - /* Check for continuation if it's not the last iteration. */
> - if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> - {
> - rc = start;
> - break;
> - }
> - }
> -
> - if ( ap2m )
> - p2m_unlock(ap2m);
> - p2m_unlock(p2m);
> -
> - return rc;
> -}
> -
> -long p2m_set_mem_access_multi(struct domain *d,
> - const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> - const XEN_GUEST_HANDLE(const_uint8) access_list,
> - uint32_t nr, uint32_t start, uint32_t mask,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> - long rc = 0;
> -
> - /* altp2m view 0 is treated as the hostp2m */
> - if ( altp2m_idx )
> - {
> - if ( altp2m_idx >= MAX_ALTP2M ||
> - d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> - return -EINVAL;
> -
> - ap2m = d->arch.altp2m_p2m[altp2m_idx];
> - }
> -
> - p2m_lock(p2m);
> - if ( ap2m )
> - p2m_lock(ap2m);
> -
> - while ( start < nr )
> - {
> - p2m_access_t a;
> - uint8_t access;
> - uint64_t gfn_l;
> -
> - if ( copy_from_guest_offset(&gfn_l, pfn_list, start, 1) ||
> - copy_from_guest_offset(&access, access_list, start, 1) )
> - {
> - rc = -EFAULT;
> - break;
> - }
> -
> - if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> - {
> - rc = -EINVAL;
> - break;
> - }
> -
> - rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> -
> - if ( rc )
> - break;
> -
> - /* Check for continuation if it's not the last iteration. */
> - if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> - {
> - rc = start;
> - break;
> - }
> - }
> -
> - if ( ap2m )
> - p2m_unlock(ap2m);
> - p2m_unlock(p2m);
> -
> - return rc;
> -}
> -
> -/*
> - * Get access type for a gfn.
> - * If gfn == INVALID_GFN, gets the default access type.
> - */
> -int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> - p2m_type_t t;
> - p2m_access_t a;
> - mfn_t mfn;
> -
> - static const xenmem_access_t memaccess[] = {
> -#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - /* If request to get default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - *access = memaccess[p2m->default_access];
> - return 0;
> - }
> -
> - gfn_lock(p2m, gfn, 0);
> - mfn = p2m->get_entry(p2m, gfn_x(gfn), &t, &a, 0, NULL, NULL);
> - gfn_unlock(p2m, gfn, 0);
> -
> - if ( mfn_eq(mfn, INVALID_MFN) )
> - return -ESRCH;
> -
> - if ( (unsigned) a >= ARRAY_SIZE(memaccess) )
> - return -ERANGE;
> -
> - *access = memaccess[a];
> - return 0;
> -}
> -
> static struct p2m_domain *
> p2m_getlru_nestedp2m(struct domain *d, struct p2m_domain *p2m)
> {
> diff --git a/xen/arch/x86/vm_event.c b/xen/arch/x86/vm_event.c
> index 1e88d67..8d8bc4a 100644
> --- a/xen/arch/x86/vm_event.c
> +++ b/xen/arch/x86/vm_event.c
> @@ -18,7 +18,8 @@
> * License along with this program; If not, see <http://www.gnu.org/licenses/>.
> */
>
> -#include <asm/p2m.h>
> +#include <xen/sched.h>
> +#include <xen/mem_access.h>
> #include <asm/vm_event.h>
>
> /* Implicitly serialized by the domctl lock. */
> diff --git a/xen/common/mem_access.c b/xen/common/mem_access.c
> index 565a320..19f63bb 100644
> --- a/xen/common/mem_access.c
> +++ b/xen/common/mem_access.c
> @@ -24,8 +24,8 @@
> #include <xen/guest_access.h>
> #include <xen/hypercall.h>
> #include <xen/vm_event.h>
> +#include <xen/mem_access.h>
> #include <public/memory.h>
> -#include <asm/p2m.h>
> #include <xsm/xsm.h>
>
> int mem_access_memop(unsigned long cmd,
> diff --git a/xen/include/asm-arm/mem_access.h b/xen/include/asm-arm/mem_access.h
> new file mode 100644
> index 0000000..3a155f8
> --- /dev/null
> +++ b/xen/include/asm-arm/mem_access.h
> @@ -0,0 +1,53 @@
> +/*
> + * mem_access.h: architecture specific mem_access handling routines
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef _ASM_ARM_MEM_ACCESS_H
> +#define _ASM_ARM_MEM_ACCESS_H
> +
> +static inline
> +bool p2m_mem_access_emulate_check(struct vcpu *v,
> + const vm_event_response_t *rsp)
> +{
> + /* Not supported on ARM. */
> + return 0;
> +}
> +
> +/* vm_event and mem_access are supported on any ARM guest */
> +static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> +{
> + return 1;
> +}
> +
> +/*
> + * Send mem event based on the access. Boolean return value indicates if trap
> + * needs to be injected into guest.
> + */
> +bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec);
> +
> +struct page_info*
> +p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag,
> + const struct vcpu *v);
> +
> +#endif /* _ASM_ARM_MEM_ACCESS_H */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> index fdb6b47..2b22e9a 100644
> --- a/xen/include/asm-arm/p2m.h
> +++ b/xen/include/asm-arm/p2m.h
> @@ -4,6 +4,7 @@
> #include <xen/mm.h>
> #include <xen/radix-tree.h>
> #include <xen/rwlock.h>
> +#include <xen/mem_access.h>
> #include <public/vm_event.h> /* for vm_event_response_t */
> #include <public/memory.h>
> #include <xen/p2m-common.h>
> @@ -139,14 +140,6 @@ typedef enum {
> p2m_to_mask(p2m_map_foreign)))
>
> static inline
> -bool p2m_mem_access_emulate_check(struct vcpu *v,
> - const vm_event_response_t *rsp)
> -{
> - /* Not supported on ARM. */
> - return 0;
> -}
> -
> -static inline
> void p2m_altp2m_check(struct vcpu *v, uint16_t idx)
> {
> /* Not supported on ARM. */
> @@ -343,22 +336,26 @@ static inline int get_page_and_type(struct page_info *page,
> /* get host p2m table */
> #define p2m_get_hostp2m(d) (&(d)->arch.p2m)
>
> -/* vm_event and mem_access are supported on any ARM guest */
> -static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> -{
> - return 1;
> -}
> -
> static inline bool_t p2m_vm_event_sanity_check(struct domain *d)
> {
> return 1;
> }
>
> /*
> - * Send mem event based on the access. Boolean return value indicates if trap
> - * needs to be injected into guest.
> + * Return the start of the next mapping based on the order of the
> + * current one.
> */
> -bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec);
> +static inline gfn_t gfn_next_boundary(gfn_t gfn, unsigned int order)
> +{
> + /*
> + * The order corresponds to the order of the mapping (or invalid
> + * range) in the page table. So we need to align the GFN before
> + * incrementing.
> + */
> + gfn = _gfn(gfn_x(gfn) & ~((1UL << order) - 1));
> +
> + return gfn_add(gfn, 1UL << order);
> +}
>
> #endif /* _XEN_P2M_H */
>
> diff --git a/xen/include/asm-x86/mem_access.h b/xen/include/asm-x86/mem_access.h
> new file mode 100644
> index 0000000..9f7b409
> --- /dev/null
> +++ b/xen/include/asm-x86/mem_access.h
> @@ -0,0 +1,61 @@
> +/******************************************************************************
> + * include/asm-x86/mem_access.h
> + *
> + * Memory access support.
> + *
> + * Copyright (c) 2011 GridCentric Inc. (Andres Lagar-Cavilla)
> + * Copyright (c) 2007 Advanced Micro Devices (Wei Huang)
> + * Parts of this code are Copyright (c) 2006-2007 by XenSource Inc.
> + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman
> + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ASM_X86_MEM_ACCESS_H__
> +#define __ASM_X86_MEM_ACCESS_H__
> +
> +/*
> + * Setup vm_event request based on the access (gla is -1ull if not available).
> + * Handles the rw2rx conversion. Boolean return value indicates if event type
> + * is syncronous (aka. requires vCPU pause). If the req_ptr has been populated,
> + * then the caller should use monitor_traps to send the event on the MONITOR
> + * ring. Once having released get_gfn* locks caller must also xfree the
> + * request.
> + */
> +bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> + struct npfec npfec,
> + vm_event_request_t **req_ptr);
> +
> +/* Check for emulation and mark vcpu for skipping one instruction
> + * upon rescheduling if required. */
> +bool p2m_mem_access_emulate_check(struct vcpu *v,
> + const vm_event_response_t *rsp);
> +
> +/* Sanity check for mem_access hardware support */
> +static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> +{
> + return is_hvm_domain(d) && cpu_has_vmx && hap_enabled(d);
> +}
> +
> +#endif /*__ASM_X86_MEM_ACCESS_H__ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h
> index 7035860..8964e90 100644
> --- a/xen/include/asm-x86/p2m.h
> +++ b/xen/include/asm-x86/p2m.h
> @@ -29,6 +29,7 @@
> #include <xen/config.h>
> #include <xen/paging.h>
> #include <xen/p2m-common.h>
> +#include <xen/mem_access.h>
> #include <asm/mem_sharing.h>
> #include <asm/page.h> /* for pagetable_t */
>
> @@ -663,29 +664,6 @@ int p2m_mem_paging_prep(struct domain *d, unsigned long gfn, uint64_t buffer);
> /* Resume normal operation (in case a domain was paused) */
> void p2m_mem_paging_resume(struct domain *d, vm_event_response_t *rsp);
>
> -/*
> - * Setup vm_event request based on the access (gla is -1ull if not available).
> - * Handles the rw2rx conversion. Boolean return value indicates if event type
> - * is syncronous (aka. requires vCPU pause). If the req_ptr has been populated,
> - * then the caller should use monitor_traps to send the event on the MONITOR
> - * ring. Once having released get_gfn* locks caller must also xfree the
> - * request.
> - */
> -bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> - struct npfec npfec,
> - vm_event_request_t **req_ptr);
> -
> -/* Check for emulation and mark vcpu for skipping one instruction
> - * upon rescheduling if required. */
> -bool p2m_mem_access_emulate_check(struct vcpu *v,
> - const vm_event_response_t *rsp);
> -
> -/* Sanity check for mem_access hardware support */
> -static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> -{
> - return is_hvm_domain(d) && cpu_has_vmx && hap_enabled(d);
> -}
> -
> /*
> * Internal functions, only called by other p2m code
> */
> diff --git a/xen/include/xen/mem_access.h b/xen/include/xen/mem_access.h
> index da36e07..5ab34c1 100644
> --- a/xen/include/xen/mem_access.h
> +++ b/xen/include/xen/mem_access.h
> @@ -19,29 +19,78 @@
> * along with this program; If not, see <http://www.gnu.org/licenses/>.
> */
>
> -#ifndef _XEN_ASM_MEM_ACCESS_H
> -#define _XEN_ASM_MEM_ACCESS_H
> +#ifndef _XEN_MEM_ACCESS_H
> +#define _XEN_MEM_ACCESS_H
>
> +#include <xen/types.h>
> +#include <xen/mm.h>
> #include <public/memory.h>
> -#include <asm/p2m.h>
> +#include <public/vm_event.h>
> +#include <asm/mem_access.h>
>
> -#ifdef CONFIG_HAS_MEM_ACCESS
> +/*
> + * Additional access types, which are used to further restrict
> + * the permissions given my the p2m_type_t memory type. Violations
> + * caused by p2m_access_t restrictions are sent to the vm_event
> + * interface.
> + *
> + * The access permissions are soft state: when any ambiguous change of page
> + * type or use occurs, or when pages are flushed, swapped, or at any other
> + * convenient type, the access permissions can get reset to the p2m_domain
> + * default.
> + */
> +typedef enum {
> + /* Code uses bottom three bits with bitmask semantics */
> + p2m_access_n = 0, /* No access allowed. */
> + p2m_access_r = 1 << 0,
> + p2m_access_w = 1 << 1,
> + p2m_access_x = 1 << 2,
> + p2m_access_rw = p2m_access_r | p2m_access_w,
> + p2m_access_rx = p2m_access_r | p2m_access_x,
> + p2m_access_wx = p2m_access_w | p2m_access_x,
> + p2m_access_rwx = p2m_access_r | p2m_access_w | p2m_access_x,
> +
> + p2m_access_rx2rw = 8, /* Special: page goes from RX to RW on write */
> + p2m_access_n2rwx = 9, /* Special: page goes from N to RWX on access, *
> + * generates an event but does not pause the
> + * vcpu */
> +
> + /* NOTE: Assumed to be only 4 bits right now on x86. */
> +} p2m_access_t;
> +
> +/*
> + * Set access type for a region of gfns.
> + * If gfn == INVALID_GFN, sets the default access type.
> + */
> +long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access,
> + unsigned int altp2m_idx);
>
> +long p2m_set_mem_access_multi(struct domain *d,
> + const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> + const XEN_GUEST_HANDLE(const_uint8) access_list,
> + uint32_t nr, uint32_t start, uint32_t mask,
> + unsigned int altp2m_idx);
> +
> +/*
> + * Get access type for a gfn.
> + * If gfn == INVALID_GFN, gets the default access type.
> + */
> +int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access);
> +
> +#ifdef CONFIG_HAS_MEM_ACCESS
> int mem_access_memop(unsigned long cmd,
> XEN_GUEST_HANDLE_PARAM(xen_mem_access_op_t) arg);
> -
> #else
> -
> static inline
> int mem_access_memop(unsigned long cmd,
> XEN_GUEST_HANDLE_PARAM(xen_mem_access_op_t) arg)
> {
> return -ENOSYS;
> }
> +#endif /* CONFIG_HAS_MEM_ACCESS */
>
> -#endif /* HAS_MEM_ACCESS */
> -
> -#endif /* _XEN_ASM_MEM_ACCESS_H */
> +#endif /* _XEN_MEM_ACCESS_H */
>
> /*
> * Local variables:
> diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
> index 3be1e91..8cd5a6b 100644
> --- a/xen/include/xen/p2m-common.h
> +++ b/xen/include/xen/p2m-common.h
> @@ -1,38 +1,6 @@
> #ifndef _XEN_P2M_COMMON_H
> #define _XEN_P2M_COMMON_H
>
> -#include <public/vm_event.h>
> -
> -/*
> - * Additional access types, which are used to further restrict
> - * the permissions given my the p2m_type_t memory type. Violations
> - * caused by p2m_access_t restrictions are sent to the vm_event
> - * interface.
> - *
> - * The access permissions are soft state: when any ambiguous change of page
> - * type or use occurs, or when pages are flushed, swapped, or at any other
> - * convenient type, the access permissions can get reset to the p2m_domain
> - * default.
> - */
> -typedef enum {
> - /* Code uses bottom three bits with bitmask semantics */
> - p2m_access_n = 0, /* No access allowed. */
> - p2m_access_r = 1 << 0,
> - p2m_access_w = 1 << 1,
> - p2m_access_x = 1 << 2,
> - p2m_access_rw = p2m_access_r | p2m_access_w,
> - p2m_access_rx = p2m_access_r | p2m_access_x,
> - p2m_access_wx = p2m_access_w | p2m_access_x,
> - p2m_access_rwx = p2m_access_r | p2m_access_w | p2m_access_x,
> -
> - p2m_access_rx2rw = 8, /* Special: page goes from RX to RW on write */
> - p2m_access_n2rwx = 9, /* Special: page goes from N to RWX on access, *
> - * generates an event but does not pause the
> - * vcpu */
> -
> - /* NOTE: Assumed to be only 4 bits right now on x86. */
> -} p2m_access_t;
> -
> /* Map MMIO regions in the p2m: start_gfn and nr describe the range in
> * * the guest physical address space to map, starting from the machine
> * * frame number mfn. */
> @@ -45,24 +13,4 @@ int unmap_mmio_regions(struct domain *d,
> unsigned long nr,
> mfn_t mfn);
>
> -/*
> - * Set access type for a region of gfns.
> - * If gfn == INVALID_GFN, sets the default access type.
> - */
> -long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> - uint32_t start, uint32_t mask, xenmem_access_t access,
> - unsigned int altp2m_idx);
> -
> -long p2m_set_mem_access_multi(struct domain *d,
> - const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> - const XEN_GUEST_HANDLE(const_uint8) access_list,
> - uint32_t nr, uint32_t start, uint32_t mask,
> - unsigned int altp2m_idx);
> -
> -/*
> - * Get access type for a gfn.
> - * If gfn == INVALID_GFN, gets the default access type.
> - */
> -int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access);
> -
> #endif /* _XEN_P2M_COMMON_H */
> --
> 2.10.2
>
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v2 2/2] p2m: split mem_access into separate files
2016-12-09 19:59 ` [PATCH v2 2/2] p2m: split mem_access into separate files Tamas K Lengyel
2017-01-03 15:31 ` Tamas K Lengyel
@ 2017-01-30 21:11 ` Stefano Stabellini
1 sibling, 0 replies; 24+ messages in thread
From: Stefano Stabellini @ 2017-01-30 21:11 UTC (permalink / raw)
To: Tamas K Lengyel
Cc: Stefano Stabellini, George Dunlap, Andrew Cooper, Julien Grall,
Jan Beulich, xen-devel
On Fri, 9 Dec 2016, Tamas K Lengyel wrote:
> This patch relocates mem_access components that are currently mixed with p2m
> code into separate files. This better aligns the code with similar subsystems,
> such as mem_sharing and mem_paging, which are already in separate files. There
> are no code-changes introduced, the patch is mechanical code movement.
>
> On ARM we also relocate the static inline gfn_next_boundary function to p2m.h
> as it is a function the mem_access code needs access to.
>
> Signed-off-by: Tamas K Lengyel <tamas.lengyel@zentific.com>
> Acked-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
I'll commit both patches shortly.
> ---
> Cc: Stefano Stabellini <sstabellini@kernel.org>
> Cc: Julien Grall <julien.grall@arm.com>
> Cc: Jan Beulich <jbeulich@suse.com>
> Cc: Andrew Cooper <andrew.cooper3@citrix.com>
> Cc: George Dunlap <george.dunlap@eu.citrix.com>
>
> v2: Don't move ARM radix tree functions
> Include asm/mem_accesss.h in xen/mem_access.h
> ---
> MAINTAINERS | 2 +
> xen/arch/arm/Makefile | 1 +
> xen/arch/arm/mem_access.c | 431 ++++++++++++++++++++++++++++++++++++
> xen/arch/arm/p2m.c | 414 +----------------------------------
> xen/arch/arm/traps.c | 1 +
> xen/arch/x86/mm/Makefile | 1 +
> xen/arch/x86/mm/mem_access.c | 462 +++++++++++++++++++++++++++++++++++++++
> xen/arch/x86/mm/p2m.c | 421 -----------------------------------
> xen/arch/x86/vm_event.c | 3 +-
> xen/common/mem_access.c | 2 +-
> xen/include/asm-arm/mem_access.h | 53 +++++
> xen/include/asm-arm/p2m.h | 31 ++-
> xen/include/asm-x86/mem_access.h | 61 ++++++
> xen/include/asm-x86/p2m.h | 24 +-
> xen/include/xen/mem_access.h | 67 +++++-
> xen/include/xen/p2m-common.h | 52 -----
> 16 files changed, 1089 insertions(+), 937 deletions(-)
> create mode 100644 xen/arch/arm/mem_access.c
> create mode 100644 xen/arch/x86/mm/mem_access.c
> create mode 100644 xen/include/asm-arm/mem_access.h
> create mode 100644 xen/include/asm-x86/mem_access.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f0d0202..fb26be3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -402,6 +402,8 @@ S: Supported
> F: tools/tests/xen-access
> F: xen/arch/*/monitor.c
> F: xen/arch/*/vm_event.c
> +F: xen/arch/arm/mem_access.c
> +F: xen/arch/x86/mm/mem_access.c
> F: xen/arch/x86/hvm/monitor.c
> F: xen/common/mem_access.c
> F: xen/common/monitor.c
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index da39d39..b095e8a 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -24,6 +24,7 @@ obj-y += io.o
> obj-y += irq.o
> obj-y += kernel.o
> obj-$(CONFIG_LIVEPATCH) += livepatch.o
> +obj-y += mem_access.o
> obj-y += mm.o
> obj-y += monitor.o
> obj-y += p2m.o
> diff --git a/xen/arch/arm/mem_access.c b/xen/arch/arm/mem_access.c
> new file mode 100644
> index 0000000..a6e5bcd
> --- /dev/null
> +++ b/xen/arch/arm/mem_access.c
> @@ -0,0 +1,431 @@
> +/*
> + * arch/arm/mem_access.c
> + *
> + * Architecture-specific mem_access handling routines
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/config.h>
> +#include <xen/mem_access.h>
> +#include <xen/monitor.h>
> +#include <xen/sched.h>
> +#include <xen/vm_event.h>
> +#include <public/vm_event.h>
> +#include <asm/event.h>
> +
> +static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
> + xenmem_access_t *access)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + void *i;
> + unsigned int index;
> +
> + static const xenmem_access_t memaccess[] = {
> +#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + ASSERT(p2m_is_locked(p2m));
> +
> + /* If no setting was ever set, just return rwx. */
> + if ( !p2m->mem_access_enabled )
> + {
> + *access = XENMEM_access_rwx;
> + return 0;
> + }
> +
> + /* If request to get default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + *access = memaccess[p2m->default_access];
> + return 0;
> + }
> +
> + i = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> +
> + if ( !i )
> + {
> + /*
> + * No setting was found in the Radix tree. Check if the
> + * entry exists in the page-tables.
> + */
> + mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
> +
> + if ( mfn_eq(mfn, INVALID_MFN) )
> + return -ESRCH;
> +
> + /* If entry exists then its rwx. */
> + *access = XENMEM_access_rwx;
> + }
> + else
> + {
> + /* Setting was found in the Radix tree. */
> + index = radix_tree_ptr_to_int(i);
> + if ( index >= ARRAY_SIZE(memaccess) )
> + return -ERANGE;
> +
> + *access = memaccess[index];
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * If mem_access is in use it might have been the reason why get_page_from_gva
> + * failed to fetch the page, as it uses the MMU for the permission checking.
> + * Only in these cases we do a software-based type check and fetch the page if
> + * we indeed found a conflicting mem_access setting.
> + */
> +struct page_info*
> +p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag,
> + const struct vcpu *v)
> +{
> + long rc;
> + paddr_t ipa;
> + gfn_t gfn;
> + mfn_t mfn;
> + xenmem_access_t xma;
> + p2m_type_t t;
> + struct page_info *page = NULL;
> + struct p2m_domain *p2m = &v->domain->arch.p2m;
> +
> + rc = gva_to_ipa(gva, &ipa, flag);
> + if ( rc < 0 )
> + goto err;
> +
> + gfn = _gfn(paddr_to_pfn(ipa));
> +
> + /*
> + * We do this first as this is faster in the default case when no
> + * permission is set on the page.
> + */
> + rc = __p2m_get_mem_access(v->domain, gfn, &xma);
> + if ( rc < 0 )
> + goto err;
> +
> + /* Let's check if mem_access limited the access. */
> + switch ( xma )
> + {
> + default:
> + case XENMEM_access_rwx:
> + case XENMEM_access_rw:
> + /*
> + * If mem_access contains no rw perm restrictions at all then the original
> + * fault was correct.
> + */
> + goto err;
> + case XENMEM_access_n2rwx:
> + case XENMEM_access_n:
> + case XENMEM_access_x:
> + /*
> + * If no r/w is permitted by mem_access, this was a fault caused by mem_access.
> + */
> + break;
> + case XENMEM_access_wx:
> + case XENMEM_access_w:
> + /*
> + * If this was a read then it was because of mem_access, but if it was
> + * a write then the original get_page_from_gva fault was correct.
> + */
> + if ( flag == GV2M_READ )
> + break;
> + else
> + goto err;
> + case XENMEM_access_rx2rw:
> + case XENMEM_access_rx:
> + case XENMEM_access_r:
> + /*
> + * If this was a write then it was because of mem_access, but if it was
> + * a read then the original get_page_from_gva fault was correct.
> + */
> + if ( flag == GV2M_WRITE )
> + break;
> + else
> + goto err;
> + }
> +
> + /*
> + * We had a mem_access permission limiting the access, but the page type
> + * could also be limiting, so we need to check that as well.
> + */
> + mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL);
> + if ( mfn_eq(mfn, INVALID_MFN) )
> + goto err;
> +
> + if ( !mfn_valid(mfn_x(mfn)) )
> + goto err;
> +
> + /*
> + * Base type doesn't allow r/w
> + */
> + if ( t != p2m_ram_rw )
> + goto err;
> +
> + page = mfn_to_page(mfn_x(mfn));
> +
> + if ( unlikely(!get_page(page, v->domain)) )
> + page = NULL;
> +
> +err:
> + return page;
> +}
> +
> +bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec)
> +{
> + int rc;
> + bool_t violation;
> + xenmem_access_t xma;
> + vm_event_request_t *req;
> + struct vcpu *v = current;
> + struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
> +
> + /* Mem_access is not in use. */
> + if ( !p2m->mem_access_enabled )
> + return true;
> +
> + rc = p2m_get_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), &xma);
> + if ( rc )
> + return true;
> +
> + /* Now check for mem_access violation. */
> + switch ( xma )
> + {
> + case XENMEM_access_rwx:
> + violation = false;
> + break;
> + case XENMEM_access_rw:
> + violation = npfec.insn_fetch;
> + break;
> + case XENMEM_access_wx:
> + violation = npfec.read_access;
> + break;
> + case XENMEM_access_rx:
> + case XENMEM_access_rx2rw:
> + violation = npfec.write_access;
> + break;
> + case XENMEM_access_x:
> + violation = npfec.read_access || npfec.write_access;
> + break;
> + case XENMEM_access_w:
> + violation = npfec.read_access || npfec.insn_fetch;
> + break;
> + case XENMEM_access_r:
> + violation = npfec.write_access || npfec.insn_fetch;
> + break;
> + default:
> + case XENMEM_access_n:
> + case XENMEM_access_n2rwx:
> + violation = true;
> + break;
> + }
> +
> + if ( !violation )
> + return true;
> +
> + /* First, handle rx2rw and n2rwx conversion automatically. */
> + if ( npfec.write_access && xma == XENMEM_access_rx2rw )
> + {
> + rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> + 0, ~0, XENMEM_access_rw, 0);
> + return false;
> + }
> + else if ( xma == XENMEM_access_n2rwx )
> + {
> + rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> + 0, ~0, XENMEM_access_rwx, 0);
> + }
> +
> + /* Otherwise, check if there is a vm_event monitor subscriber */
> + if ( !vm_event_check_ring(&v->domain->vm_event->monitor) )
> + {
> + /* No listener */
> + if ( p2m->access_required )
> + {
> + gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> + "no vm_event listener VCPU %d, dom %d\n",
> + v->vcpu_id, v->domain->domain_id);
> + domain_crash(v->domain);
> + }
> + else
> + {
> + /* n2rwx was already handled */
> + if ( xma != XENMEM_access_n2rwx )
> + {
> + /* A listener is not required, so clear the access
> + * restrictions. */
> + rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> + 0, ~0, XENMEM_access_rwx, 0);
> + }
> + }
> +
> + /* No need to reinject */
> + return false;
> + }
> +
> + req = xzalloc(vm_event_request_t);
> + if ( req )
> + {
> + req->reason = VM_EVENT_REASON_MEM_ACCESS;
> +
> + /* Send request to mem access subscriber */
> + req->u.mem_access.gfn = gpa >> PAGE_SHIFT;
> + req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> + if ( npfec.gla_valid )
> + {
> + req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> + req->u.mem_access.gla = gla;
> +
> + if ( npfec.kind == npfec_kind_with_gla )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> + else if ( npfec.kind == npfec_kind_in_gpt )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> + }
> + req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> + req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> + req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> +
> + if ( monitor_traps(v, (xma != XENMEM_access_n2rwx), req) < 0 )
> + domain_crash(v->domain);
> +
> + xfree(req);
> + }
> +
> + return false;
> +}
> +
> +/*
> + * Set access type for a region of pfns.
> + * If gfn == INVALID_GFN, sets the default access type.
> + */
> +long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + p2m_access_t a;
> + unsigned int order;
> + long rc = 0;
> +
> + static const p2m_access_t memaccess[] = {
> +#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + switch ( access )
> + {
> + case 0 ... ARRAY_SIZE(memaccess) - 1:
> + a = memaccess[access];
> + break;
> + case XENMEM_access_default:
> + a = p2m->default_access;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /*
> + * Flip mem_access_enabled to true when a permission is set, as to prevent
> + * allocating or inserting super-pages.
> + */
> + p2m->mem_access_enabled = true;
> +
> + /* If request to set default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + p2m->default_access = a;
> + return 0;
> + }
> +
> + p2m_write_lock(p2m);
> +
> + for ( gfn = gfn_add(gfn, start); nr > start;
> + gfn = gfn_next_boundary(gfn, order) )
> + {
> + p2m_type_t t;
> + mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order);
> +
> +
> + if ( !mfn_eq(mfn, INVALID_MFN) )
> + {
> + order = 0;
> + rc = p2m_set_entry(p2m, gfn, 1, mfn, t, a);
> + if ( rc )
> + break;
> + }
> +
> + start += gfn_x(gfn_next_boundary(gfn, order)) - gfn_x(gfn);
> + /* Check for continuation if it is not the last iteration */
> + if ( nr > start && !(start & mask) && hypercall_preempt_check() )
> + {
> + rc = start;
> + break;
> + }
> + }
> +
> + p2m_write_unlock(p2m);
> +
> + return rc;
> +}
> +
> +long p2m_set_mem_access_multi(struct domain *d,
> + const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> + const XEN_GUEST_HANDLE(const_uint8) access_list,
> + uint32_t nr, uint32_t start, uint32_t mask,
> + unsigned int altp2m_idx)
> +{
> + /* Not yet implemented on ARM. */
> + return -EOPNOTSUPP;
> +}
> +
> +int p2m_get_mem_access(struct domain *d, gfn_t gfn,
> + xenmem_access_t *access)
> +{
> + int ret;
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> +
> + p2m_read_lock(p2m);
> + ret = __p2m_get_mem_access(d, gfn, access);
> + p2m_read_unlock(p2m);
> +
> + return ret;
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 837be1d..4e7ce3d 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -7,6 +7,7 @@
> #include <xen/vm_event.h>
> #include <xen/monitor.h>
> #include <xen/iocap.h>
> +#include <xen/mem_access.h>
> #include <public/vm_event.h>
> #include <asm/flushtlb.h>
> #include <asm/gic.h>
> @@ -58,22 +59,6 @@ static inline bool p2m_is_superpage(lpae_t pte, unsigned int level)
> return (level < 3) && p2m_mapping(pte);
> }
>
> -/*
> - * Return the start of the next mapping based on the order of the
> - * current one.
> - */
> -static inline gfn_t gfn_next_boundary(gfn_t gfn, unsigned int order)
> -{
> - /*
> - * The order corresponds to the order of the mapping (or invalid
> - * range) in the page table. So we need to align the GFN before
> - * incrementing.
> - */
> - gfn = _gfn(gfn_x(gfn) & ~((1UL << order) - 1));
> -
> - return gfn_add(gfn, 1UL << order);
> -}
> -
> static void p2m_flush_tlb(struct p2m_domain *p2m);
>
> /* Unlock the flush and do a P2M TLB flush if necessary */
> @@ -602,73 +587,6 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry)
> return 0;
> }
>
> -static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
> - xenmem_access_t *access)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> - void *i;
> - unsigned int index;
> -
> - static const xenmem_access_t memaccess[] = {
> -#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - ASSERT(p2m_is_locked(p2m));
> -
> - /* If no setting was ever set, just return rwx. */
> - if ( !p2m->mem_access_enabled )
> - {
> - *access = XENMEM_access_rwx;
> - return 0;
> - }
> -
> - /* If request to get default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - *access = memaccess[p2m->default_access];
> - return 0;
> - }
> -
> - i = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> -
> - if ( !i )
> - {
> - /*
> - * No setting was found in the Radix tree. Check if the
> - * entry exists in the page-tables.
> - */
> - mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
> -
> - if ( mfn_eq(mfn, INVALID_MFN) )
> - return -ESRCH;
> -
> - /* If entry exists then its rwx. */
> - *access = XENMEM_access_rwx;
> - }
> - else
> - {
> - /* Setting was found in the Radix tree. */
> - index = radix_tree_ptr_to_int(i);
> - if ( index >= ARRAY_SIZE(memaccess) )
> - return -ERANGE;
> -
> - *access = memaccess[index];
> - }
> -
> - return 0;
> -}
> -
> static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
> p2m_access_t a)
> {
> @@ -1454,106 +1372,6 @@ mfn_t gfn_to_mfn(struct domain *d, gfn_t gfn)
> return p2m_lookup(d, gfn, NULL);
> }
>
> -/*
> - * If mem_access is in use it might have been the reason why get_page_from_gva
> - * failed to fetch the page, as it uses the MMU for the permission checking.
> - * Only in these cases we do a software-based type check and fetch the page if
> - * we indeed found a conflicting mem_access setting.
> - */
> -static struct page_info*
> -p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag,
> - const struct vcpu *v)
> -{
> - long rc;
> - paddr_t ipa;
> - gfn_t gfn;
> - mfn_t mfn;
> - xenmem_access_t xma;
> - p2m_type_t t;
> - struct page_info *page = NULL;
> - struct p2m_domain *p2m = &v->domain->arch.p2m;
> -
> - rc = gva_to_ipa(gva, &ipa, flag);
> - if ( rc < 0 )
> - goto err;
> -
> - gfn = _gfn(paddr_to_pfn(ipa));
> -
> - /*
> - * We do this first as this is faster in the default case when no
> - * permission is set on the page.
> - */
> - rc = __p2m_get_mem_access(v->domain, gfn, &xma);
> - if ( rc < 0 )
> - goto err;
> -
> - /* Let's check if mem_access limited the access. */
> - switch ( xma )
> - {
> - default:
> - case XENMEM_access_rwx:
> - case XENMEM_access_rw:
> - /*
> - * If mem_access contains no rw perm restrictions at all then the original
> - * fault was correct.
> - */
> - goto err;
> - case XENMEM_access_n2rwx:
> - case XENMEM_access_n:
> - case XENMEM_access_x:
> - /*
> - * If no r/w is permitted by mem_access, this was a fault caused by mem_access.
> - */
> - break;
> - case XENMEM_access_wx:
> - case XENMEM_access_w:
> - /*
> - * If this was a read then it was because of mem_access, but if it was
> - * a write then the original get_page_from_gva fault was correct.
> - */
> - if ( flag == GV2M_READ )
> - break;
> - else
> - goto err;
> - case XENMEM_access_rx2rw:
> - case XENMEM_access_rx:
> - case XENMEM_access_r:
> - /*
> - * If this was a write then it was because of mem_access, but if it was
> - * a read then the original get_page_from_gva fault was correct.
> - */
> - if ( flag == GV2M_WRITE )
> - break;
> - else
> - goto err;
> - }
> -
> - /*
> - * We had a mem_access permission limiting the access, but the page type
> - * could also be limiting, so we need to check that as well.
> - */
> - mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL);
> - if ( mfn_eq(mfn, INVALID_MFN) )
> - goto err;
> -
> - if ( !mfn_valid(mfn_x(mfn)) )
> - goto err;
> -
> - /*
> - * Base type doesn't allow r/w
> - */
> - if ( t != p2m_ram_rw )
> - goto err;
> -
> - page = mfn_to_page(mfn_x(mfn));
> -
> - if ( unlikely(!get_page(page, v->domain)) )
> - page = NULL;
> -
> -err:
> - return page;
> -}
> -
> struct page_info *get_page_from_gva(struct vcpu *v, vaddr_t va,
> unsigned long flags)
> {
> @@ -1666,236 +1484,6 @@ void __init setup_virt_paging(void)
> smp_call_function(setup_virt_paging_one, (void *)val, 1);
> }
>
> -bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec)
> -{
> - int rc;
> - bool_t violation;
> - xenmem_access_t xma;
> - vm_event_request_t *req;
> - struct vcpu *v = current;
> - struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
> -
> - /* Mem_access is not in use. */
> - if ( !p2m->mem_access_enabled )
> - return true;
> -
> - rc = p2m_get_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), &xma);
> - if ( rc )
> - return true;
> -
> - /* Now check for mem_access violation. */
> - switch ( xma )
> - {
> - case XENMEM_access_rwx:
> - violation = false;
> - break;
> - case XENMEM_access_rw:
> - violation = npfec.insn_fetch;
> - break;
> - case XENMEM_access_wx:
> - violation = npfec.read_access;
> - break;
> - case XENMEM_access_rx:
> - case XENMEM_access_rx2rw:
> - violation = npfec.write_access;
> - break;
> - case XENMEM_access_x:
> - violation = npfec.read_access || npfec.write_access;
> - break;
> - case XENMEM_access_w:
> - violation = npfec.read_access || npfec.insn_fetch;
> - break;
> - case XENMEM_access_r:
> - violation = npfec.write_access || npfec.insn_fetch;
> - break;
> - default:
> - case XENMEM_access_n:
> - case XENMEM_access_n2rwx:
> - violation = true;
> - break;
> - }
> -
> - if ( !violation )
> - return true;
> -
> - /* First, handle rx2rw and n2rwx conversion automatically. */
> - if ( npfec.write_access && xma == XENMEM_access_rx2rw )
> - {
> - rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> - 0, ~0, XENMEM_access_rw, 0);
> - return false;
> - }
> - else if ( xma == XENMEM_access_n2rwx )
> - {
> - rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> - 0, ~0, XENMEM_access_rwx, 0);
> - }
> -
> - /* Otherwise, check if there is a vm_event monitor subscriber */
> - if ( !vm_event_check_ring(&v->domain->vm_event->monitor) )
> - {
> - /* No listener */
> - if ( p2m->access_required )
> - {
> - gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> - "no vm_event listener VCPU %d, dom %d\n",
> - v->vcpu_id, v->domain->domain_id);
> - domain_crash(v->domain);
> - }
> - else
> - {
> - /* n2rwx was already handled */
> - if ( xma != XENMEM_access_n2rwx )
> - {
> - /* A listener is not required, so clear the access
> - * restrictions. */
> - rc = p2m_set_mem_access(v->domain, _gfn(paddr_to_pfn(gpa)), 1,
> - 0, ~0, XENMEM_access_rwx, 0);
> - }
> - }
> -
> - /* No need to reinject */
> - return false;
> - }
> -
> - req = xzalloc(vm_event_request_t);
> - if ( req )
> - {
> - req->reason = VM_EVENT_REASON_MEM_ACCESS;
> -
> - /* Send request to mem access subscriber */
> - req->u.mem_access.gfn = gpa >> PAGE_SHIFT;
> - req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> - if ( npfec.gla_valid )
> - {
> - req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> - req->u.mem_access.gla = gla;
> -
> - if ( npfec.kind == npfec_kind_with_gla )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> - else if ( npfec.kind == npfec_kind_in_gpt )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> - }
> - req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> - req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> - req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> -
> - if ( monitor_traps(v, (xma != XENMEM_access_n2rwx), req) < 0 )
> - domain_crash(v->domain);
> -
> - xfree(req);
> - }
> -
> - return false;
> -}
> -
> -/*
> - * Set access type for a region of pfns.
> - * If gfn == INVALID_GFN, sets the default access type.
> - */
> -long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> - uint32_t start, uint32_t mask, xenmem_access_t access,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> - p2m_access_t a;
> - unsigned int order;
> - long rc = 0;
> -
> - static const p2m_access_t memaccess[] = {
> -#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - switch ( access )
> - {
> - case 0 ... ARRAY_SIZE(memaccess) - 1:
> - a = memaccess[access];
> - break;
> - case XENMEM_access_default:
> - a = p2m->default_access;
> - break;
> - default:
> - return -EINVAL;
> - }
> -
> - /*
> - * Flip mem_access_enabled to true when a permission is set, as to prevent
> - * allocating or inserting super-pages.
> - */
> - p2m->mem_access_enabled = true;
> -
> - /* If request to set default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - p2m->default_access = a;
> - return 0;
> - }
> -
> - p2m_write_lock(p2m);
> -
> - for ( gfn = gfn_add(gfn, start); nr > start;
> - gfn = gfn_next_boundary(gfn, order) )
> - {
> - p2m_type_t t;
> - mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order);
> -
> -
> - if ( !mfn_eq(mfn, INVALID_MFN) )
> - {
> - order = 0;
> - rc = __p2m_set_entry(p2m, gfn, 0, mfn, t, a);
> - if ( rc )
> - break;
> - }
> -
> - start += gfn_x(gfn_next_boundary(gfn, order)) - gfn_x(gfn);
> - /* Check for continuation if it is not the last iteration */
> - if ( nr > start && !(start & mask) && hypercall_preempt_check() )
> - {
> - rc = start;
> - break;
> - }
> - }
> -
> - p2m_write_unlock(p2m);
> -
> - return rc;
> -}
> -
> -long p2m_set_mem_access_multi(struct domain *d,
> - const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> - const XEN_GUEST_HANDLE(const_uint8) access_list,
> - uint32_t nr, uint32_t start, uint32_t mask,
> - unsigned int altp2m_idx)
> -{
> - /* Not yet implemented on ARM. */
> - return -EOPNOTSUPP;
> -}
> -
> -int p2m_get_mem_access(struct domain *d, gfn_t gfn,
> - xenmem_access_t *access)
> -{
> - int ret;
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> -
> - p2m_read_lock(p2m);
> - ret = __p2m_get_mem_access(d, gfn, access);
> - p2m_read_unlock(p2m);
> -
> - return ret;
> -}
> -
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> index 8ff73fe..f2ea083 100644
> --- a/xen/arch/arm/traps.c
> +++ b/xen/arch/arm/traps.c
> @@ -32,6 +32,7 @@
> #include <xen/domain_page.h>
> #include <xen/perfc.h>
> #include <xen/virtual_region.h>
> +#include <xen/mem_access.h>
> #include <public/sched.h>
> #include <public/xen.h>
> #include <asm/debugger.h>
> diff --git a/xen/arch/x86/mm/Makefile b/xen/arch/x86/mm/Makefile
> index 9804c3a..e977dd8 100644
> --- a/xen/arch/x86/mm/Makefile
> +++ b/xen/arch/x86/mm/Makefile
> @@ -9,6 +9,7 @@ obj-y += guest_walk_3.o
> obj-y += guest_walk_4.o
> obj-y += mem_paging.o
> obj-y += mem_sharing.o
> +obj-y += mem_access.o
>
> guest_walk_%.o: guest_walk.c Makefile
> $(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
> diff --git a/xen/arch/x86/mm/mem_access.c b/xen/arch/x86/mm/mem_access.c
> new file mode 100644
> index 0000000..34a994d
> --- /dev/null
> +++ b/xen/arch/x86/mm/mem_access.c
> @@ -0,0 +1,462 @@
> +/******************************************************************************
> + * arch/x86/mm/mem_access.c
> + *
> + * Parts of this code are Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp)
> + * Parts of this code are Copyright (c) 2007 by Advanced Micro Devices.
> + * Parts of this code are Copyright (c) 2006-2007 by XenSource Inc.
> + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman
> + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/guest_access.h> /* copy_from_guest() */
> +#include <xen/mem_access.h>
> +#include <xen/vm_event.h>
> +#include <xen/event.h>
> +#include <public/vm_event.h>
> +#include <asm/p2m.h>
> +#include <asm/altp2m.h>
> +#include <asm/vm_event.h>
> +
> +#include "mm-locks.h"
> +
> +bool p2m_mem_access_emulate_check(struct vcpu *v,
> + const vm_event_response_t *rsp)
> +{
> + xenmem_access_t access;
> + bool violation = 1;
> + const struct vm_event_mem_access *data = &rsp->u.mem_access;
> +
> + if ( p2m_get_mem_access(v->domain, _gfn(data->gfn), &access) == 0 )
> + {
> + switch ( access )
> + {
> + case XENMEM_access_n:
> + case XENMEM_access_n2rwx:
> + default:
> + violation = data->flags & MEM_ACCESS_RWX;
> + break;
> +
> + case XENMEM_access_r:
> + violation = data->flags & MEM_ACCESS_WX;
> + break;
> +
> + case XENMEM_access_w:
> + violation = data->flags & MEM_ACCESS_RX;
> + break;
> +
> + case XENMEM_access_x:
> + violation = data->flags & MEM_ACCESS_RW;
> + break;
> +
> + case XENMEM_access_rx:
> + case XENMEM_access_rx2rw:
> + violation = data->flags & MEM_ACCESS_W;
> + break;
> +
> + case XENMEM_access_wx:
> + violation = data->flags & MEM_ACCESS_R;
> + break;
> +
> + case XENMEM_access_rw:
> + violation = data->flags & MEM_ACCESS_X;
> + break;
> +
> + case XENMEM_access_rwx:
> + violation = 0;
> + break;
> + }
> + }
> +
> + return violation;
> +}
> +
> +bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> + struct npfec npfec,
> + vm_event_request_t **req_ptr)
> +{
> + struct vcpu *v = current;
> + unsigned long gfn = gpa >> PAGE_SHIFT;
> + struct domain *d = v->domain;
> + struct p2m_domain *p2m = NULL;
> + mfn_t mfn;
> + p2m_type_t p2mt;
> + p2m_access_t p2ma;
> + vm_event_request_t *req;
> + int rc;
> +
> + if ( altp2m_active(d) )
> + p2m = p2m_get_altp2m(v);
> + if ( !p2m )
> + p2m = p2m_get_hostp2m(d);
> +
> + /* First, handle rx2rw conversion automatically.
> + * These calls to p2m->set_entry() must succeed: we have the gfn
> + * locked and just did a successful get_entry(). */
> + gfn_lock(p2m, gfn, 0);
> + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> +
> + if ( npfec.write_access && p2ma == p2m_access_rx2rw )
> + {
> + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw, -1);
> + ASSERT(rc == 0);
> + gfn_unlock(p2m, gfn, 0);
> + return 1;
> + }
> + else if ( p2ma == p2m_access_n2rwx )
> + {
> + ASSERT(npfec.write_access || npfec.read_access || npfec.insn_fetch);
> + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> + p2mt, p2m_access_rwx, -1);
> + ASSERT(rc == 0);
> + }
> + gfn_unlock(p2m, gfn, 0);
> +
> + /* Otherwise, check if there is a memory event listener, and send the message along */
> + if ( !vm_event_check_ring(&d->vm_event->monitor) || !req_ptr )
> + {
> + /* No listener */
> + if ( p2m->access_required )
> + {
> + gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> + "no vm_event listener VCPU %d, dom %d\n",
> + v->vcpu_id, d->domain_id);
> + domain_crash(v->domain);
> + return 0;
> + }
> + else
> + {
> + gfn_lock(p2m, gfn, 0);
> + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> + if ( p2ma != p2m_access_n2rwx )
> + {
> + /* A listener is not required, so clear the access
> + * restrictions. This set must succeed: we have the
> + * gfn locked and just did a successful get_entry(). */
> + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> + p2mt, p2m_access_rwx, -1);
> + ASSERT(rc == 0);
> + }
> + gfn_unlock(p2m, gfn, 0);
> + return 1;
> + }
> + }
> +
> + *req_ptr = NULL;
> + req = xzalloc(vm_event_request_t);
> + if ( req )
> + {
> + *req_ptr = req;
> +
> + req->reason = VM_EVENT_REASON_MEM_ACCESS;
> + req->u.mem_access.gfn = gfn;
> + req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> + if ( npfec.gla_valid )
> + {
> + req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> + req->u.mem_access.gla = gla;
> +
> + if ( npfec.kind == npfec_kind_with_gla )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> + else if ( npfec.kind == npfec_kind_in_gpt )
> + req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> + }
> + req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> + req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> + req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> + }
> +
> + /* Return whether vCPU pause is required (aka. sync event) */
> + return (p2ma != p2m_access_n2rwx);
> +}
> +
> +int p2m_set_altp2m_mem_access(struct domain *d, struct p2m_domain *hp2m,
> + struct p2m_domain *ap2m, p2m_access_t a,
> + gfn_t gfn)
> +{
> + mfn_t mfn;
> + p2m_type_t t;
> + p2m_access_t old_a;
> + unsigned int page_order;
> + unsigned long gfn_l = gfn_x(gfn);
> + int rc;
> +
> + mfn = ap2m->get_entry(ap2m, gfn_l, &t, &old_a, 0, NULL, NULL);
> +
> + /* Check host p2m if no valid entry in alternate */
> + if ( !mfn_valid(mfn_x(mfn)) )
> + {
> +
> + mfn = __get_gfn_type_access(hp2m, gfn_l, &t, &old_a,
> + P2M_ALLOC | P2M_UNSHARE, &page_order, 0);
> +
> + rc = -ESRCH;
> + if ( !mfn_valid(mfn_x(mfn)) || t != p2m_ram_rw )
> + return rc;
> +
> + /* If this is a superpage, copy that first */
> + if ( page_order != PAGE_ORDER_4K )
> + {
> + unsigned long mask = ~((1UL << page_order) - 1);
> + unsigned long gfn2_l = gfn_l & mask;
> + mfn_t mfn2 = _mfn(mfn_x(mfn) & mask);
> +
> + rc = ap2m->set_entry(ap2m, gfn2_l, mfn2, page_order, t, old_a, 1);
> + if ( rc )
> + return rc;
> + }
> + }
> +
> + return ap2m->set_entry(ap2m, gfn_l, mfn, PAGE_ORDER_4K, t, a,
> + (current->domain != d));
> +}
> +
> +static int set_mem_access(struct domain *d, struct p2m_domain *p2m,
> + struct p2m_domain *ap2m, p2m_access_t a,
> + gfn_t gfn)
> +{
> + int rc = 0;
> +
> + if ( ap2m )
> + {
> + rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, gfn);
> + /* If the corresponding mfn is invalid we will want to just skip it */
> + if ( rc == -ESRCH )
> + rc = 0;
> + }
> + else
> + {
> + mfn_t mfn;
> + p2m_access_t _a;
> + p2m_type_t t;
> + unsigned long gfn_l = gfn_x(gfn);
> +
> + mfn = p2m->get_entry(p2m, gfn_l, &t, &_a, 0, NULL, NULL);
> + rc = p2m->set_entry(p2m, gfn_l, mfn, PAGE_ORDER_4K, t, a, -1);
> + }
> +
> + return rc;
> +}
> +
> +static bool xenmem_access_to_p2m_access(struct p2m_domain *p2m,
> + xenmem_access_t xaccess,
> + p2m_access_t *paccess)
> +{
> + static const p2m_access_t memaccess[] = {
> +#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + switch ( xaccess )
> + {
> + case 0 ... ARRAY_SIZE(memaccess) - 1:
> + *paccess = memaccess[xaccess];
> + break;
> + case XENMEM_access_default:
> + *paccess = p2m->default_access;
> + break;
> + default:
> + return false;
> + }
> +
> + return true;
> +}
> +
> +/*
> + * Set access type for a region of gfns.
> + * If gfn == INVALID_GFN, sets the default access type.
> + */
> +long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> + p2m_access_t a;
> + unsigned long gfn_l;
> + long rc = 0;
> +
> + /* altp2m view 0 is treated as the hostp2m */
> + if ( altp2m_idx )
> + {
> + if ( altp2m_idx >= MAX_ALTP2M ||
> + d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> + return -EINVAL;
> +
> + ap2m = d->arch.altp2m_p2m[altp2m_idx];
> + }
> +
> + if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> + return -EINVAL;
> +
> + /* If request to set default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + p2m->default_access = a;
> + return 0;
> + }
> +
> + p2m_lock(p2m);
> + if ( ap2m )
> + p2m_lock(ap2m);
> +
> + for ( gfn_l = gfn_x(gfn) + start; nr > start; ++gfn_l )
> + {
> + rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> +
> + if ( rc )
> + break;
> +
> + /* Check for continuation if it's not the last iteration. */
> + if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> + {
> + rc = start;
> + break;
> + }
> + }
> +
> + if ( ap2m )
> + p2m_unlock(ap2m);
> + p2m_unlock(p2m);
> +
> + return rc;
> +}
> +
> +long p2m_set_mem_access_multi(struct domain *d,
> + const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> + const XEN_GUEST_HANDLE(const_uint8) access_list,
> + uint32_t nr, uint32_t start, uint32_t mask,
> + unsigned int altp2m_idx)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> + long rc = 0;
> +
> + /* altp2m view 0 is treated as the hostp2m */
> + if ( altp2m_idx )
> + {
> + if ( altp2m_idx >= MAX_ALTP2M ||
> + d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> + return -EINVAL;
> +
> + ap2m = d->arch.altp2m_p2m[altp2m_idx];
> + }
> +
> + p2m_lock(p2m);
> + if ( ap2m )
> + p2m_lock(ap2m);
> +
> + while ( start < nr )
> + {
> + p2m_access_t a;
> + uint8_t access;
> + uint64_t gfn_l;
> +
> + if ( copy_from_guest_offset(&gfn_l, pfn_list, start, 1) ||
> + copy_from_guest_offset(&access, access_list, start, 1) )
> + {
> + rc = -EFAULT;
> + break;
> + }
> +
> + if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> + {
> + rc = -EINVAL;
> + break;
> + }
> +
> + rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> +
> + if ( rc )
> + break;
> +
> + /* Check for continuation if it's not the last iteration. */
> + if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> + {
> + rc = start;
> + break;
> + }
> + }
> +
> + if ( ap2m )
> + p2m_unlock(ap2m);
> + p2m_unlock(p2m);
> +
> + return rc;
> +}
> +
> +/*
> + * Get access type for a gfn.
> + * If gfn == INVALID_GFN, gets the default access type.
> + */
> +int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + p2m_type_t t;
> + p2m_access_t a;
> + mfn_t mfn;
> +
> + static const xenmem_access_t memaccess[] = {
> +#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + /* If request to get default access. */
> + if ( gfn_eq(gfn, INVALID_GFN) )
> + {
> + *access = memaccess[p2m->default_access];
> + return 0;
> + }
> +
> + gfn_lock(p2m, gfn, 0);
> + mfn = p2m->get_entry(p2m, gfn_x(gfn), &t, &a, 0, NULL, NULL);
> + gfn_unlock(p2m, gfn, 0);
> +
> + if ( mfn_eq(mfn, INVALID_MFN) )
> + return -ESRCH;
> +
> + if ( (unsigned) a >= ARRAY_SIZE(memaccess) )
> + return -ERANGE;
> +
> + *access = memaccess[a];
> + return 0;
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
> index 6a45185..6299d5a 100644
> --- a/xen/arch/x86/mm/p2m.c
> +++ b/xen/arch/x86/mm/p2m.c
> @@ -1589,433 +1589,12 @@ void p2m_mem_paging_resume(struct domain *d, vm_event_response_t *rsp)
> }
> }
>
> -bool p2m_mem_access_emulate_check(struct vcpu *v,
> - const vm_event_response_t *rsp)
> -{
> - xenmem_access_t access;
> - bool violation = 1;
> - const struct vm_event_mem_access *data = &rsp->u.mem_access;
> -
> - if ( p2m_get_mem_access(v->domain, _gfn(data->gfn), &access) == 0 )
> - {
> - switch ( access )
> - {
> - case XENMEM_access_n:
> - case XENMEM_access_n2rwx:
> - default:
> - violation = data->flags & MEM_ACCESS_RWX;
> - break;
> -
> - case XENMEM_access_r:
> - violation = data->flags & MEM_ACCESS_WX;
> - break;
> -
> - case XENMEM_access_w:
> - violation = data->flags & MEM_ACCESS_RX;
> - break;
> -
> - case XENMEM_access_x:
> - violation = data->flags & MEM_ACCESS_RW;
> - break;
> -
> - case XENMEM_access_rx:
> - case XENMEM_access_rx2rw:
> - violation = data->flags & MEM_ACCESS_W;
> - break;
> -
> - case XENMEM_access_wx:
> - violation = data->flags & MEM_ACCESS_R;
> - break;
> -
> - case XENMEM_access_rw:
> - violation = data->flags & MEM_ACCESS_X;
> - break;
> -
> - case XENMEM_access_rwx:
> - violation = 0;
> - break;
> - }
> - }
> -
> - return violation;
> -}
> -
> void p2m_altp2m_check(struct vcpu *v, uint16_t idx)
> {
> if ( altp2m_active(v->domain) )
> p2m_switch_vcpu_altp2m_by_id(v, idx);
> }
>
> -bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> - struct npfec npfec,
> - vm_event_request_t **req_ptr)
> -{
> - struct vcpu *v = current;
> - unsigned long gfn = gpa >> PAGE_SHIFT;
> - struct domain *d = v->domain;
> - struct p2m_domain *p2m = NULL;
> - mfn_t mfn;
> - p2m_type_t p2mt;
> - p2m_access_t p2ma;
> - vm_event_request_t *req;
> - int rc;
> -
> - if ( altp2m_active(d) )
> - p2m = p2m_get_altp2m(v);
> - if ( !p2m )
> - p2m = p2m_get_hostp2m(d);
> -
> - /* First, handle rx2rw conversion automatically.
> - * These calls to p2m->set_entry() must succeed: we have the gfn
> - * locked and just did a successful get_entry(). */
> - gfn_lock(p2m, gfn, 0);
> - mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> -
> - if ( npfec.write_access && p2ma == p2m_access_rx2rw )
> - {
> - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw, -1);
> - ASSERT(rc == 0);
> - gfn_unlock(p2m, gfn, 0);
> - return 1;
> - }
> - else if ( p2ma == p2m_access_n2rwx )
> - {
> - ASSERT(npfec.write_access || npfec.read_access || npfec.insn_fetch);
> - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> - p2mt, p2m_access_rwx, -1);
> - ASSERT(rc == 0);
> - }
> - gfn_unlock(p2m, gfn, 0);
> -
> - /* Otherwise, check if there is a memory event listener, and send the message along */
> - if ( !vm_event_check_ring(&d->vm_event->monitor) || !req_ptr )
> - {
> - /* No listener */
> - if ( p2m->access_required )
> - {
> - gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> - "no vm_event listener VCPU %d, dom %d\n",
> - v->vcpu_id, d->domain_id);
> - domain_crash(v->domain);
> - return 0;
> - }
> - else
> - {
> - gfn_lock(p2m, gfn, 0);
> - mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL, NULL);
> - if ( p2ma != p2m_access_n2rwx )
> - {
> - /* A listener is not required, so clear the access
> - * restrictions. This set must succeed: we have the
> - * gfn locked and just did a successful get_entry(). */
> - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
> - p2mt, p2m_access_rwx, -1);
> - ASSERT(rc == 0);
> - }
> - gfn_unlock(p2m, gfn, 0);
> - return 1;
> - }
> - }
> -
> - *req_ptr = NULL;
> - req = xzalloc(vm_event_request_t);
> - if ( req )
> - {
> - *req_ptr = req;
> -
> - req->reason = VM_EVENT_REASON_MEM_ACCESS;
> - req->u.mem_access.gfn = gfn;
> - req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1);
> - if ( npfec.gla_valid )
> - {
> - req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID;
> - req->u.mem_access.gla = gla;
> -
> - if ( npfec.kind == npfec_kind_with_gla )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA;
> - else if ( npfec.kind == npfec_kind_in_gpt )
> - req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT;
> - }
> - req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0;
> - req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0;
> - req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0;
> - }
> -
> - /* Return whether vCPU pause is required (aka. sync event) */
> - return (p2ma != p2m_access_n2rwx);
> -}
> -
> -static inline
> -int p2m_set_altp2m_mem_access(struct domain *d, struct p2m_domain *hp2m,
> - struct p2m_domain *ap2m, p2m_access_t a,
> - gfn_t gfn)
> -{
> - mfn_t mfn;
> - p2m_type_t t;
> - p2m_access_t old_a;
> - unsigned int page_order;
> - unsigned long gfn_l = gfn_x(gfn);
> - int rc;
> -
> - mfn = ap2m->get_entry(ap2m, gfn_l, &t, &old_a, 0, NULL, NULL);
> -
> - /* Check host p2m if no valid entry in alternate */
> - if ( !mfn_valid(mfn) )
> - {
> -
> - mfn = __get_gfn_type_access(hp2m, gfn_l, &t, &old_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 ( page_order != PAGE_ORDER_4K )
> - {
> - unsigned long mask = ~((1UL << page_order) - 1);
> - unsigned long gfn2_l = gfn_l & mask;
> - mfn_t mfn2 = _mfn(mfn_x(mfn) & mask);
> -
> - rc = ap2m->set_entry(ap2m, gfn2_l, mfn2, page_order, t, old_a, 1);
> - if ( rc )
> - return rc;
> - }
> - }
> -
> - return ap2m->set_entry(ap2m, gfn_l, mfn, PAGE_ORDER_4K, t, a,
> - (current->domain != d));
> -}
> -
> -static int set_mem_access(struct domain *d, struct p2m_domain *p2m,
> - struct p2m_domain *ap2m, p2m_access_t a,
> - gfn_t gfn)
> -{
> - int rc = 0;
> -
> - if ( ap2m )
> - {
> - rc = p2m_set_altp2m_mem_access(d, p2m, ap2m, a, gfn);
> - /* If the corresponding mfn is invalid we will want to just skip it */
> - if ( rc == -ESRCH )
> - rc = 0;
> - }
> - else
> - {
> - mfn_t mfn;
> - p2m_access_t _a;
> - p2m_type_t t;
> - unsigned long gfn_l = gfn_x(gfn);
> -
> - mfn = p2m->get_entry(p2m, gfn_l, &t, &_a, 0, NULL, NULL);
> - rc = p2m->set_entry(p2m, gfn_l, mfn, PAGE_ORDER_4K, t, a, -1);
> - }
> -
> - return rc;
> -}
> -
> -static bool xenmem_access_to_p2m_access(struct p2m_domain *p2m,
> - xenmem_access_t xaccess,
> - p2m_access_t *paccess)
> -{
> - static const p2m_access_t memaccess[] = {
> -#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - switch ( xaccess )
> - {
> - case 0 ... ARRAY_SIZE(memaccess) - 1:
> - *paccess = memaccess[xaccess];
> - break;
> - case XENMEM_access_default:
> - *paccess = p2m->default_access;
> - break;
> - default:
> - return false;
> - }
> -
> - return true;
> -}
> -
> -/*
> - * Set access type for a region of gfns.
> - * If gfn == INVALID_GFN, sets the default access type.
> - */
> -long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> - uint32_t start, uint32_t mask, xenmem_access_t access,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> - p2m_access_t a;
> - unsigned long gfn_l;
> - long rc = 0;
> -
> - /* altp2m view 0 is treated as the hostp2m */
> - if ( altp2m_idx )
> - {
> - if ( altp2m_idx >= MAX_ALTP2M ||
> - d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> - return -EINVAL;
> -
> - ap2m = d->arch.altp2m_p2m[altp2m_idx];
> - }
> -
> - if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> - return -EINVAL;
> -
> - /* If request to set default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - p2m->default_access = a;
> - return 0;
> - }
> -
> - p2m_lock(p2m);
> - if ( ap2m )
> - p2m_lock(ap2m);
> -
> - for ( gfn_l = gfn_x(gfn) + start; nr > start; ++gfn_l )
> - {
> - rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> -
> - if ( rc )
> - break;
> -
> - /* Check for continuation if it's not the last iteration. */
> - if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> - {
> - rc = start;
> - break;
> - }
> - }
> -
> - if ( ap2m )
> - p2m_unlock(ap2m);
> - p2m_unlock(p2m);
> -
> - return rc;
> -}
> -
> -long p2m_set_mem_access_multi(struct domain *d,
> - const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> - const XEN_GUEST_HANDLE(const_uint8) access_list,
> - uint32_t nr, uint32_t start, uint32_t mask,
> - unsigned int altp2m_idx)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d), *ap2m = NULL;
> - long rc = 0;
> -
> - /* altp2m view 0 is treated as the hostp2m */
> - if ( altp2m_idx )
> - {
> - if ( altp2m_idx >= MAX_ALTP2M ||
> - d->arch.altp2m_eptp[altp2m_idx] == mfn_x(INVALID_MFN) )
> - return -EINVAL;
> -
> - ap2m = d->arch.altp2m_p2m[altp2m_idx];
> - }
> -
> - p2m_lock(p2m);
> - if ( ap2m )
> - p2m_lock(ap2m);
> -
> - while ( start < nr )
> - {
> - p2m_access_t a;
> - uint8_t access;
> - uint64_t gfn_l;
> -
> - if ( copy_from_guest_offset(&gfn_l, pfn_list, start, 1) ||
> - copy_from_guest_offset(&access, access_list, start, 1) )
> - {
> - rc = -EFAULT;
> - break;
> - }
> -
> - if ( !xenmem_access_to_p2m_access(p2m, access, &a) )
> - {
> - rc = -EINVAL;
> - break;
> - }
> -
> - rc = set_mem_access(d, p2m, ap2m, a, _gfn(gfn_l));
> -
> - if ( rc )
> - break;
> -
> - /* Check for continuation if it's not the last iteration. */
> - if ( nr > ++start && !(start & mask) && hypercall_preempt_check() )
> - {
> - rc = start;
> - break;
> - }
> - }
> -
> - if ( ap2m )
> - p2m_unlock(ap2m);
> - p2m_unlock(p2m);
> -
> - return rc;
> -}
> -
> -/*
> - * Get access type for a gfn.
> - * If gfn == INVALID_GFN, gets the default access type.
> - */
> -int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access)
> -{
> - struct p2m_domain *p2m = p2m_get_hostp2m(d);
> - p2m_type_t t;
> - p2m_access_t a;
> - mfn_t mfn;
> -
> - static const xenmem_access_t memaccess[] = {
> -#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> - ACCESS(n),
> - ACCESS(r),
> - ACCESS(w),
> - ACCESS(rw),
> - ACCESS(x),
> - ACCESS(rx),
> - ACCESS(wx),
> - ACCESS(rwx),
> - ACCESS(rx2rw),
> - ACCESS(n2rwx),
> -#undef ACCESS
> - };
> -
> - /* If request to get default access. */
> - if ( gfn_eq(gfn, INVALID_GFN) )
> - {
> - *access = memaccess[p2m->default_access];
> - return 0;
> - }
> -
> - gfn_lock(p2m, gfn, 0);
> - mfn = p2m->get_entry(p2m, gfn_x(gfn), &t, &a, 0, NULL, NULL);
> - gfn_unlock(p2m, gfn, 0);
> -
> - if ( mfn_eq(mfn, INVALID_MFN) )
> - return -ESRCH;
> -
> - if ( (unsigned) a >= ARRAY_SIZE(memaccess) )
> - return -ERANGE;
> -
> - *access = memaccess[a];
> - return 0;
> -}
> -
> static struct p2m_domain *
> p2m_getlru_nestedp2m(struct domain *d, struct p2m_domain *p2m)
> {
> diff --git a/xen/arch/x86/vm_event.c b/xen/arch/x86/vm_event.c
> index 1e88d67..8d8bc4a 100644
> --- a/xen/arch/x86/vm_event.c
> +++ b/xen/arch/x86/vm_event.c
> @@ -18,7 +18,8 @@
> * License along with this program; If not, see <http://www.gnu.org/licenses/>.
> */
>
> -#include <asm/p2m.h>
> +#include <xen/sched.h>
> +#include <xen/mem_access.h>
> #include <asm/vm_event.h>
>
> /* Implicitly serialized by the domctl lock. */
> diff --git a/xen/common/mem_access.c b/xen/common/mem_access.c
> index 565a320..19f63bb 100644
> --- a/xen/common/mem_access.c
> +++ b/xen/common/mem_access.c
> @@ -24,8 +24,8 @@
> #include <xen/guest_access.h>
> #include <xen/hypercall.h>
> #include <xen/vm_event.h>
> +#include <xen/mem_access.h>
> #include <public/memory.h>
> -#include <asm/p2m.h>
> #include <xsm/xsm.h>
>
> int mem_access_memop(unsigned long cmd,
> diff --git a/xen/include/asm-arm/mem_access.h b/xen/include/asm-arm/mem_access.h
> new file mode 100644
> index 0000000..3a155f8
> --- /dev/null
> +++ b/xen/include/asm-arm/mem_access.h
> @@ -0,0 +1,53 @@
> +/*
> + * mem_access.h: architecture specific mem_access handling routines
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef _ASM_ARM_MEM_ACCESS_H
> +#define _ASM_ARM_MEM_ACCESS_H
> +
> +static inline
> +bool p2m_mem_access_emulate_check(struct vcpu *v,
> + const vm_event_response_t *rsp)
> +{
> + /* Not supported on ARM. */
> + return 0;
> +}
> +
> +/* vm_event and mem_access are supported on any ARM guest */
> +static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> +{
> + return 1;
> +}
> +
> +/*
> + * Send mem event based on the access. Boolean return value indicates if trap
> + * needs to be injected into guest.
> + */
> +bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec);
> +
> +struct page_info*
> +p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag,
> + const struct vcpu *v);
> +
> +#endif /* _ASM_ARM_MEM_ACCESS_H */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> index fdb6b47..2b22e9a 100644
> --- a/xen/include/asm-arm/p2m.h
> +++ b/xen/include/asm-arm/p2m.h
> @@ -4,6 +4,7 @@
> #include <xen/mm.h>
> #include <xen/radix-tree.h>
> #include <xen/rwlock.h>
> +#include <xen/mem_access.h>
> #include <public/vm_event.h> /* for vm_event_response_t */
> #include <public/memory.h>
> #include <xen/p2m-common.h>
> @@ -139,14 +140,6 @@ typedef enum {
> p2m_to_mask(p2m_map_foreign)))
>
> static inline
> -bool p2m_mem_access_emulate_check(struct vcpu *v,
> - const vm_event_response_t *rsp)
> -{
> - /* Not supported on ARM. */
> - return 0;
> -}
> -
> -static inline
> void p2m_altp2m_check(struct vcpu *v, uint16_t idx)
> {
> /* Not supported on ARM. */
> @@ -343,22 +336,26 @@ static inline int get_page_and_type(struct page_info *page,
> /* get host p2m table */
> #define p2m_get_hostp2m(d) (&(d)->arch.p2m)
>
> -/* vm_event and mem_access are supported on any ARM guest */
> -static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> -{
> - return 1;
> -}
> -
> static inline bool_t p2m_vm_event_sanity_check(struct domain *d)
> {
> return 1;
> }
>
> /*
> - * Send mem event based on the access. Boolean return value indicates if trap
> - * needs to be injected into guest.
> + * Return the start of the next mapping based on the order of the
> + * current one.
> */
> -bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec);
> +static inline gfn_t gfn_next_boundary(gfn_t gfn, unsigned int order)
> +{
> + /*
> + * The order corresponds to the order of the mapping (or invalid
> + * range) in the page table. So we need to align the GFN before
> + * incrementing.
> + */
> + gfn = _gfn(gfn_x(gfn) & ~((1UL << order) - 1));
> +
> + return gfn_add(gfn, 1UL << order);
> +}
>
> #endif /* _XEN_P2M_H */
>
> diff --git a/xen/include/asm-x86/mem_access.h b/xen/include/asm-x86/mem_access.h
> new file mode 100644
> index 0000000..9f7b409
> --- /dev/null
> +++ b/xen/include/asm-x86/mem_access.h
> @@ -0,0 +1,61 @@
> +/******************************************************************************
> + * include/asm-x86/mem_access.h
> + *
> + * Memory access support.
> + *
> + * Copyright (c) 2011 GridCentric Inc. (Andres Lagar-Cavilla)
> + * Copyright (c) 2007 Advanced Micro Devices (Wei Huang)
> + * Parts of this code are Copyright (c) 2006-2007 by XenSource Inc.
> + * Parts of this code are Copyright (c) 2006 by Michael A Fetterman
> + * Parts based on earlier work by Michael A Fetterman, Ian Pratt et al.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ASM_X86_MEM_ACCESS_H__
> +#define __ASM_X86_MEM_ACCESS_H__
> +
> +/*
> + * Setup vm_event request based on the access (gla is -1ull if not available).
> + * Handles the rw2rx conversion. Boolean return value indicates if event type
> + * is syncronous (aka. requires vCPU pause). If the req_ptr has been populated,
> + * then the caller should use monitor_traps to send the event on the MONITOR
> + * ring. Once having released get_gfn* locks caller must also xfree the
> + * request.
> + */
> +bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> + struct npfec npfec,
> + vm_event_request_t **req_ptr);
> +
> +/* Check for emulation and mark vcpu for skipping one instruction
> + * upon rescheduling if required. */
> +bool p2m_mem_access_emulate_check(struct vcpu *v,
> + const vm_event_response_t *rsp);
> +
> +/* Sanity check for mem_access hardware support */
> +static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> +{
> + return is_hvm_domain(d) && cpu_has_vmx && hap_enabled(d);
> +}
> +
> +#endif /*__ASM_X86_MEM_ACCESS_H__ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h
> index 7035860..8964e90 100644
> --- a/xen/include/asm-x86/p2m.h
> +++ b/xen/include/asm-x86/p2m.h
> @@ -29,6 +29,7 @@
> #include <xen/config.h>
> #include <xen/paging.h>
> #include <xen/p2m-common.h>
> +#include <xen/mem_access.h>
> #include <asm/mem_sharing.h>
> #include <asm/page.h> /* for pagetable_t */
>
> @@ -663,29 +664,6 @@ int p2m_mem_paging_prep(struct domain *d, unsigned long gfn, uint64_t buffer);
> /* Resume normal operation (in case a domain was paused) */
> void p2m_mem_paging_resume(struct domain *d, vm_event_response_t *rsp);
>
> -/*
> - * Setup vm_event request based on the access (gla is -1ull if not available).
> - * Handles the rw2rx conversion. Boolean return value indicates if event type
> - * is syncronous (aka. requires vCPU pause). If the req_ptr has been populated,
> - * then the caller should use monitor_traps to send the event on the MONITOR
> - * ring. Once having released get_gfn* locks caller must also xfree the
> - * request.
> - */
> -bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla,
> - struct npfec npfec,
> - vm_event_request_t **req_ptr);
> -
> -/* Check for emulation and mark vcpu for skipping one instruction
> - * upon rescheduling if required. */
> -bool p2m_mem_access_emulate_check(struct vcpu *v,
> - const vm_event_response_t *rsp);
> -
> -/* Sanity check for mem_access hardware support */
> -static inline bool_t p2m_mem_access_sanity_check(struct domain *d)
> -{
> - return is_hvm_domain(d) && cpu_has_vmx && hap_enabled(d);
> -}
> -
> /*
> * Internal functions, only called by other p2m code
> */
> diff --git a/xen/include/xen/mem_access.h b/xen/include/xen/mem_access.h
> index da36e07..5ab34c1 100644
> --- a/xen/include/xen/mem_access.h
> +++ b/xen/include/xen/mem_access.h
> @@ -19,29 +19,78 @@
> * along with this program; If not, see <http://www.gnu.org/licenses/>.
> */
>
> -#ifndef _XEN_ASM_MEM_ACCESS_H
> -#define _XEN_ASM_MEM_ACCESS_H
> +#ifndef _XEN_MEM_ACCESS_H
> +#define _XEN_MEM_ACCESS_H
>
> +#include <xen/types.h>
> +#include <xen/mm.h>
> #include <public/memory.h>
> -#include <asm/p2m.h>
> +#include <public/vm_event.h>
> +#include <asm/mem_access.h>
>
> -#ifdef CONFIG_HAS_MEM_ACCESS
> +/*
> + * Additional access types, which are used to further restrict
> + * the permissions given my the p2m_type_t memory type. Violations
> + * caused by p2m_access_t restrictions are sent to the vm_event
> + * interface.
> + *
> + * The access permissions are soft state: when any ambiguous change of page
> + * type or use occurs, or when pages are flushed, swapped, or at any other
> + * convenient type, the access permissions can get reset to the p2m_domain
> + * default.
> + */
> +typedef enum {
> + /* Code uses bottom three bits with bitmask semantics */
> + p2m_access_n = 0, /* No access allowed. */
> + p2m_access_r = 1 << 0,
> + p2m_access_w = 1 << 1,
> + p2m_access_x = 1 << 2,
> + p2m_access_rw = p2m_access_r | p2m_access_w,
> + p2m_access_rx = p2m_access_r | p2m_access_x,
> + p2m_access_wx = p2m_access_w | p2m_access_x,
> + p2m_access_rwx = p2m_access_r | p2m_access_w | p2m_access_x,
> +
> + p2m_access_rx2rw = 8, /* Special: page goes from RX to RW on write */
> + p2m_access_n2rwx = 9, /* Special: page goes from N to RWX on access, *
> + * generates an event but does not pause the
> + * vcpu */
> +
> + /* NOTE: Assumed to be only 4 bits right now on x86. */
> +} p2m_access_t;
> +
> +/*
> + * Set access type for a region of gfns.
> + * If gfn == INVALID_GFN, sets the default access type.
> + */
> +long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access,
> + unsigned int altp2m_idx);
>
> +long p2m_set_mem_access_multi(struct domain *d,
> + const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> + const XEN_GUEST_HANDLE(const_uint8) access_list,
> + uint32_t nr, uint32_t start, uint32_t mask,
> + unsigned int altp2m_idx);
> +
> +/*
> + * Get access type for a gfn.
> + * If gfn == INVALID_GFN, gets the default access type.
> + */
> +int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access);
> +
> +#ifdef CONFIG_HAS_MEM_ACCESS
> int mem_access_memop(unsigned long cmd,
> XEN_GUEST_HANDLE_PARAM(xen_mem_access_op_t) arg);
> -
> #else
> -
> static inline
> int mem_access_memop(unsigned long cmd,
> XEN_GUEST_HANDLE_PARAM(xen_mem_access_op_t) arg)
> {
> return -ENOSYS;
> }
> +#endif /* CONFIG_HAS_MEM_ACCESS */
>
> -#endif /* HAS_MEM_ACCESS */
> -
> -#endif /* _XEN_ASM_MEM_ACCESS_H */
> +#endif /* _XEN_MEM_ACCESS_H */
>
> /*
> * Local variables:
> diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
> index 3be1e91..8cd5a6b 100644
> --- a/xen/include/xen/p2m-common.h
> +++ b/xen/include/xen/p2m-common.h
> @@ -1,38 +1,6 @@
> #ifndef _XEN_P2M_COMMON_H
> #define _XEN_P2M_COMMON_H
>
> -#include <public/vm_event.h>
> -
> -/*
> - * Additional access types, which are used to further restrict
> - * the permissions given my the p2m_type_t memory type. Violations
> - * caused by p2m_access_t restrictions are sent to the vm_event
> - * interface.
> - *
> - * The access permissions are soft state: when any ambiguous change of page
> - * type or use occurs, or when pages are flushed, swapped, or at any other
> - * convenient type, the access permissions can get reset to the p2m_domain
> - * default.
> - */
> -typedef enum {
> - /* Code uses bottom three bits with bitmask semantics */
> - p2m_access_n = 0, /* No access allowed. */
> - p2m_access_r = 1 << 0,
> - p2m_access_w = 1 << 1,
> - p2m_access_x = 1 << 2,
> - p2m_access_rw = p2m_access_r | p2m_access_w,
> - p2m_access_rx = p2m_access_r | p2m_access_x,
> - p2m_access_wx = p2m_access_w | p2m_access_x,
> - p2m_access_rwx = p2m_access_r | p2m_access_w | p2m_access_x,
> -
> - p2m_access_rx2rw = 8, /* Special: page goes from RX to RW on write */
> - p2m_access_n2rwx = 9, /* Special: page goes from N to RWX on access, *
> - * generates an event but does not pause the
> - * vcpu */
> -
> - /* NOTE: Assumed to be only 4 bits right now on x86. */
> -} p2m_access_t;
> -
> /* Map MMIO regions in the p2m: start_gfn and nr describe the range in
> * * the guest physical address space to map, starting from the machine
> * * frame number mfn. */
> @@ -45,24 +13,4 @@ int unmap_mmio_regions(struct domain *d,
> unsigned long nr,
> mfn_t mfn);
>
> -/*
> - * Set access type for a region of gfns.
> - * If gfn == INVALID_GFN, sets the default access type.
> - */
> -long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
> - uint32_t start, uint32_t mask, xenmem_access_t access,
> - unsigned int altp2m_idx);
> -
> -long p2m_set_mem_access_multi(struct domain *d,
> - const XEN_GUEST_HANDLE(const_uint64) pfn_list,
> - const XEN_GUEST_HANDLE(const_uint8) access_list,
> - uint32_t nr, uint32_t start, uint32_t mask,
> - unsigned int altp2m_idx);
> -
> -/*
> - * Get access type for a gfn.
> - * If gfn == INVALID_GFN, gets the default access type.
> - */
> -int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access);
> -
> #endif /* _XEN_P2M_COMMON_H */
> --
> 2.10.2
>
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
^ permalink raw reply [flat|nested] 24+ messages in thread