Message ID | 20161209195925.21420-2-tamas.lengyel@zentific.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 >
On Tue, Jan 03, 2017 at 08:31:25AM -0700, Tamas K Lengyel wrote: > 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> Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
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 >
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 */