[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen master] p2m: split mem_access into separate files
commit a43a9ce34b0f8b3006101c34c2de39d9a3c8c686 Author: Tamas K Lengyel <tamas.lengyel@xxxxxxxxxxxx> AuthorDate: Fri Dec 9 12:59:25 2016 -0700 Commit: Stefano Stabellini <sstabellini@xxxxxxxxxx> CommitDate: Mon Jan 30 13:08:44 2017 -0800 p2m: split mem_access into separate files 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@xxxxxxxxxxxx> Acked-by: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx> Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx> Acked-by: George Dunlap <george.dunlap@xxxxxxxxxx> Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx> --- 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(-) 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 cf67bbe..7afb8a3 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 7299792..980d991 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 <xen/xmalloc.h> #include <public/vm_event.h> #include <asm/flushtlb.h> @@ -69,22 +70,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 */ @@ -611,73 +596,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) { @@ -1472,106 +1390,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) { @@ -1704,236 +1522,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 35d8e8b..92b1d80 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 aa627d8..73d93ee 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 52b3159..a7ce6ca 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 9de55fc..5ddad34 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. */ @@ -340,22 +333,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 0e72880..f720f83 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 */ -- generated by git-patchbot for /home/xen/git/xen.git#master _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxx https://lists.xenproject.org/xen-changelog
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |