[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH for-4.5 v7 07/21] xen: Relocate p2m_mem_access_check into common and refactor it.
The p2m_mem_access_check was used only to automatically promote the special-case mem_access rights and to allocate the mem_access message. In this patch we abstract the violation check into a common mem_access_check function and change its return value to int. Return with rc < 0 means the fault wasn't triggered by mem_access permissions. Return with rc == 0 it was but rights were not automatically promoted. Return with rc > 0 means mem_access rights had been automatically promoted. As part of this patch, we relocate the x86/mm/mm-locks.h header into asm as to be able to create a generic inlined p2m_gpfn_lock/unlock wrapper that each arch can define based on its locking scheme. Signed-off-by: Tamas K Lengyel <tklengyel@xxxxxxxxxxxxx> --- xen/arch/x86/hvm/hvm.c | 59 ++------ xen/arch/x86/mm/mem_sharing.c | 2 +- xen/arch/x86/mm/mm-locks.h | 299 ----------------------------------------- xen/arch/x86/mm/p2m-ept.c | 9 +- xen/arch/x86/mm/p2m-pod.c | 2 +- xen/arch/x86/mm/p2m-pt.c | 2 +- xen/arch/x86/mm/p2m.c | 102 +------------- xen/arch/x86/mm/paging.c | 4 +- xen/common/mem_access.c | 143 +++++++++++++++++++- xen/include/asm-x86/mm-locks.h | 299 +++++++++++++++++++++++++++++++++++++++++ xen/include/asm-x86/p2m.h | 12 ++ xen/include/asm-x86/page.h | 4 - xen/include/xen/mem_access.h | 17 ++- xen/include/xen/mm.h | 4 + 14 files changed, 492 insertions(+), 466 deletions(-) delete mode 100644 xen/arch/x86/mm/mm-locks.h create mode 100644 xen/include/asm-x86/mm-locks.h diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 7649d36..aab397a 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -2677,7 +2677,6 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, struct p2m_domain *p2m; int rc, fall_through = 0, paged = 0; int sharing_enomem = 0; - mem_event_request_t *req_ptr = NULL; /* On Nested Virtualization, walk the guest page table. * If this succeeds, all is fine. @@ -2745,50 +2744,21 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, /* Check access permissions first, then handle faults */ if ( mfn_x(mfn) != INVALID_MFN ) { - bool_t violation; - - /* If the access is against the permissions, then send to mem_event */ - switch (p2ma) + rc = mem_access_check(gpa, gla, npfec); + if ( rc < 0 ) { - case p2m_access_n: - case p2m_access_n2rwx: - default: - violation = npfec.read_access || npfec.write_access || npfec.insn_fetch; - break; - case p2m_access_r: - violation = npfec.write_access || npfec.insn_fetch; - break; - case p2m_access_w: - violation = npfec.read_access || npfec.insn_fetch; - break; - case p2m_access_x: - violation = npfec.read_access || npfec.write_access; - break; - case p2m_access_rx: - case p2m_access_rx2rw: - violation = npfec.write_access; - break; - case p2m_access_wx: - violation = npfec.read_access; - break; - case p2m_access_rw: - violation = npfec.insn_fetch; - break; - case p2m_access_rwx: - violation = 0; - break; + /* Fault wasn't triggered by mem_access. */ } - - if ( violation ) + else if ( rc > 0 ) { - if ( p2m_mem_access_check(gpa, gla, npfec, &req_ptr) ) - { - fall_through = 1; - } else { - /* Rights not promoted, vcpu paused, work here is done */ - rc = 1; - goto out_put_gfn; - } + /* Fault was triggered by mem_access and settings got promoted. */ + fall_through = 1; + } + else + { + /* Rights not promoted, vcpu paused, work here is done */ + rc = 1; + goto out_put_gfn; } } @@ -2877,11 +2847,6 @@ out: rc = 0; } } - if ( req_ptr ) - { - mem_access_send_req(v->domain, req_ptr); - xfree(req_ptr); - } return rc; } diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c index 7c0fc7d..bcfcf6a 100644 --- a/xen/arch/x86/mm/mem_sharing.c +++ b/xen/arch/x86/mm/mem_sharing.c @@ -34,9 +34,9 @@ #include <asm/p2m.h> #include <asm/atomic.h> #include <asm/event.h> +#include <asm/mm-locks.h> #include <xsm/xsm.h> -#include "mm-locks.h" static shr_handle_t next_handle = 1; diff --git a/xen/arch/x86/mm/mm-locks.h b/xen/arch/x86/mm/mm-locks.h deleted file mode 100644 index 769f7bc..0000000 --- a/xen/arch/x86/mm/mm-locks.h +++ /dev/null @@ -1,299 +0,0 @@ -/****************************************************************************** - * arch/x86/mm/mm-locks.h - * - * Spinlocks used by the code in arch/x86/mm. - * - * Copyright (c) 2011 Citrix Systems, inc. - * Copyright (c) 2007 Advanced Micro Devices (Wei Huang) - * Copyright (c) 2006-2007 XenSource Inc. - * Copyright (c) 2006 Michael A Fetterman - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _MM_LOCKS_H -#define _MM_LOCKS_H - -#include <asm/mem_sharing.h> - -/* Per-CPU variable for enforcing the lock ordering */ -DECLARE_PER_CPU(int, mm_lock_level); -#define __get_lock_level() (this_cpu(mm_lock_level)) - -static inline void mm_lock_init(mm_lock_t *l) -{ - spin_lock_init(&l->lock); - l->locker = -1; - l->locker_function = "nobody"; - l->unlock_level = 0; -} - -static inline int mm_locked_by_me(mm_lock_t *l) -{ - return (l->lock.recurse_cpu == current->processor); -} - -/* If you see this crash, the numbers printed are lines in this file - * where the offending locks are declared. */ -#define __check_lock_level(l) \ -do { \ - if ( unlikely(__get_lock_level() > (l)) ) \ - { \ - printk("mm locking order violation: %i > %i\n", \ - __get_lock_level(), (l)); \ - BUG(); \ - } \ -} while(0) - -#define __set_lock_level(l) \ -do { \ - __get_lock_level() = (l); \ -} while(0) - -static inline void _mm_lock(mm_lock_t *l, const char *func, int level, int rec) -{ - if ( !((mm_locked_by_me(l)) && rec) ) - __check_lock_level(level); - spin_lock_recursive(&l->lock); - if ( l->lock.recurse_cnt == 1 ) - { - l->locker_function = func; - l->unlock_level = __get_lock_level(); - } - else if ( (unlikely(!rec)) ) - panic("mm lock already held by %s", l->locker_function); - __set_lock_level(level); -} - -static inline void _mm_enforce_order_lock_pre(int level) -{ - __check_lock_level(level); -} - -static inline void _mm_enforce_order_lock_post(int level, int *unlock_level, - unsigned short *recurse_count) -{ - if ( recurse_count ) - { - if ( (*recurse_count)++ == 0 ) - { - *unlock_level = __get_lock_level(); - } - } else { - *unlock_level = __get_lock_level(); - } - __set_lock_level(level); -} - - -static inline void mm_rwlock_init(mm_rwlock_t *l) -{ - rwlock_init(&l->lock); - l->locker = -1; - l->locker_function = "nobody"; - l->unlock_level = 0; -} - -static inline int mm_write_locked_by_me(mm_rwlock_t *l) -{ - return (l->locker == get_processor_id()); -} - -static inline void _mm_write_lock(mm_rwlock_t *l, const char *func, int level) -{ - if ( !mm_write_locked_by_me(l) ) - { - __check_lock_level(level); - write_lock(&l->lock); - l->locker = get_processor_id(); - l->locker_function = func; - l->unlock_level = __get_lock_level(); - __set_lock_level(level); - } - l->recurse_count++; -} - -static inline void mm_write_unlock(mm_rwlock_t *l) -{ - if ( --(l->recurse_count) != 0 ) - return; - l->locker = -1; - l->locker_function = "nobody"; - __set_lock_level(l->unlock_level); - write_unlock(&l->lock); -} - -static inline void _mm_read_lock(mm_rwlock_t *l, int level) -{ - __check_lock_level(level); - read_lock(&l->lock); - /* There's nowhere to store the per-CPU unlock level so we can't - * set the lock level. */ -} - -static inline void mm_read_unlock(mm_rwlock_t *l) -{ - read_unlock(&l->lock); -} - -/* This wrapper uses the line number to express the locking order below */ -#define declare_mm_lock(name) \ - static inline void mm_lock_##name(mm_lock_t *l, const char *func, int rec)\ - { _mm_lock(l, func, __LINE__, rec); } -#define declare_mm_rwlock(name) \ - static inline void mm_write_lock_##name(mm_rwlock_t *l, const char *func) \ - { _mm_write_lock(l, func, __LINE__); } \ - static inline void mm_read_lock_##name(mm_rwlock_t *l) \ - { _mm_read_lock(l, __LINE__); } -/* These capture the name of the calling function */ -#define mm_lock(name, l) mm_lock_##name(l, __func__, 0) -#define mm_lock_recursive(name, l) mm_lock_##name(l, __func__, 1) -#define mm_write_lock(name, l) mm_write_lock_##name(l, __func__) -#define mm_read_lock(name, l) mm_read_lock_##name(l) - -/* This wrapper is intended for "external" locks which do not use - * the mm_lock_t types. Such locks inside the mm code are also subject - * to ordering constraints. */ -#define declare_mm_order_constraint(name) \ - static inline void mm_enforce_order_lock_pre_##name(void) \ - { _mm_enforce_order_lock_pre(__LINE__); } \ - static inline void mm_enforce_order_lock_post_##name( \ - int *unlock_level, unsigned short *recurse_count) \ - { _mm_enforce_order_lock_post(__LINE__, unlock_level, recurse_count); } \ - -static inline void mm_unlock(mm_lock_t *l) -{ - if ( l->lock.recurse_cnt == 1 ) - { - l->locker_function = "nobody"; - __set_lock_level(l->unlock_level); - } - spin_unlock_recursive(&l->lock); -} - -static inline void mm_enforce_order_unlock(int unlock_level, - unsigned short *recurse_count) -{ - if ( recurse_count ) - { - BUG_ON(*recurse_count == 0); - if ( (*recurse_count)-- == 1 ) - { - __set_lock_level(unlock_level); - } - } else { - __set_lock_level(unlock_level); - } -} - -/************************************************************************ - * * - * To avoid deadlocks, these locks _MUST_ be taken in the order they're * - * declared in this file. The locking functions will enforce this. * - * * - ************************************************************************/ - -declare_mm_lock(nestedp2m) -#define nestedp2m_lock(d) mm_lock(nestedp2m, &(d)->arch.nested_p2m_lock) -#define nestedp2m_unlock(d) mm_unlock(&(d)->arch.nested_p2m_lock) - -/* P2M lock (per-p2m-table) - * - * This protects all queries and updates to the p2m table. - * Queries may be made under the read lock but all modifications - * need the main (write) lock. - * - * The write lock is recursive as it is common for a code path to look - * up a gfn and later mutate it. - */ - -declare_mm_rwlock(p2m); -#define p2m_lock(p) mm_write_lock(p2m, &(p)->lock); -#define p2m_unlock(p) mm_write_unlock(&(p)->lock); -#define gfn_lock(p,g,o) p2m_lock(p) -#define gfn_unlock(p,g,o) p2m_unlock(p) -#define p2m_read_lock(p) mm_read_lock(p2m, &(p)->lock) -#define p2m_read_unlock(p) mm_read_unlock(&(p)->lock) -#define p2m_locked_by_me(p) mm_write_locked_by_me(&(p)->lock) -#define gfn_locked_by_me(p,g) p2m_locked_by_me(p) - -/* Sharing per page lock - * - * This is an external lock, not represented by an mm_lock_t. The memory - * sharing lock uses it to protect addition and removal of (gfn,domain) - * tuples to a shared page. We enforce order here against the p2m lock, - * which is taken after the page_lock to change the gfn's p2m entry. - * - * The lock is recursive because during share we lock two pages. */ - -declare_mm_order_constraint(per_page_sharing) -#define page_sharing_mm_pre_lock() mm_enforce_order_lock_pre_per_page_sharing() -#define page_sharing_mm_post_lock(l, r) \ - mm_enforce_order_lock_post_per_page_sharing((l), (r)) -#define page_sharing_mm_unlock(l, r) mm_enforce_order_unlock((l), (r)) - -/* Nested P2M lock (per-domain) - * - * A per-domain lock that protects the mapping from nested-CR3 to - * nested-p2m. In particular it covers: - * - the array of nested-p2m tables, and all LRU activity therein; and - * - setting the "cr3" field of any p2m table to a non-P2M_BASE_EAADR value. - * (i.e. assigning a p2m table to be the shadow of that cr3 */ - -/* PoD lock (per-p2m-table) - * - * Protects private PoD data structs: entry and cache - * counts, page lists, sweep parameters. */ - -declare_mm_lock(pod) -#define pod_lock(p) mm_lock(pod, &(p)->pod.lock) -#define pod_unlock(p) mm_unlock(&(p)->pod.lock) -#define pod_locked_by_me(p) mm_locked_by_me(&(p)->pod.lock) - -/* Page alloc lock (per-domain) - * - * This is an external lock, not represented by an mm_lock_t. However, - * pod code uses it in conjunction with the p2m lock, and expecting - * the ordering which we enforce here. - * The lock is not recursive. */ - -declare_mm_order_constraint(page_alloc) -#define page_alloc_mm_pre_lock() mm_enforce_order_lock_pre_page_alloc() -#define page_alloc_mm_post_lock(l) mm_enforce_order_lock_post_page_alloc(&(l), NULL) -#define page_alloc_mm_unlock(l) mm_enforce_order_unlock((l), NULL) - -/* Paging lock (per-domain) - * - * For shadow pagetables, this lock protects - * - all changes to shadow page table pages - * - the shadow hash table - * - the shadow page allocator - * - all changes to guest page table pages - * - all changes to the page_info->tlbflush_timestamp - * - the page_info->count fields on shadow pages - * - * For HAP, it protects the NPT/EPT tables and mode changes. - * - * It also protects the log-dirty bitmap from concurrent accesses (and - * teardowns, etc). */ - -declare_mm_lock(paging) -#define paging_lock(d) mm_lock(paging, &(d)->arch.paging.lock) -#define paging_lock_recursive(d) \ - mm_lock_recursive(paging, &(d)->arch.paging.lock) -#define paging_unlock(d) mm_unlock(&(d)->arch.paging.lock) -#define paging_locked_by_me(d) mm_locked_by_me(&(d)->arch.paging.lock) - -#endif /* _MM_LOCKS_H */ diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c index 15c6e83..1d11956 100644 --- a/xen/arch/x86/mm/p2m-ept.c +++ b/xen/arch/x86/mm/p2m-ept.c @@ -19,6 +19,9 @@ #include <xen/config.h> #include <xen/domain_page.h> #include <xen/sched.h> +#include <xen/iommu.h> +#include <xen/keyhandler.h> +#include <xen/softirq.h> #include <asm/current.h> #include <asm/paging.h> #include <asm/types.h> @@ -26,13 +29,9 @@ #include <asm/p2m.h> #include <asm/hvm/vmx/vmx.h> #include <asm/hvm/vmx/vmcs.h> -#include <xen/iommu.h> #include <asm/mtrr.h> #include <asm/hvm/cacheattr.h> -#include <xen/keyhandler.h> -#include <xen/softirq.h> - -#include "mm-locks.h" +#include <asm/mm-locks.h> #define atomic_read_ept_entry(__pepte) \ ( (ept_entry_t) { .epte = read_atomic(&(__pepte)->epte) } ) diff --git a/xen/arch/x86/mm/p2m-pod.c b/xen/arch/x86/mm/p2m-pod.c index 43f507c..1484aa3 100644 --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -32,8 +32,8 @@ #include <asm/mem_sharing.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/svm/amd-iommu-proto.h> +#include <asm/mm-locks.h> -#include "mm-locks.h" /* Override macros from asm/page.h to make them work with mfn_t */ #undef mfn_to_page diff --git a/xen/arch/x86/mm/p2m-pt.c b/xen/arch/x86/mm/p2m-pt.c index e48b63a..a6bbb46 100644 --- a/xen/arch/x86/mm/p2m-pt.c +++ b/xen/arch/x86/mm/p2m-pt.c @@ -37,8 +37,8 @@ #include <asm/mem_sharing.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/svm/amd-iommu-proto.h> +#include <asm/mm-locks.h> -#include "mm-locks.h" /* Override macros from asm/page.h to make them work with mfn_t */ #undef mfn_to_page diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index bf8e537..d192d6c 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -36,9 +36,9 @@ #include <asm/mem_sharing.h> #include <asm/hvm/nestedhvm.h> #include <asm/hvm/svm/amd-iommu-proto.h> +#include <asm/mm-locks.h> #include <xsm/xsm.h> -#include "mm-locks.h" /* turn on/off 1GB host page table support for hap, default on */ bool_t __read_mostly opt_hap_1gb = 1; @@ -1327,106 +1327,6 @@ void p2m_mem_paging_resume(struct domain *d) } } -bool_t p2m_mem_access_check(paddr_t gpa, unsigned long gla, - struct npfec npfec, - mem_event_request_t **req_ptr) -{ - struct vcpu *v = current; - unsigned long gfn = gpa >> PAGE_SHIFT; - struct domain *d = v->domain; - struct p2m_domain* p2m = p2m_get_hostp2m(d); - mfn_t mfn; - p2m_type_t p2mt; - p2m_access_t p2ma; - mem_event_request_t *req; - int rc; - - /* 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); - - if ( npfec.write_access && p2ma == p2m_access_rx2rw ) - { - rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw); - 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); - ASSERT(rc == 0); - } - gfn_unlock(p2m, gfn, 0); - - /* Otherwise, check if there is a memory event listener, and send the message along */ - if ( !mem_event_check_ring(&d->mem_event->access) || !req_ptr ) - { - /* No listener */ - if ( p2m->access_required ) - { - gdprintk(XENLOG_INFO, "Memory access permissions failure, " - "no mem_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); - 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); - ASSERT(rc == 0); - } - gfn_unlock(p2m, gfn, 0); - return 1; - } - } - - *req_ptr = NULL; - req = xzalloc(mem_event_request_t); - if ( req ) - { - *req_ptr = req; - req->reason = MEM_EVENT_REASON_VIOLATION; - - /* Pause the current VCPU */ - if ( p2ma != p2m_access_n2rwx ) - req->flags |= MEM_EVENT_FLAG_VCPU_PAUSED; - - /* Send request to mem event */ - req->gfn = gfn; - req->offset = gpa & ((1 << PAGE_SHIFT) - 1); - req->gla_valid = npfec.gla_valid; - req->gla = gla; - if ( npfec.kind == npfec_kind_with_gla ) - req->fault_with_gla = 1; - else if ( npfec.kind == npfec_kind_in_gpt ) - req->fault_in_gpt = 1; - req->access_r = npfec.read_access; - req->access_w = npfec.write_access; - req->access_x = npfec.insn_fetch; - req->vcpu_id = v->vcpu_id; - } - - /* Pause the current VCPU */ - if ( p2ma != p2m_access_n2rwx ) - mem_event_vcpu_pause(v); - - /* VCPU may be paused, return whether we promoted automatically */ - return (p2ma == p2m_access_n2rwx); -} - /* Set access type for a region of pfns. * If start_pfn == -1ul, sets the default access type */ long p2m_set_mem_access(struct domain *d, unsigned long pfn, uint32_t nr, diff --git a/xen/arch/x86/mm/paging.c b/xen/arch/x86/mm/paging.c index 455000d..eb7751e 100644 --- a/xen/arch/x86/mm/paging.c +++ b/xen/arch/x86/mm/paging.c @@ -22,15 +22,15 @@ #include <xen/init.h> #include <xen/guest_access.h> +#include <xen/numa.h> #include <asm/paging.h> #include <asm/shadow.h> #include <asm/p2m.h> #include <asm/hap.h> #include <asm/hvm/nestedhvm.h> -#include <xen/numa.h> +#include <asm/mm-locks.h> #include <xsm/xsm.h> -#include "mm-locks.h" /* Printouts */ #define PAGING_PRINTK(_f, _a...) \ diff --git a/xen/common/mem_access.c b/xen/common/mem_access.c index 1674d7a..8fc2942 100644 --- a/xen/common/mem_access.c +++ b/xen/common/mem_access.c @@ -136,7 +136,7 @@ int mem_access_memop(unsigned long cmd, return rc; } -int mem_access_send_req(struct domain *d, mem_event_request_t *req) +static int mem_access_send_req(struct domain *d, mem_event_request_t *req) { int rc = mem_event_claim_slot(d, &d->mem_event->access); if ( rc < 0 ) @@ -147,6 +147,147 @@ int mem_access_send_req(struct domain *d, mem_event_request_t *req) return 0; } +int mem_access_check(paddr_t gpa, unsigned long gla, struct npfec npfec) +{ + bool_t violation; + mfn_t mfn; + p2m_access_t p2ma; + p2m_type_t p2mt; + mem_event_request_t *req; + int rc; + struct vcpu *v = current; + unsigned long gfn = gpa >> PAGE_SHIFT; + struct domain *d = v->domain; + struct p2m_domain* 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(). */ + p2m_gpfn_lock(p2m, gfn); + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, NULL); + + /* Check if the access is against the permissions. */ + switch ( p2ma ) + { + case p2m_access_rwx: + violation = 0; + break; + case p2m_access_rw: + violation = npfec.insn_fetch; + break; + case p2m_access_wx: + violation = npfec.read_access; + break; + case p2m_access_rx: + case p2m_access_rx2rw: + violation = npfec.write_access; + break; + case p2m_access_x: + violation = npfec.read_access || npfec.write_access; + break; + case p2m_access_w: + violation = npfec.read_access || npfec.insn_fetch; + break; + case p2m_access_r: + violation = npfec.write_access || npfec.insn_fetch; + break; + case p2m_access_n: + case p2m_access_n2rwx: + default: + violation = npfec.read_access || npfec.write_access || npfec.insn_fetch; + break; + } + + /* If no violation is found here, it needs to be reinjected. */ + if ( !violation ) + { + p2m_gpfn_unlock(p2m, gfn); + return -EFAULT; + } + + /* Check for automatic setting promotion. */ + if ( npfec.write_access && p2ma == p2m_access_rx2rw ) + { + rc = p2m->set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2mt, p2m_access_rw); + ASSERT(rc == 0); + p2m_gpfn_unlock(p2m, gfn); + 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); + ASSERT(rc == 0); + } + p2m_gpfn_unlock(p2m, gfn); + + /* Otherwise, check if there is a memory event listener, and send the message along */ + if ( !mem_event_check_ring(&d->mem_event->access) ) + { + /* No listener */ + if ( p2m->access_required ) + { + gdprintk(XENLOG_INFO, "Memory access permissions failure, " + "no mem_event listener VCPU %d, dom %d\n", + v->vcpu_id, d->domain_id); + domain_crash(v->domain); + return -EFAULT; + } + else + { + p2m_gpfn_lock(p2m, gfn); + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, 0, 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); + ASSERT(rc == 0); + } + p2m_gpfn_unlock(p2m, gfn); + return 1; + } + } + + req = xzalloc(mem_event_request_t); + if ( req ) + { + req->reason = MEM_EVENT_REASON_VIOLATION; + + /* Pause the current VCPU */ + if ( p2ma != p2m_access_n2rwx ) + req->flags |= MEM_EVENT_FLAG_VCPU_PAUSED; + + /* Send request to mem event */ + req->gfn = gfn; + req->offset = gpa & ((1 << PAGE_SHIFT) - 1); + req->gla_valid = npfec.gla_valid; + req->gla = gla; + if ( npfec.kind == npfec_kind_with_gla ) + req->fault_with_gla = 1; + else if ( npfec.kind == npfec_kind_in_gpt ) + req->fault_in_gpt = 1; + req->access_r = npfec.read_access; + req->access_w = npfec.write_access; + req->access_x = npfec.insn_fetch; + req->vcpu_id = v->vcpu_id; + + mem_access_send_req(v->domain, req); + xfree(req); + } + + /* Pause the current VCPU */ + if ( p2ma != p2m_access_n2rwx ) + mem_event_vcpu_pause(v); + + /* VCPU may be paused, return whether we promoted automatically */ + return (p2ma == p2m_access_n2rwx); +} + /* * Local variables: * mode: C diff --git a/xen/include/asm-x86/mm-locks.h b/xen/include/asm-x86/mm-locks.h new file mode 100644 index 0000000..7056a30 --- /dev/null +++ b/xen/include/asm-x86/mm-locks.h @@ -0,0 +1,299 @@ +/****************************************************************************** + * mm-locks.h + * + * Spinlocks used by the code in arch/x86/mm. + * + * Copyright (c) 2011 Citrix Systems, inc. + * Copyright (c) 2007 Advanced Micro Devices (Wei Huang) + * Copyright (c) 2006-2007 XenSource Inc. + * Copyright (c) 2006 Michael A Fetterman + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MM_LOCKS_H +#define _MM_LOCKS_H + +#include <asm/mem_sharing.h> + +/* Per-CPU variable for enforcing the lock ordering */ +DECLARE_PER_CPU(int, mm_lock_level); +#define __get_lock_level() (this_cpu(mm_lock_level)) + +static inline void mm_lock_init(mm_lock_t *l) +{ + spin_lock_init(&l->lock); + l->locker = -1; + l->locker_function = "nobody"; + l->unlock_level = 0; +} + +static inline int mm_locked_by_me(mm_lock_t *l) +{ + return (l->lock.recurse_cpu == current->processor); +} + +/* If you see this crash, the numbers printed are lines in this file + * where the offending locks are declared. */ +#define __check_lock_level(l) \ +do { \ + if ( unlikely(__get_lock_level() > (l)) ) \ + { \ + printk("mm locking order violation: %i > %i\n", \ + __get_lock_level(), (l)); \ + BUG(); \ + } \ +} while(0) + +#define __set_lock_level(l) \ +do { \ + __get_lock_level() = (l); \ +} while(0) + +static inline void _mm_lock(mm_lock_t *l, const char *func, int level, int rec) +{ + if ( !((mm_locked_by_me(l)) && rec) ) + __check_lock_level(level); + spin_lock_recursive(&l->lock); + if ( l->lock.recurse_cnt == 1 ) + { + l->locker_function = func; + l->unlock_level = __get_lock_level(); + } + else if ( (unlikely(!rec)) ) + panic("mm lock already held by %s", l->locker_function); + __set_lock_level(level); +} + +static inline void _mm_enforce_order_lock_pre(int level) +{ + __check_lock_level(level); +} + +static inline void _mm_enforce_order_lock_post(int level, int *unlock_level, + unsigned short *recurse_count) +{ + if ( recurse_count ) + { + if ( (*recurse_count)++ == 0 ) + { + *unlock_level = __get_lock_level(); + } + } else { + *unlock_level = __get_lock_level(); + } + __set_lock_level(level); +} + + +static inline void mm_rwlock_init(mm_rwlock_t *l) +{ + rwlock_init(&l->lock); + l->locker = -1; + l->locker_function = "nobody"; + l->unlock_level = 0; +} + +static inline int mm_write_locked_by_me(mm_rwlock_t *l) +{ + return (l->locker == get_processor_id()); +} + +static inline void _mm_write_lock(mm_rwlock_t *l, const char *func, int level) +{ + if ( !mm_write_locked_by_me(l) ) + { + __check_lock_level(level); + write_lock(&l->lock); + l->locker = get_processor_id(); + l->locker_function = func; + l->unlock_level = __get_lock_level(); + __set_lock_level(level); + } + l->recurse_count++; +} + +static inline void mm_write_unlock(mm_rwlock_t *l) +{ + if ( --(l->recurse_count) != 0 ) + return; + l->locker = -1; + l->locker_function = "nobody"; + __set_lock_level(l->unlock_level); + write_unlock(&l->lock); +} + +static inline void _mm_read_lock(mm_rwlock_t *l, int level) +{ + __check_lock_level(level); + read_lock(&l->lock); + /* There's nowhere to store the per-CPU unlock level so we can't + * set the lock level. */ +} + +static inline void mm_read_unlock(mm_rwlock_t *l) +{ + read_unlock(&l->lock); +} + +/* This wrapper uses the line number to express the locking order below */ +#define declare_mm_lock(name) \ + static inline void mm_lock_##name(mm_lock_t *l, const char *func, int rec)\ + { _mm_lock(l, func, __LINE__, rec); } +#define declare_mm_rwlock(name) \ + static inline void mm_write_lock_##name(mm_rwlock_t *l, const char *func) \ + { _mm_write_lock(l, func, __LINE__); } \ + static inline void mm_read_lock_##name(mm_rwlock_t *l) \ + { _mm_read_lock(l, __LINE__); } +/* These capture the name of the calling function */ +#define mm_lock(name, l) mm_lock_##name(l, __func__, 0) +#define mm_lock_recursive(name, l) mm_lock_##name(l, __func__, 1) +#define mm_write_lock(name, l) mm_write_lock_##name(l, __func__) +#define mm_read_lock(name, l) mm_read_lock_##name(l) + +/* This wrapper is intended for "external" locks which do not use + * the mm_lock_t types. Such locks inside the mm code are also subject + * to ordering constraints. */ +#define declare_mm_order_constraint(name) \ + static inline void mm_enforce_order_lock_pre_##name(void) \ + { _mm_enforce_order_lock_pre(__LINE__); } \ + static inline void mm_enforce_order_lock_post_##name( \ + int *unlock_level, unsigned short *recurse_count) \ + { _mm_enforce_order_lock_post(__LINE__, unlock_level, recurse_count); } \ + +static inline void mm_unlock(mm_lock_t *l) +{ + if ( l->lock.recurse_cnt == 1 ) + { + l->locker_function = "nobody"; + __set_lock_level(l->unlock_level); + } + spin_unlock_recursive(&l->lock); +} + +static inline void mm_enforce_order_unlock(int unlock_level, + unsigned short *recurse_count) +{ + if ( recurse_count ) + { + BUG_ON(*recurse_count == 0); + if ( (*recurse_count)-- == 1 ) + { + __set_lock_level(unlock_level); + } + } else { + __set_lock_level(unlock_level); + } +} + +/************************************************************************ + * * + * To avoid deadlocks, these locks _MUST_ be taken in the order they're * + * declared in this file. The locking functions will enforce this. * + * * + ************************************************************************/ + +declare_mm_lock(nestedp2m) +#define nestedp2m_lock(d) mm_lock(nestedp2m, &(d)->arch.nested_p2m_lock) +#define nestedp2m_unlock(d) mm_unlock(&(d)->arch.nested_p2m_lock) + +/* P2M lock (per-p2m-table) + * + * This protects all queries and updates to the p2m table. + * Queries may be made under the read lock but all modifications + * need the main (write) lock. + * + * The write lock is recursive as it is common for a code path to look + * up a gfn and later mutate it. + */ + +declare_mm_rwlock(p2m); +#define p2m_lock(p) mm_write_lock(p2m, &(p)->lock); +#define p2m_unlock(p) mm_write_unlock(&(p)->lock); +#define gfn_lock(p,g,o) p2m_lock(p) +#define gfn_unlock(p,g,o) p2m_unlock(p) +#define p2m_read_lock(p) mm_read_lock(p2m, &(p)->lock) +#define p2m_read_unlock(p) mm_read_unlock(&(p)->lock) +#define p2m_locked_by_me(p) mm_write_locked_by_me(&(p)->lock) +#define gfn_locked_by_me(p,g) p2m_locked_by_me(p) + +/* Sharing per page lock + * + * This is an external lock, not represented by an mm_lock_t. The memory + * sharing lock uses it to protect addition and removal of (gfn,domain) + * tuples to a shared page. We enforce order here against the p2m lock, + * which is taken after the page_lock to change the gfn's p2m entry. + * + * The lock is recursive because during share we lock two pages. */ + +declare_mm_order_constraint(per_page_sharing) +#define page_sharing_mm_pre_lock() mm_enforce_order_lock_pre_per_page_sharing() +#define page_sharing_mm_post_lock(l, r) \ + mm_enforce_order_lock_post_per_page_sharing((l), (r)) +#define page_sharing_mm_unlock(l, r) mm_enforce_order_unlock((l), (r)) + +/* Nested P2M lock (per-domain) + * + * A per-domain lock that protects the mapping from nested-CR3 to + * nested-p2m. In particular it covers: + * - the array of nested-p2m tables, and all LRU activity therein; and + * - setting the "cr3" field of any p2m table to a non-P2M_BASE_EAADR value. + * (i.e. assigning a p2m table to be the shadow of that cr3 */ + +/* PoD lock (per-p2m-table) + * + * Protects private PoD data structs: entry and cache + * counts, page lists, sweep parameters. */ + +declare_mm_lock(pod) +#define pod_lock(p) mm_lock(pod, &(p)->pod.lock) +#define pod_unlock(p) mm_unlock(&(p)->pod.lock) +#define pod_locked_by_me(p) mm_locked_by_me(&(p)->pod.lock) + +/* Page alloc lock (per-domain) + * + * This is an external lock, not represented by an mm_lock_t. However, + * pod code uses it in conjunction with the p2m lock, and expecting + * the ordering which we enforce here. + * The lock is not recursive. */ + +declare_mm_order_constraint(page_alloc) +#define page_alloc_mm_pre_lock() mm_enforce_order_lock_pre_page_alloc() +#define page_alloc_mm_post_lock(l) mm_enforce_order_lock_post_page_alloc(&(l), NULL) +#define page_alloc_mm_unlock(l) mm_enforce_order_unlock((l), NULL) + +/* Paging lock (per-domain) + * + * For shadow pagetables, this lock protects + * - all changes to shadow page table pages + * - the shadow hash table + * - the shadow page allocator + * - all changes to guest page table pages + * - all changes to the page_info->tlbflush_timestamp + * - the page_info->count fields on shadow pages + * + * For HAP, it protects the NPT/EPT tables and mode changes. + * + * It also protects the log-dirty bitmap from concurrent accesses (and + * teardowns, etc). */ + +declare_mm_lock(paging) +#define paging_lock(d) mm_lock(paging, &(d)->arch.paging.lock) +#define paging_lock_recursive(d) \ + mm_lock_recursive(paging, &(d)->arch.paging.lock) +#define paging_unlock(d) mm_unlock(&(d)->arch.paging.lock) +#define paging_locked_by_me(d) mm_locked_by_me(&(d)->arch.paging.lock) + +#endif /* _MM_LOCKS_H */ diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index ff1ec97..f4c185d 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -32,6 +32,7 @@ #include <xen/p2m-common.h> #include <asm/mem_sharing.h> #include <asm/page.h> /* for pagetable_t */ +#include <asm/mm-locks.h> /* for gfn_lock */ extern bool_t opt_hap_1gb, opt_hap_2mb; @@ -584,6 +585,17 @@ long p2m_set_mem_access(struct domain *d, unsigned long start_pfn, uint32_t nr, int p2m_get_mem_access(struct domain *d, unsigned long pfn, xenmem_access_t *access); +/* Used by mem_access_check in mem_access common. */ +static inline void p2m_gpfn_lock(struct p2m_domain *p2m, unsigned long gpfn) +{ + gfn_lock(p2m, gpfn, 0); +} + +static inline void p2m_gpfn_unlock(struct p2m_domain *p2m, unsigned long gpfn) +{ + gfn_unlock(p2m, gpfn, 0); +} + /* * Internal functions, only called by other p2m code */ diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h index ccc268d..66762c1 100644 --- a/xen/include/asm-x86/page.h +++ b/xen/include/asm-x86/page.h @@ -11,10 +11,6 @@ #define PAGE_MASK (~(PAGE_SIZE-1)) #define PAGE_FLAG_MASK (~0) -#define PAGE_ORDER_4K 0 -#define PAGE_ORDER_2M 9 -#define PAGE_ORDER_1G 18 - #ifndef __ASSEMBLY__ # include <asm/types.h> # include <xen/lib.h> diff --git a/xen/include/xen/mem_access.h b/xen/include/xen/mem_access.h index 6ceb2a4..7d921a6 100644 --- a/xen/include/xen/mem_access.h +++ b/xen/include/xen/mem_access.h @@ -29,11 +29,20 @@ int mem_access_memop(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(xen_mem_access_op_t) arg); -int mem_access_send_req(struct domain *d, mem_event_request_t *req); /* Resumes the running of the VCPU, restarting the last instruction */ void mem_access_resume(struct domain *d); +/* + * Return with rc < 0 means the fault wasn't triggered by mem_access. + * Return with rc == 0 means it was but rights were not automatically promoted. + * Return with rc > 0 means it was and rights were automatically promoted. + * + * If the fault was triggered by mem_access, this function automatically + * forwards the event to the listener. + */ +int mem_access_check(paddr_t gpa, unsigned long gla, struct npfec npfec); + #else static inline @@ -43,14 +52,14 @@ int mem_access_memop(unsigned long cmd, return -ENOSYS; } +static inline void mem_access_resume(struct domain *d) {} + static inline -int mem_access_send_req(struct domain *d, mem_event_request_t *req) +int mem_access_check(paddr_t gpa, unsigned long gla, struct npfec npfec) { return -ENOSYS; } -static inline void mem_access_resume(struct domain *d) {} - #endif /* HAS_MEM_ACCESS */ #endif /* _XEN_ASM_MEM_ACCESS_H */ diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h index 74a65a6..bffd8e6 100644 --- a/xen/include/xen/mm.h +++ b/xen/include/xen/mm.h @@ -131,6 +131,10 @@ struct npfec { #define MAX_ORDER 20 /* 2^20 contiguous pages */ #endif +#define PAGE_ORDER_4K 0 +#define PAGE_ORDER_2M 9 +#define PAGE_ORDER_1G 18 + #define page_list_entry list_head #include <asm/mm.h> -- 2.1.0 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |