--- repo/tools/libxc/Makefile 2011-01-02 14:01:17.680914482 -0800 +++ new/tools/libxc/Makefile 2010-12-29 16:31:09.659926627 -0800 @@ -28,6 +28,7 @@ CTRL_SRCS-y += xc_tmem.c CTRL_SRCS-y += xc_mem_event.c CTRL_SRCS-y += xc_mem_paging.c +CTRL_SRCS-y += xc_mem_access.c CTRL_SRCS-y += xc_memshr.c CTRL_SRCS-y += xc_hcall_buf.c CTRL_SRCS-y += xc_foreign_memory.c --- repo/tools/libxc/xc_misc.c 2011-01-02 14:01:17.729038477 -0800 +++ new/tools/libxc/xc_misc.c 2011-01-02 13:16:32.869139452 -0800 @@ -511,6 +511,98 @@ return rc; } +int xc_hvm_set_mem_access( + xc_interface *xch, domid_t dom, hvmmem_access_t mem_access, uint64_t first_pfn, uint64_t nr) +{ + DECLARE_HYPERCALL; + DECLARE_HYPERCALL_BUFFER(struct xen_hvm_set_mem_access, arg); + int rc; + + arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg)); + if ( arg == NULL ) + { + PERROR("Could not allocate memory for xc_hvm_set_mem_access hypercall"); + return -1; + } + + arg->domid = dom; + arg->hvmmem_access = mem_access; + arg->first_pfn = first_pfn; + arg->nr = nr; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_set_mem_access; + hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg); + + rc = do_xen_hypercall(xch, &hypercall); + + xc_hypercall_buffer_free(xch, arg); + + return rc; +} + +int xc_hvm_get_mem_access( + xc_interface *xch, domid_t dom, uint64_t pfn, hvmmem_access_t* mem_access) +{ + DECLARE_HYPERCALL; + DECLARE_HYPERCALL_BUFFER(struct xen_hvm_get_mem_access, arg); + int rc; + + arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg)); + if ( arg == NULL ) + { + PERROR("Could not allocate memory for xc_hvm_get_mem_access hypercall"); + return -1; + } + + arg->domid = dom; + arg->pfn = pfn; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_get_mem_access; + hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg); + + rc = do_xen_hypercall(xch, &hypercall); + + if ( !rc ) + *mem_access = arg->hvmmem_access; + + xc_hypercall_buffer_free(xch, arg); + + return rc; +} + +int xc_hvm_inject_trap( + xc_interface *xch, domid_t dom, int vcpu, uint32_t trap, uint32_t error_code, + uint64_t cr2) +{ + DECLARE_HYPERCALL; + DECLARE_HYPERCALL_BUFFER(struct xen_hvm_inject_trap, arg); + int rc; + + arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg)); + if ( arg == NULL ) + { + PERROR("Could not allocate memory for xc_hvm_inject_trap hypercall"); + return -1; + } + + arg->domid = dom; + arg->vcpuid = vcpu; + arg->trap = trap; + arg->error_code = error_code; + arg->cr2 = cr2; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_inject_trap; + hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg); + + rc = do_xen_hypercall(xch, &hypercall); + + xc_hypercall_buffer_free(xch, arg); + + return rc; +} /* * Local variables: --- repo/tools/libxc/xenctrl.h 2011-01-02 14:01:17.750913546 -0800 +++ new/tools/libxc/xenctrl.h 2011-01-02 10:50:42.527575561 -0800 @@ -1398,6 +1398,27 @@ int xc_hvm_set_mem_type( xc_interface *xch, domid_t dom, hvmmem_type_t memtype, uint64_t first_pfn, uint64_t nr); +/* + * Set a range of memory to a specific access. + * Allowed types are HVMMEM_access_default, HVMMEM_access_n, any combination of + * HVM_access_ + (rwx), and HVM_access_rx2rw + */ +int xc_hvm_set_mem_access( + xc_interface *xch, domid_t dom, hvmmem_access_t memaccess, uint64_t first_pfn, uint64_t nr); + +/* + * Gets the mem access for the given page (returned in memacess on success) + */ +int xc_hvm_get_mem_access( + xc_interface *xch, domid_t dom, uint64_t pfn, hvmmem_access_t* memaccess); + +/* + * Injects a hardware/software CPU trap, to take effect the next time the HVM + * resumes. + */ +int xc_hvm_inject_trap( + xc_interface *xch, domid_t dom, int vcpu, uint32_t trap, uint32_t error_code, + uint64_t cr2); /* * LOGGING AND ERROR REPORTING @@ -1704,6 +1725,8 @@ int xc_mem_paging_prep(xc_interface *xch, domid_t domain_id, unsigned long gfn); int xc_mem_paging_resume(xc_interface *xch, domid_t domain_id, unsigned long gfn); +int xc_mem_access_resume(xc_interface *xch, domid_t domain_id, + unsigned long gfn); /** * memshr operations --- repo/xen/arch/ia64/vmx/vmx_hypercall.c 2011-01-02 14:01:18.601617165 -0800 +++ new/xen/arch/ia64/vmx/vmx_hypercall.c 2010-12-29 16:31:09.529918011 -0800 @@ -218,6 +218,9 @@ } case HVMOP_set_mem_type: + case HVMOP_set_mem_access: + case HVMOP_get_mem_access: + rc = -ENOSYS; break; --- repo/xen/arch/x86/hvm/hvm.c 2011-01-02 14:01:18.739216170 -0800 +++ new/xen/arch/x86/hvm/hvm.c 2011-01-02 12:33:08.026463887 -0800 @@ -61,6 +61,8 @@ #include #include #include +#include +#include bool_t __read_mostly hvm_enabled; @@ -307,6 +309,15 @@ return; /* bail */ } } + + /* Inject pending hw/sw trap */ + if (v->arch.hvm_vcpu.inject_trap != -1) + { + hvm_inject_exception(v->arch.hvm_vcpu.inject_trap, + v->arch.hvm_vcpu.inject_error_code, + v->arch.hvm_vcpu.inject_cr2); + v->arch.hvm_vcpu.inject_trap = -1; + } } static void hvm_init_ioreq_page( @@ -947,6 +958,8 @@ spin_lock_init(&v->arch.hvm_vcpu.tm_lock); INIT_LIST_HEAD(&v->arch.hvm_vcpu.tm_list); + v->arch.hvm_vcpu.inject_trap = -1; + #ifdef CONFIG_COMPAT rc = setup_compat_arg_xlat(v); if ( rc != 0 ) @@ -1086,61 +1099,91 @@ domain_shutdown(v->domain, SHUTDOWN_reboot); } -bool_t hvm_hap_nested_page_fault(unsigned long gfn) +bool_t hvm_hap_nested_page_fault(unsigned long gpa, + bool_t gla_valid, + unsigned long gla, + bool_t access_r, + bool_t access_w, + bool_t access_x) { + unsigned long gfn = gpa >> PAGE_SHIFT; p2m_type_t p2mt; mfn_t mfn; struct vcpu *v = current; struct p2m_domain *p2m = p2m_get_hostp2m(v->domain); + int guest_fault = 0; mfn = gfn_to_mfn_guest(p2m, gfn, &p2mt); - /* - * If this GFN is emulated MMIO or marked as read-only, pass the fault - * to the mmio handler. - */ - if ( (p2mt == p2m_mmio_dm) || (p2mt == p2m_ram_ro) ) - { - if ( !handle_mmio() ) - hvm_inject_exception(TRAP_gp_fault, 0, 0); - return 1; - } - #ifdef __x86_64__ /* Check if the page has been paged out */ - if ( p2m_is_paged(p2mt) || (p2mt == p2m_ram_paging_out) ) + if ( p2m_is_paged(p2mt) || (p2mt == p2m_ram_paging_out) ) + { p2m_mem_paging_populate(p2m, gfn); + return 1; + } /* Mem sharing: unshare the page and try again */ - if ( p2mt == p2m_ram_shared ) + if ( p2mt == p2m_ram_shared && access_w ) { mem_sharing_unshare_page(p2m, gfn, 0); return 1; } #endif - - /* Spurious fault? PoD and log-dirty also take this path. */ - if ( p2m_is_ram(p2mt) ) + + /* + * If this GFN is emulated MMIO or marked as read-only (which old MMIO is), + * pass the fault to the mmio handler first. + */ + if ( (p2mt == p2m_mmio_dm) || (p2mt == p2m_ram_ro) ) { - /* - * Page log dirty is always done with order 0. If this mfn resides in - * a large page, we do not change other pages type within that large - * page. - */ - paging_mark_dirty(v->domain, mfn_x(mfn)); - p2m_change_type(p2m, gfn, p2m_ram_logdirty, p2m_ram_rw); + if ( !handle_mmio() ) + { + guest_fault = 1; + goto check_access_handler; + } return 1; } - /* Shouldn't happen: Maybe the guest was writing to a r/o grant mapping? */ - if ( p2mt == p2m_grant_map_ro ) + /* Was it a write access: log-dirty, etc... */ + if ( access_w ) { + /* PoD and log-dirty also take this path. */ + if ( p2mt != p2m_ram_rw && p2m_is_ram(p2mt) ) + { + /* + * Page log dirty is always done with order 0. If this mfn resides in + * a large page, we do not change other pages type within that large + * page. + */ + paging_mark_dirty(v->domain, mfn_x(mfn)); + p2m_change_type(p2m, gfn, p2m_ram_logdirty, p2m_ram_rw); + return 1; + } + + /* Shouldn't happen: Maybe the guest was writing to a r/o grant mapping? */ + if ( p2mt == p2m_grant_map_ro ) + { + gdprintk(XENLOG_WARNING, + "trying to write to read-only grant mapping\n"); + guest_fault = 1; + goto check_access_handler; + } + } /* end access_w */ + + check_access_handler: + /* Even if it is the guest's "fault", check with the mem_event interface instead if + * one is there */ + if ( p2m_mem_access_check(gpa, gla_valid, gla, access_r, access_w, access_x) ) + return 1; + + /* If there is no handler, then fault if guest_fault = 1 */ + if ( guest_fault ) { - gdprintk(XENLOG_WARNING, - "trying to write to read-only grant mapping\n"); hvm_inject_exception(TRAP_gp_fault, 0, 0); return 1; } + /* Nothing handled it: it must be an access error with no memory handler, so fail */ return 0; } @@ -3184,10 +3227,34 @@ case HVM_PARAM_ACPI_IOPORTS_LOCATION: rc = pmtimer_change_ioport(d, a.value); break; + case HVM_PARAM_MEMORY_EVENT_INT3: + if ( a.value & HVMPME_onchangeonly ) + rc = -EINVAL; + break; } - if ( rc == 0 ) + if ( rc == 0 ) + { d->arch.hvm_domain.params[a.index] = a.value; + + switch( a.index ) + { + case HVM_PARAM_MEMORY_EVENT_INT3: + { + domain_pause(d); + domain_unpause(d); /* Causes guest to latch new status */ + break; + } + case HVM_PARAM_MEMORY_EVENT_CR3: + { + for_each_vcpu ( d, v ) + hvm_funcs.update_guest_cr(v, 0); /* Latches new CR3 mask through CR0 code */ + break; + } + } + + } + } else { @@ -3412,6 +3479,143 @@ break; } + case HVMOP_set_mem_access: + { + struct xen_hvm_set_mem_access a; + struct domain *d; + struct p2m_domain *p2m; + unsigned long pfn; + + p2m_access_t memaccess[] = { + p2m_access_n, + p2m_access_r, + p2m_access_w, + p2m_access_rw, + p2m_access_x, + p2m_access_rx, + p2m_access_wx, + p2m_access_rwx, + p2m_access_rx2rw, + 0, /* HVMMEM_access_default -- will get set below */ + }; + + if ( copy_from_guest(&a, arg, 1) ) + return -EFAULT; + + rc = rcu_lock_target_domain_by_id(a.domid, &d); + if ( rc != 0 ) + return rc; + + rc = -EINVAL; + if ( !is_hvm_domain(d) ) + goto param_fail5; + + p2m = p2m_get_hostp2m(d); + memaccess[HVMMEM_access_default] = p2m->default_access; + + /* If request to set default access */ + if ( a.first_pfn == ~0ull ) + { + rc = 0; + p2m->default_access = memaccess[a.hvmmem_access]; + goto param_fail5; + } + + rc = -EINVAL; + if ( (a.first_pfn > domain_get_maximum_gpfn(d)) || + ((a.first_pfn + a.nr - 1) < a.first_pfn) || + ((a.first_pfn + a.nr - 1) > domain_get_maximum_gpfn(d)) ) + goto param_fail5; + + if ( a.hvmmem_access >= ARRAY_SIZE(memaccess) ) + goto param_fail5; + + for ( pfn = a.first_pfn; pfn < a.first_pfn + a.nr; pfn++ ) + { + p2m_type_t t; + mfn_t mfn; + int success; + + mfn = gfn_to_mfn_unshare(p2m, pfn, &t, 0); + + p2m_lock(p2m); + success = p2m->set_entry(p2m, pfn, mfn, 0, t, memaccess[a.hvmmem_access]); + p2m_unlock(p2m); + if ( !success ) + goto param_fail5; + } + + rc = 0; + + param_fail5: + rcu_unlock_domain(d); + break; + } + + case HVMOP_get_mem_access: + { + struct xen_hvm_get_mem_access a; + struct domain *d; + struct p2m_domain *p2m; + p2m_type_t t; + p2m_access_t ac; + mfn_t mfn; + + /* Interface access to internal p2m accesses */ + hvmmem_access_t memaccess[] = { + HVMMEM_access_n, + HVMMEM_access_r, + HVMMEM_access_w, + HVMMEM_access_rw, + HVMMEM_access_x, + HVMMEM_access_rx, + HVMMEM_access_wx, + HVMMEM_access_rwx, + HVMMEM_access_rx2rw + }; + + if ( copy_from_guest(&a, arg, 1) ) + return -EFAULT; + + rc = rcu_lock_target_domain_by_id(a.domid, &d); + if ( rc != 0 ) + return rc; + + rc = -EINVAL; + if ( !is_hvm_domain(d) ) + goto param_fail6; + + p2m = p2m_get_hostp2m(d); + + if ( a.pfn == ~0ull ) + { + a.hvmmem_access = memaccess[p2m->default_access]; + } + else { + rc = -EINVAL; + if ( (a.pfn > domain_get_maximum_gpfn(d)) ) + goto param_fail6; + + rc = -ESRCH; + mfn = p2m->get_entry(p2m, a.pfn, &t, &ac, p2m_query); + + if ( mfn_x(mfn) == INVALID_MFN ) + goto param_fail6; + + rc = -ERANGE; + if ( ac >= ARRAY_SIZE(memaccess) ) + goto param_fail6; + + a.hvmmem_access = memaccess[ac]; + } + + rc = copy_to_guest(arg, &a, 1) ? -EFAULT : 0; + + param_fail6: + rcu_unlock_domain(d); + break; + } + case HVMOP_pagetable_dying: { struct xen_hvm_pagetable_dying a; @@ -3426,12 +3630,12 @@ rc = -EINVAL; if ( !is_hvm_domain(d) || !paging_mode_shadow(d) ) - goto param_fail5; + goto param_fail7; rc = 0; pagetable_dying(d, a.gpa); - param_fail5: + param_fail7: rcu_unlock_domain(d); break; } @@ -3462,6 +3666,41 @@ break; } + case HVMOP_inject_trap: + { + xen_hvm_inject_trap_t tr; + struct domain *d; + struct vcpu *v; + + if ( copy_from_guest(&tr, arg, 1 ) ) + return -EFAULT; + + rc = rcu_lock_target_domain_by_id(tr.domid, &d); + if ( rc != 0 ) + return rc; + + rc = -EINVAL; + if ( !is_hvm_domain(d) ) + goto param_fail8; + + rc = -ENOENT; + if ( tr.vcpuid >= d->max_vcpus || (v = d->vcpu[tr.vcpuid]) == NULL ) + goto param_fail8; + + if ( v->arch.hvm_vcpu.inject_trap != -1 ) + rc = -EBUSY; + else + { + v->arch.hvm_vcpu.inject_trap = tr.trap; + v->arch.hvm_vcpu.inject_error_code = tr.error_code; + v->arch.hvm_vcpu.inject_cr2 = tr.cr2; + } + + param_fail8: + rcu_unlock_domain(d); + break; + } + default: { gdprintk(XENLOG_WARNING, "Bad HVM op %ld.\n", op); @@ -3502,6 +3741,161 @@ return rc; } +int hvm_memory_event_cr0(unsigned long value, unsigned long old) { + struct vcpu* v = current; + struct domain *d = v->domain; + mem_event_request_t req; + + long p = d->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_CR0]; + + if ( p & HVMPME_MODE_MASK ) + { + int rc; + + if ( (p & HVMPME_onchangeonly) && value == old ) + return 1; + + rc = mem_event_check_ring(d); + if ( rc ) + return rc; + + memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_ACCESS; + req.reason = MEM_EVENT_REASON_CR0; + + if ( (p & HVMPME_MODE_MASK) == HVMPME_mode_sync ) + { + req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED; + vcpu_pause_nosync(v); + } + + req.gfn = value; + req.vcpu_id = v->vcpu_id; + + mem_event_put_request(d, &req); + + return 1; + } + return 0; +} + +int hvm_memory_event_cr3(unsigned long value, unsigned long old) { + struct vcpu* v = current; + struct domain *d = v->domain; + mem_event_request_t req; + + long p = d->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_CR3]; + + if ( p & HVMPME_MODE_MASK ) + { + int rc; + + if ( (p & HVMPME_onchangeonly) && value == old ) + return 1; + + rc = mem_event_check_ring(d); + if ( rc ) + return rc; + + memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_ACCESS; + req.reason = MEM_EVENT_REASON_CR3; + + if ( (p & HVMPME_MODE_MASK) == HVMPME_mode_sync ) + { + req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED; + vcpu_pause_nosync(v); + } + + req.gfn = value; + req.vcpu_id = v->vcpu_id; + + mem_event_put_request(d, &req); + + return 1; + } + return 0; +} + +int hvm_memory_event_cr4(unsigned long value, unsigned long old) { + struct vcpu* v = current; + struct domain *d = v->domain; + mem_event_request_t req; + + long p = d->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_CR4]; + + if ( p & HVMPME_MODE_MASK ) + { + int rc; + + if ( (p & HVMPME_onchangeonly) && value == old ) + return 1; + + rc = mem_event_check_ring(d); + if ( rc ) + return rc; + + memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_ACCESS; + req.reason = MEM_EVENT_REASON_CR4; + + if ( (p & HVMPME_MODE_MASK) == HVMPME_mode_sync ) + { + req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED; + vcpu_pause_nosync(v); + } + + req.gfn = value; + req.vcpu_id = v->vcpu_id; + + mem_event_put_request(d, &req); + + return 1; + } + return 0; +} + +int hvm_memory_event_int3(unsigned long gla) { + struct vcpu* v = current; + struct domain *d = v->domain; + mem_event_request_t req; + + long p = d->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3]; + + if ( p & HVMPME_MODE_MASK ) + { + uint32_t pfec = PFEC_page_present; + unsigned long gfn; + int rc; + + rc = mem_event_check_ring(d); + if ( rc ) + return rc; + + gfn = paging_gva_to_gfn(current, gla, &pfec); + + memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_ACCESS; + req.reason = MEM_EVENT_REASON_INT3; + + if ( (p & HVMPME_MODE_MASK) == HVMPME_mode_sync ) + { + req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED; + vcpu_pause_nosync(v); + } + + req.gfn = gfn; + req.offset = gla & ((1 << PAGE_SHIFT) - 1); + req.gla = gla; + req.gla_valid = 1; + req.vcpu_id = v->vcpu_id; + + mem_event_put_request(d, &req); + + return 1; + } + return 0; +} /* * Local variables: --- repo/xen/arch/x86/hvm/svm/svm.c 2011-01-02 14:01:18.761106064 -0800 +++ new/xen/arch/x86/hvm/svm/svm.c 2010-12-29 16:31:09.499921600 -0800 @@ -979,7 +979,7 @@ __trace_var(TRC_HVM_NPF, 0, sizeof(_d), &_d); } - if ( hvm_hap_nested_page_fault(gfn) ) + if ( hvm_hap_nested_page_fault(gpa, 0, ~0ull, 0, 0, 0) ) return; /* Everything else is an error. */ --- repo/xen/arch/x86/hvm/vmx/vmcs.c 2011-01-02 14:01:18.770642823 -0800 +++ new/xen/arch/x86/hvm/vmx/vmcs.c 2011-01-02 12:33:08.076511820 -0800 @@ -1082,7 +1082,9 @@ hvm_asid_flush_vcpu(v); } - debug_state = v->domain->debugger_attached; + debug_state = v->domain->debugger_attached + || v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3]; + if ( unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) ) { v->arch.hvm_vcpu.debug_state_latch = debug_state; --- repo/xen/arch/x86/hvm/vmx/vmx.c 2011-01-02 14:01:18.779947036 -0800 +++ new/xen/arch/x86/hvm/vmx/vmx.c 2011-01-02 12:33:08.066464467 -0800 @@ -1064,12 +1064,16 @@ if ( paging_mode_hap(v->domain) ) { - /* We manage GUEST_CR3 when guest CR0.PE is zero. */ + /* We manage GUEST_CR3 when guest CR0.PE is zero or when cr3 memevents are on */ uint32_t cr3_ctls = (CPU_BASED_CR3_LOAD_EXITING | CPU_BASED_CR3_STORE_EXITING); v->arch.hvm_vmx.exec_control &= ~cr3_ctls; if ( !hvm_paging_enabled(v) ) v->arch.hvm_vmx.exec_control |= cr3_ctls; + + if ( v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_CR3] ) + v->arch.hvm_vmx.exec_control |= CPU_BASED_CR3_LOAD_EXITING; + vmx_update_cpu_exec_control(v); /* Changing CR0.PE can change some bits in real CR4. */ @@ -1252,9 +1256,12 @@ unsigned long intr_info = __vmread(VM_ENTRY_INTR_INFO); struct vcpu *curr = current; + int type = X86_EVENTTYPE_HW_EXCEPTION; + switch ( trap ) { case TRAP_debug: + type = X86_EVENTTYPE_SW_EXCEPTION; if ( guest_cpu_user_regs()->eflags & X86_EFLAGS_TF ) { __restore_debug_registers(curr); @@ -1269,6 +1276,9 @@ domain_pause_for_debugger(); return; } + + type = X86_EVENTTYPE_SW_EXCEPTION; + __vmwrite(VM_ENTRY_INSTRUCTION_LEN, 1); /* int3 */ } if ( unlikely(intr_info & INTR_INFO_VALID_MASK) && @@ -1279,7 +1289,7 @@ error_code = 0; } - __vmx_inject_exception(trap, X86_EVENTTYPE_HW_EXCEPTION, error_code); + __vmx_inject_exception(trap, type, error_code); if ( trap == TRAP_page_fault ) HVMTRACE_LONG_2D(PF_INJECT, error_code, @@ -1565,6 +1575,8 @@ unsigned long value; struct vcpu *v = current; struct vlapic *vlapic = vcpu_vlapic(v); + int rc = 0; + unsigned long old; switch ( gp ) { @@ -1589,13 +1601,25 @@ switch ( cr ) { case 0: - return !hvm_set_cr0(value); + old = v->arch.hvm_vcpu.guest_cr[0]; + rc = !hvm_set_cr0(value); + if (rc) + hvm_memory_event_cr0(value, old); + return rc; case 3: - return !hvm_set_cr3(value); + old = v->arch.hvm_vcpu.guest_cr[3]; + rc = !hvm_set_cr3(value); + if (rc) + hvm_memory_event_cr3(value, old); + return rc; case 4: - return !hvm_set_cr4(value); + old = v->arch.hvm_vcpu.guest_cr[4]; + rc = !hvm_set_cr4(value); + if (rc) + hvm_memory_event_cr4(value, old); + return rc; case 8: vlapic_set_reg(vlapic, APIC_TASKPRI, ((value & 0x0F) << 4)); @@ -1676,11 +1700,17 @@ cr = exit_qualification & VMX_CONTROL_REG_ACCESS_NUM; mov_from_cr(cr, gp, regs); break; - case VMX_CONTROL_REG_ACCESS_TYPE_CLTS: + case VMX_CONTROL_REG_ACCESS_TYPE_CLTS: + { + unsigned long old = v->arch.hvm_vcpu.guest_cr[0]; v->arch.hvm_vcpu.guest_cr[0] &= ~X86_CR0_TS; vmx_update_guest_cr(v, 0); + + hvm_memory_event_cr0(v->arch.hvm_vcpu.guest_cr[0], old); + HVMTRACE_0D(CLTS); break; + } case VMX_CONTROL_REG_ACCESS_TYPE_LMSW: value = v->arch.hvm_vcpu.guest_cr[0]; /* LMSW can: (1) set bits 0-3; (2) clear bits 1-3. */ @@ -2079,7 +2109,13 @@ __trace_var(TRC_HVM_NPF, 0, sizeof(_d), &_d); } - if ( hvm_hap_nested_page_fault(gfn) ) + if ( hvm_hap_nested_page_fault(gpa, + qualification & EPT_GLA_VALID ? 1 : 0, + qualification & EPT_GLA_VALID + ? __vmread(GUEST_LINEAR_ADDRESS) : ~0ull, + qualification & EPT_READ_VIOLATION ? 1 : 0, + qualification & EPT_WRITE_VIOLATION ? 1 : 0, + qualification & EPT_EXEC_VIOLATION ? 1 : 0) ) return; /* Everything else is an error. */ @@ -2344,13 +2380,29 @@ goto exit_and_crash; domain_pause_for_debugger(); break; - case TRAP_int3: - if ( !v->domain->debugger_attached ) - goto exit_and_crash; - update_guest_eip(); /* Safe: INT3 */ - current->arch.gdbsx_vcpu_event = TRAP_int3; - domain_pause_for_debugger(); - break; + case TRAP_int3: + { + if ( v->domain->debugger_attached ) + { + update_guest_eip(); /* Safe: INT3 */ + current->arch.gdbsx_vcpu_event = TRAP_int3; + domain_pause_for_debugger(); + break; + } + else { + int handled = hvm_memory_event_int3(regs->eip); + + if ( handled < 0 ) + { + vmx_inject_exception(TRAP_int3, HVM_DELIVER_NO_ERROR_CODE, 0); + break; + } + else if ( handled ) + break; + } + + goto exit_and_crash; + } case TRAP_no_device: vmx_fpu_dirty_intercept(); break; --- repo/xen/arch/x86/mm/hap/p2m-ept.c 2011-01-02 14:01:18.812788250 -0800 +++ new/xen/arch/x86/mm/hap/p2m-ept.c 2010-12-31 22:53:49.760220769 -0800 @@ -62,8 +62,9 @@ return r; } -static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type) +static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type, p2m_access_t access) { + /* First apply type permissions */ switch(type) { case p2m_invalid: @@ -75,30 +76,61 @@ case p2m_ram_paging_in_start: default: entry->r = entry->w = entry->x = 0; - return; + break; case p2m_ram_rw: entry->r = entry->w = entry->x = 1; - return; + break; case p2m_mmio_direct: entry->r = entry->x = 1; entry->w = !rangeset_contains_singleton(mmio_ro_ranges, entry->mfn); - return; + break; case p2m_ram_logdirty: case p2m_ram_ro: case p2m_ram_shared: entry->r = entry->x = 1; entry->w = 0; - return; + break; case p2m_grant_map_rw: entry->r = entry->w = 1; entry->x = 0; - return; + break; case p2m_grant_map_ro: entry->r = 1; entry->w = entry->x = 0; - return; + break; + } + + + /* Then restrict with access permissions */ + switch (access) + { + case p2m_access_n: + entry->r = entry->w = entry->x = 0; + break; + case p2m_access_r: + entry->w = entry->x = 0; + break; + case p2m_access_w: + entry->r = entry->x = 0; + break; + case p2m_access_x: + entry->r = entry->w = 0; + break; + case p2m_access_rx: + case p2m_access_rx2rw: + entry->w = 0; + break; + case p2m_access_wx: + entry->r = 0; + break; + case p2m_access_rw: + entry->x = 0; + break; + case p2m_access_rwx: + break; } + } #define GUEST_TABLE_MAP_FAILED 0 @@ -117,6 +149,8 @@ ept_entry->epte = 0; ept_entry->mfn = page_to_mfn(pg); + ept_entry->access = p2m->default_access; + ept_entry->r = ept_entry->w = ept_entry->x = 1; return 1; @@ -170,11 +204,12 @@ epte->emt = ept_entry->emt; epte->ipat = ept_entry->ipat; epte->sp = (level > 1) ? 1 : 0; + epte->access = ept_entry->access; epte->sa_p2mt = ept_entry->sa_p2mt; epte->mfn = ept_entry->mfn + i * trunk; epte->rsvd2_snp = ( iommu_enabled && iommu_snoop ) ? 1 : 0; - ept_p2m_type_to_flags(epte, epte->sa_p2mt); + ept_p2m_type_to_flags(epte, epte->sa_p2mt, epte->access); if ( (level - 1) == target ) continue; @@ -260,7 +295,7 @@ */ static int ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int order, p2m_type_t p2mt) + unsigned int order, p2m_type_t p2mt, p2m_access_t p2ma) { ept_entry_t *table, *ept_entry = NULL; unsigned long gfn_remainder = gfn; @@ -334,9 +369,11 @@ /* Construct the new entry, and then write it once */ new_entry.emt = epte_get_entry_emt(p2m->domain, gfn, mfn, &ipat, direct_mmio); + new_entry.ipat = ipat; new_entry.sp = order ? 1 : 0; new_entry.sa_p2mt = p2mt; + new_entry.access = p2ma; new_entry.rsvd2_snp = (iommu_enabled && iommu_snoop); if ( new_entry.mfn == mfn_x(mfn) ) @@ -344,7 +381,7 @@ else new_entry.mfn = mfn_x(mfn); - ept_p2m_type_to_flags(&new_entry, p2mt); + ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); } atomic_write_ept_entry(ept_entry, new_entry); @@ -384,6 +421,7 @@ new_entry.ipat = ipat; new_entry.sp = i ? 1 : 0; new_entry.sa_p2mt = p2mt; + new_entry.access = p2ma; new_entry.rsvd2_snp = (iommu_enabled && iommu_snoop); if ( new_entry.mfn == mfn_x(mfn) ) @@ -391,7 +429,7 @@ else /* the caller should take care of the previous page */ new_entry.mfn = mfn_x(mfn); - ept_p2m_type_to_flags(&new_entry, p2mt); + ept_p2m_type_to_flags(&new_entry, p2mt, p2ma); atomic_write_ept_entry(ept_entry, new_entry); } @@ -447,7 +485,7 @@ /* Read ept p2m entries */ static mfn_t ept_get_entry(struct p2m_domain *p2m, - unsigned long gfn, p2m_type_t *t, + unsigned long gfn, p2m_type_t *t, p2m_access_t* a, p2m_query_t q) { struct domain *d = p2m->domain; @@ -460,6 +498,7 @@ mfn_t mfn = _mfn(INVALID_MFN); *t = p2m_mmio_dm; + *a = p2m_access_n; /* This pfn is higher than the highest the p2m map currently holds */ if ( gfn > p2m->max_mapped_pfn ) @@ -519,6 +558,8 @@ if ( ept_entry->sa_p2mt != p2m_invalid ) { *t = ept_entry->sa_p2mt; + *a = ept_entry->access; + mfn = _mfn(ept_entry->mfn); if ( i ) { @@ -626,10 +667,10 @@ } static mfn_t ept_get_entry_current(struct p2m_domain *p2m, - unsigned long gfn, p2m_type_t *t, + unsigned long gfn, p2m_type_t *t, p2m_access_t *a, p2m_query_t q) { - return ept_get_entry(p2m, gfn, t, q); + return ept_get_entry(p2m, gfn, t, a, q); } /* @@ -689,7 +730,7 @@ order = level * EPT_TABLE_ORDER; if ( need_modify_ept_entry(p2m, gfn, mfn, e.ipat, e.emt, e.sa_p2mt) ) - ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt); + ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access); gfn += trunk; break; } @@ -699,7 +740,7 @@ else /* gfn assigned with 4k */ { if ( need_modify_ept_entry(p2m, gfn, mfn, e.ipat, e.emt, e.sa_p2mt) ) - ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt); + ept_set_entry(p2m, gfn, mfn, order, e.sa_p2mt, e.access); } } p2m_unlock(p2m); @@ -730,7 +771,7 @@ continue; e.sa_p2mt = nt; - ept_p2m_type_to_flags(&e, nt); + ept_p2m_type_to_flags(&e, nt, e.access); atomic_write_ept_entry(&epte[i], e); } } --- repo/xen/arch/x86/mm/Makefile 2011-01-02 14:01:18.801414916 -0800 +++ new/xen/arch/x86/mm/Makefile 2010-12-29 16:31:09.529918011 -0800 @@ -9,6 +9,7 @@ obj-$(x86_64) += mem_event.o obj-$(x86_64) += mem_paging.o obj-$(x86_64) += mem_sharing.o +obj-$(x86_64) += mem_access.o guest_walk_%.o: guest_walk.c Makefile $(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@ --- repo/xen/arch/x86/mm/mem_event.c 2011-01-02 14:01:18.812788250 -0800 +++ new/xen/arch/x86/mm/mem_event.c 2011-01-02 12:33:08.076511820 -0800 @@ -26,6 +26,7 @@ #include #include #include +#include /* for public/io/ring.h macros */ #define xen_mb() mb() @@ -67,6 +68,9 @@ mem_event_ring_lock_init(d); + /* Wake any VCPUs paused for memory events */ + mem_event_unpause_vcpus(d); + return 0; err_shared: @@ -143,12 +147,32 @@ vcpu_wake(v); } +int mem_event_check_listener(struct domain *d) { + struct vcpu *curr = current; + + /* If a listener exists, return */ + if ( d->mem_event.ring_page ) + return 1; + + /* Sleep the VCPU */ + if ( (curr->domain->domain_id == d->domain_id) ) + { + set_bit(_VPF_mem_event, &curr->pause_flags); + vcpu_sleep_nosync(curr); + } + + return 0; +} + int mem_event_check_ring(struct domain *d) { struct vcpu *curr = current; int free_requests; int ring_full; + if ( !d->mem_event.ring_page ) + return -1; + mem_event_ring_lock(d); free_requests = RING_FREE_REQUESTS(&d->mem_event.front_ring); @@ -157,7 +181,7 @@ gdprintk(XENLOG_INFO, "free request slots: %d\n", free_requests); WARN_ON(free_requests == 0); } - ring_full = free_requests < MEM_EVENT_RING_THRESHOLD; + ring_full = free_requests < MEM_EVENT_RING_THRESHOLD ? 1 : 0; if ( (curr->domain->domain_id == d->domain_id) && ring_full ) { @@ -203,7 +227,11 @@ return rc; #endif - if ( mec->mode == 0 ) + rc = -ENOSYS; + + switch ( mec-> mode ) + { + case 0: { switch( mec->op ) { @@ -268,13 +296,18 @@ rc = -ENOSYS; break; } + break; } - else + case XEN_DOMCTL_MEM_EVENT_OP_PAGING: { - rc = -ENOSYS; - - if ( mec->mode & XEN_DOMCTL_MEM_EVENT_OP_PAGING ) - rc = mem_paging_domctl(d, mec, u_domctl); + rc = mem_paging_domctl(d, mec, u_domctl); + break; + } + case XEN_DOMCTL_MEM_EVENT_OP_ACCESS: + { + rc = mem_access_domctl(d, mec, u_domctl); + break; + } } return rc; --- repo/xen/arch/x86/mm/mem_sharing.c 2011-01-02 14:01:18.812788250 -0800 +++ new/xen/arch/x86/mm/mem_sharing.c 2010-12-29 16:31:09.539923275 -0800 @@ -304,6 +304,8 @@ if(page != NULL) return page; memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_SHARED; + if(must_succeed) { /* We do not support 'must_succeed' any more. External operations such --- repo/xen/arch/x86/mm/p2m.c 2011-01-02 14:01:18.812788250 -0800 +++ new/xen/arch/x86/mm/p2m.c 2011-01-02 13:21:10.273589137 -0800 @@ -285,7 +285,7 @@ */ static int set_p2m_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int page_order, p2m_type_t p2mt); + unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma); static int p2m_pod_cache_add(struct p2m_domain *p2m, @@ -693,7 +693,7 @@ { /* All PoD: Mark the whole region invalid and tell caller * we're done. */ - set_p2m_entry(p2m, gpfn, _mfn(INVALID_MFN), order, p2m_invalid); + set_p2m_entry(p2m, gpfn, _mfn(INVALID_MFN), order, p2m_invalid, p2m->default_access); p2m->pod.entry_count-=(1<pod.entry_count < 0); ret = 1; @@ -716,7 +716,7 @@ mfn = gfn_to_mfn_query(p2m, gpfn + i, &t); if ( t == p2m_populate_on_demand ) { - set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid); + set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid, p2m->default_access); p2m->pod.entry_count--; /* Lock: p2m */ BUG_ON(p2m->pod.entry_count < 0); pod--; @@ -729,7 +729,7 @@ page = mfn_to_page(mfn); - set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid); + set_p2m_entry(p2m, gpfn + i, _mfn(INVALID_MFN), 0, p2m_invalid, p2m->default_access); set_gpfn_from_mfn(mfn_x(mfn), INVALID_M2P_ENTRY); p2m_pod_cache_add(p2m, page, 0); @@ -844,7 +844,7 @@ /* Try to remove the page, restoring old mapping if it fails. */ set_p2m_entry(p2m, gfn, _mfn(POPULATE_ON_DEMAND_MFN), 9, - p2m_populate_on_demand); + p2m_populate_on_demand, p2m->default_access); /* Make none of the MFNs are used elsewhere... for example, mapped * via the grant table interface, or by qemu. Allow one refcount for @@ -899,7 +899,7 @@ out_reset: if ( reset ) - set_p2m_entry(p2m, gfn, mfn0, 9, type0); + set_p2m_entry(p2m, gfn, mfn0, 9, type0, p2m->default_access); out: return ret; @@ -957,7 +957,7 @@ /* Try to remove the page, restoring old mapping if it fails. */ set_p2m_entry(p2m, gfns[i], _mfn(POPULATE_ON_DEMAND_MFN), 0, - p2m_populate_on_demand); + p2m_populate_on_demand, p2m->default_access); /* See if the page was successfully unmapped. (Allow one refcount * for being allocated to a domain.) */ @@ -966,7 +966,7 @@ unmap_domain_page(map[i]); map[i] = NULL; - set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i]); + set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i], p2m->default_access); continue; } @@ -988,7 +988,7 @@ * check timing. */ if ( j < PAGE_SIZE/sizeof(*map[i]) ) { - set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i]); + set_p2m_entry(p2m, gfns[i], mfns[i], 0, types[i], p2m->default_access); } else { @@ -1121,7 +1121,7 @@ * 512 2MB pages. The rest of 511 calls are unnecessary. */ set_p2m_entry(p2m, gfn_aligned, _mfn(POPULATE_ON_DEMAND_MFN), 9, - p2m_populate_on_demand); + p2m_populate_on_demand, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); return 0; @@ -1158,7 +1158,7 @@ gfn_aligned = (gfn >> order) << order; - set_p2m_entry(p2m, gfn_aligned, mfn, order, p2m_ram_rw); + set_p2m_entry(p2m, gfn_aligned, mfn, order, p2m_ram_rw, p2m->default_access); for( i = 0; i < (1UL << order); i++ ) set_gpfn_from_mfn(mfn_x(mfn) + i, gfn_aligned + i); @@ -1198,7 +1198,7 @@ gfn_aligned = (gfn>>order)<default_access); if ( tb_init_done ) { struct { @@ -1250,7 +1250,7 @@ // Returns 0 on error (out of memory) static int p2m_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int page_order, p2m_type_t p2mt) + unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma) { // XXX -- this might be able to be faster iff current->domain == d mfn_t table_mfn = pagetable_get_mfn(p2m_get_pagetable(p2m)); @@ -1401,7 +1401,7 @@ } static mfn_t -p2m_gfn_to_mfn(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *t, +p2m_gfn_to_mfn(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *t, p2m_access_t *a, p2m_query_t q) { mfn_t mfn; @@ -1416,6 +1416,8 @@ * XXX Once we start explicitly registering MMIO regions in the p2m * XXX we will return p2m_invalid for unmapped gfns */ *t = p2m_mmio_dm; + /* Not implemented except with EPT */ + *a = p2m_access_rwx; mfn = pagetable_get_mfn(p2m_get_pagetable(p2m)); @@ -1542,7 +1544,7 @@ /* Read the current domain's p2m table (through the linear mapping). */ static mfn_t p2m_gfn_to_mfn_current(struct p2m_domain *p2m, - unsigned long gfn, p2m_type_t *t, + unsigned long gfn, p2m_type_t *t, p2m_access_t *a, p2m_query_t q) { mfn_t mfn = _mfn(INVALID_MFN); @@ -1553,6 +1555,9 @@ * XXX Once we start explicitly registering MMIO regions in the p2m * XXX we will return p2m_invalid for unmapped gfns */ + /* Not currently implemented except for EPT */ + *a = p2m_access_rwx; + if ( gfn <= p2m->max_mapped_pfn ) { l1_pgentry_t l1e = l1e_empty(), *p2m_entry; @@ -1726,6 +1731,8 @@ INIT_PAGE_LIST_HEAD(&p2m->pod.single); p2m->domain = d; + p2m->default_access = p2m_access_rwx; /* Dom flags override */ + p2m->set_entry = p2m_set_entry; p2m->get_entry = p2m_gfn_to_mfn; p2m->get_entry_current = p2m_gfn_to_mfn_current; @@ -1737,7 +1744,7 @@ return; } -int p2m_init(struct domain *d) +int p2m_init(struct domain *d, unsigned int domcr_flags) { struct p2m_domain *p2m; @@ -1745,6 +1752,9 @@ if ( p2m == NULL ) return -ENOMEM; p2m_initialise(d, p2m); + + if ( domcr_flags & DOMCRF_access_required ) + p2m->access_required = 1; return 0; } @@ -1759,7 +1769,7 @@ static int set_p2m_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, - unsigned int page_order, p2m_type_t p2mt) + unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma) { struct domain *d = p2m->domain; unsigned long todo = 1ul << page_order; @@ -1776,7 +1786,7 @@ else order = 0; - if ( !p2m->set_entry(p2m, gfn, mfn, order, p2mt) ) + if ( !p2m->set_entry(p2m, gfn, mfn, order, p2mt, p2ma) ) rc = 0; gfn += 1ul << order; if ( mfn_x(mfn) != INVALID_MFN ) @@ -1837,7 +1847,7 @@ /* Initialise physmap tables for slot zero. Other code assumes this. */ if ( !set_p2m_entry(p2m, 0, _mfn(INVALID_MFN), 0, - p2m_invalid) ) + p2m_invalid, p2m->default_access) ) goto error; /* Copy all existing mappings from the page list and m2p */ @@ -1856,7 +1866,7 @@ (gfn != 0x55555555L) #endif && gfn != INVALID_M2P_ENTRY - && !set_p2m_entry(p2m, gfn, mfn, 0, p2m_ram_rw) ) + && !set_p2m_entry(p2m, gfn, mfn, 0, p2m_ram_rw, p2m->default_access) ) goto error_unlock; } spin_unlock(&p2m->domain->page_alloc_lock); @@ -1883,6 +1893,7 @@ #ifdef __x86_64__ unsigned long gfn; p2m_type_t t; + p2m_access_t a; mfn_t mfn; #endif @@ -1891,7 +1902,7 @@ #ifdef __x86_64__ for ( gfn=0; gfn < p2m->max_mapped_pfn; gfn++ ) { - mfn = p2m->get_entry(p2m, gfn, &t, p2m_query); + mfn = p2m->get_entry(p2m, gfn, &t, &a, p2m_query); if ( mfn_valid(mfn) && (t == p2m_ram_shared) ) BUG_ON(mem_sharing_unshare_page(p2m, gfn, MEM_SHARING_DESTROY_GFN)); } @@ -2188,6 +2199,7 @@ unsigned long i; mfn_t mfn_return; p2m_type_t t; + p2m_access_t a; if ( !paging_mode_translate(p2m->domain) ) { @@ -2201,12 +2213,12 @@ for ( i = 0; i < (1UL << page_order); i++ ) { - mfn_return = p2m->get_entry(p2m, gfn + i, &t, p2m_query); + mfn_return = p2m->get_entry(p2m, gfn + i, &t, &a, p2m_query); if ( !p2m_is_grant(t) ) set_gpfn_from_mfn(mfn+i, INVALID_M2P_ENTRY); ASSERT( !p2m_is_valid(t) || mfn + i == mfn_x(mfn_return) ); } - set_p2m_entry(p2m, gfn, _mfn(INVALID_MFN), page_order, p2m_invalid); + set_p2m_entry(p2m, gfn, _mfn(INVALID_MFN), page_order, p2m_invalid, p2m->default_access); } void @@ -2286,7 +2298,7 @@ /* Now, actually do the two-way mapping */ if ( !set_p2m_entry(p2m, gfn, _mfn(POPULATE_ON_DEMAND_MFN), order, - p2m_populate_on_demand) ) + p2m_populate_on_demand, p2m->default_access) ) rc = -EINVAL; else { @@ -2399,7 +2411,7 @@ /* Now, actually do the two-way mapping */ if ( mfn_valid(_mfn(mfn)) ) { - if ( !set_p2m_entry(p2m, gfn, _mfn(mfn), page_order, t) ) + if ( !set_p2m_entry(p2m, gfn, _mfn(mfn), page_order, t, p2m->default_access) ) rc = -EINVAL; if ( !p2m_is_grant(t) ) { @@ -2412,7 +2424,7 @@ gdprintk(XENLOG_WARNING, "Adding bad mfn to p2m map (%#lx -> %#lx)\n", gfn, mfn); if ( !set_p2m_entry(p2m, gfn, _mfn(INVALID_MFN), page_order, - p2m_invalid) ) + p2m_invalid, p2m->default_access) ) rc = -EINVAL; else { @@ -2565,7 +2577,7 @@ } /* Modify the p2m type of a single gfn from ot to nt, returning the - * entry's previous type */ + * entry's previous type. Resets the access permissions. */ p2m_type_t p2m_change_type(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t ot, p2m_type_t nt) { @@ -2578,7 +2590,7 @@ mfn = gfn_to_mfn_query(p2m, gfn, &pt); if ( pt == ot ) - set_p2m_entry(p2m, gfn, mfn, 0, nt); + set_p2m_entry(p2m, gfn, mfn, 0, nt, p2m->default_access); p2m_unlock(p2m); @@ -2609,7 +2621,7 @@ P2M_DEBUG("set mmio %lx %lx\n", gfn, mfn_x(mfn)); p2m_lock(p2m); - rc = set_p2m_entry(p2m, gfn, mfn, 0, p2m_mmio_direct); + rc = set_p2m_entry(p2m, gfn, mfn, 0, p2m_mmio_direct, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); if ( 0 == rc ) @@ -2639,7 +2651,7 @@ return 0; } p2m_lock(p2m); - rc = set_p2m_entry(p2m, gfn, _mfn(INVALID_MFN), 0, 0); + rc = set_p2m_entry(p2m, gfn, _mfn(INVALID_MFN), 0, 0, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); @@ -2665,7 +2677,7 @@ set_gpfn_from_mfn(mfn_x(omfn), INVALID_M2P_ENTRY); P2M_DEBUG("set shared %lx %lx\n", gfn, mfn_x(mfn)); - rc = set_p2m_entry(p2m, gfn, mfn, 0, p2m_ram_shared); + rc = set_p2m_entry(p2m, gfn, mfn, 0, p2m_ram_shared, p2m->default_access); if ( 0 == rc ) gdprintk(XENLOG_ERR, "set_mmio_p2m_entry: set_p2m_entry failed! mfn=%08lx\n", @@ -2708,7 +2720,7 @@ /* Fix p2m entry */ p2m_lock(p2m); - set_p2m_entry(p2m, gfn, mfn, 0, p2m_ram_paging_out); + set_p2m_entry(p2m, gfn, mfn, 0, p2m_ram_paging_out, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); @@ -2745,7 +2757,7 @@ /* Remove mapping from p2m table */ p2m_lock(p2m); - set_p2m_entry(p2m, gfn, _mfn(PAGING_MFN), 0, p2m_ram_paged); + set_p2m_entry(p2m, gfn, _mfn(PAGING_MFN), 0, p2m_ram_paged, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); @@ -2767,6 +2779,7 @@ return; memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_PAGING; /* Fix p2m mapping */ /* XXX: It seems inefficient to have this here, as it's only needed @@ -2775,7 +2788,7 @@ if ( p2mt == p2m_ram_paged ) { p2m_lock(p2m); - set_p2m_entry(p2m, gfn, _mfn(PAGING_MFN), 0, p2m_ram_paging_in_start); + set_p2m_entry(p2m, gfn, _mfn(PAGING_MFN), 0, p2m_ram_paging_in_start, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); } @@ -2811,7 +2824,7 @@ /* Fix p2m mapping */ p2m_lock(p2m); - set_p2m_entry(p2m, gfn, page_to_mfn(page), 0, p2m_ram_paging_in); + set_p2m_entry(p2m, gfn, page_to_mfn(page), 0, p2m_ram_paging_in, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); @@ -2831,7 +2844,7 @@ /* Fix p2m entry */ mfn = gfn_to_mfn(p2m, rsp.gfn, &p2mt); p2m_lock(p2m); - set_p2m_entry(p2m, rsp.gfn, mfn, 0, p2m_ram_rw); + set_p2m_entry(p2m, rsp.gfn, mfn, 0, p2m_ram_rw, p2m->default_access); audit_p2m(p2m, 1); p2m_unlock(p2m); @@ -2844,6 +2857,98 @@ } #endif /* __x86_64__ */ +int p2m_mem_access_check(unsigned long gpa, bool_t gla_valid, unsigned long gla, + bool_t access_r, bool_t access_w, bool_t access_x) +{ + struct vcpu *v = current; + mem_event_request_t req; + unsigned long gfn = gpa >> PAGE_SHIFT; + struct domain *d = v->domain; + struct p2m_domain* p2m = p2m_get_hostp2m(d); + int res; + mfn_t mfn; + p2m_type_t p2mt; + p2m_access_t p2ma; + + /* First, handle rx2rw conversion automatically */ + mfn = p2m->get_entry(p2m, gfn, &p2mt, &p2ma, p2m_query); + + if ( access_w && p2ma == p2m_access_rx2rw ) { + p2m_lock(p2m); + p2m->set_entry(p2m, gfn, mfn, 0, p2mt, p2m_access_rw); + p2m_unlock(p2m); + + return 1; /* handled */ + } + + /* Otherwise, check if there is a memory event listener, and send the message along */ + res = mem_event_check_ring(d); + if ( res < 0 ) + { + /* No listener */ + if ( p2m->access_required ) + { + printk(XENLOG_INFO + "Memory access permissions failure, no mem_event listener: pausing VCPU %d, dom %d\n", + v->vcpu_id, d->domain_id); + + /* Will pause the VCPU */ + (void) mem_event_check_listener(d); + } + else + { + /* A listener is not required, so clear the access restrictions */ + p2m_lock(p2m); + p2m->set_entry(p2m, gfn, mfn, 0, p2mt, p2m_access_rwx); + p2m_unlock(p2m); + } + + return 1; + } + else if ( res > 0 ) + return 1; /* No space in buffer */ + + memset(&req, 0, sizeof(req)); + req.type = MEM_EVENT_TYPE_ACCESS; + req.reason = MEM_EVENT_REASON_VIOLATION; + + /* Pause the current VCPU unconditionally */ + vcpu_pause_nosync(v); + 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 = gla_valid; + req.gla = gla; + req.access_r = access_r; + req.access_w = access_w; + req.access_x = access_x; + + req.vcpu_id = v->vcpu_id; + + mem_event_put_request(d, &req); + + /* VCPU paused, mem event request sent */ + return 1; +} + +void p2m_mem_access_resume(struct p2m_domain *p2m) +{ + struct domain *d = p2m->domain; + mem_event_response_t rsp; + + mem_event_get_response(d, &rsp); + + /* Unpause domain */ + if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED ) + vcpu_unpause(d->vcpu[rsp.vcpu_id]); + + /* Unpause any domains that were paused because the ring was full or no listener + * was available */ + mem_event_unpause_vcpus(d); +} + /* * Local variables: * mode: C --- repo/xen/arch/x86/mm/paging.c 2011-01-02 14:01:18.822788319 -0800 +++ new/xen/arch/x86/mm/paging.c 2010-12-29 16:31:09.559917127 -0800 @@ -647,7 +647,7 @@ { int rc; - if ( (rc = p2m_init(d)) != 0 ) + if ( (rc = p2m_init(d, domcr_flags)) != 0 ) return rc; /* The order of the *_init calls below is important, as the later --- repo/xen/common/domctl.c 2011-01-02 14:01:18.930198530 -0800 +++ new/xen/common/domctl.c 2010-12-29 16:31:09.599918686 -0800 @@ -398,7 +398,7 @@ if ( supervisor_mode_kernel || (op->u.createdomain.flags & ~(XEN_DOMCTL_CDF_hvm_guest | XEN_DOMCTL_CDF_hap | - XEN_DOMCTL_CDF_s3_integrity | XEN_DOMCTL_CDF_oos_off)) ) + XEN_DOMCTL_CDF_s3_integrity | XEN_DOMCTL_CDF_oos_off | XEN_DOMCTL_CDF_access_required)) ) break; dom = op->domain; @@ -434,6 +434,8 @@ domcr_flags |= DOMCRF_s3_integrity; if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_oos_off ) domcr_flags |= DOMCRF_oos_off; + if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_access_required ) + domcr_flags |= DOMCRF_access_required; ret = -ENOMEM; d = domain_create(dom, domcr_flags, op->u.createdomain.ssidref); --- repo/xen/include/asm-x86/hvm/hvm.h 2011-01-02 14:01:19.240259617 -0800 +++ new/xen/include/asm-x86/hvm/hvm.h 2011-01-01 18:35:48.193395032 -0800 @@ -356,7 +356,10 @@ int hvm_debug_op(struct vcpu *v, int32_t op); -bool_t hvm_hap_nested_page_fault(unsigned long gfn); +bool_t hvm_hap_nested_page_fault(unsigned long gpa, + bool_t gla_valid, unsigned long gla, + bool_t access_r, bool_t access_w, + bool_t access_x); #define hvm_msr_tsc_aux(v) ({ \ struct domain *__d = (v)->domain; \ @@ -367,4 +370,12 @@ int hvm_x2apic_msr_read(struct vcpu *v, unsigned int msr, uint64_t *msr_content); int hvm_x2apic_msr_write(struct vcpu *v, unsigned int msr, uint64_t msr_content); +/* Called for current VCPU on crX changes by guest */ +int hvm_memory_event_cr0(unsigned long value, unsigned long old); +int hvm_memory_event_cr3(unsigned long value, unsigned long old); +int hvm_memory_event_cr4(unsigned long value, unsigned long old); + +/* Called for current VCPU on int3 */ +int hvm_memory_event_int3(unsigned long gla); + #endif /* __ASM_X86_HVM_HVM_H__ */ --- repo/xen/include/asm-x86/hvm/vcpu.h 2011-01-02 14:01:19.249536749 -0800 +++ new/xen/include/asm-x86/hvm/vcpu.h 2011-01-02 09:19:53.460776985 -0800 @@ -114,6 +114,11 @@ /* We may write up to m128 as a number of device-model transactions. */ paddr_t mmio_large_write_pa; unsigned int mmio_large_write_bytes; + + /* Pending hw/sw interrupt */ + int inject_trap; /* -1 for nothing to inject */ + int inject_error_code; + unsigned long inject_cr2; }; #endif /* __ASM_X86_HVM_VCPU_H__ */ --- repo/xen/include/asm-x86/hvm/vmx/vmx.h 2011-01-02 14:01:19.262075222 -0800 +++ new/xen/include/asm-x86/hvm/vmx/vmx.h 2010-12-29 16:31:09.619917089 -0800 @@ -42,7 +42,8 @@ rsvd2_snp : 1, /* bit 11 - Used for VT-d snoop control in shared EPT/VT-d usage */ mfn : 40, /* bits 51:12 - Machine physical frame number */ - sa_p2mt : 10, /* bits 61:52 - Software available 2 */ + sa_p2mt : 6, /* bits 57:52 - Software available 2 */ + access : 4, /* bits 61:58 - p2m_access_t */ rsvd3_tm : 1, /* bit 62 - Used for VT-d transient-mapping hint in shared EPT/VT-d usage */ avail3 : 1; /* bit 63 - Software available 3 */ --- repo/xen/include/asm-x86/mem_event.h 2011-01-02 14:01:19.272784100 -0800 +++ new/xen/include/asm-x86/mem_event.h 2010-12-29 16:31:09.619917089 -0800 @@ -24,6 +24,8 @@ #ifndef __MEM_EVENT_H__ #define __MEM_EVENT_H__ +/* Returns true if a listener exists, else pauses VCPU */ +int mem_event_check_listener(struct domain *d); int mem_event_check_ring(struct domain *d); void mem_event_put_request(struct domain *d, mem_event_request_t *req); void mem_event_get_response(struct domain *d, mem_event_response_t *rsp); --- repo/xen/include/asm-x86/p2m.h 2011-01-02 14:01:19.282783465 -0800 +++ new/xen/include/asm-x86/p2m.h 2010-12-29 16:31:09.639918203 -0800 @@ -88,6 +88,31 @@ p2m_ram_broken =14, /* Broken page, access cause domain crash */ } p2m_type_t; +/* + * 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 mem_event + * interface. + * + * The access permissions are soft state: when any ambigious 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 { + p2m_access_n = 0, /* No access permissions allowed */ + p2m_access_r = 1, + p2m_access_w = 2, + p2m_access_rw = 3, + p2m_access_x = 4, + p2m_access_rx = 5, + p2m_access_wx = 6, + p2m_access_rwx = 7, + p2m_access_rx2rw = 8, /* Special: page goes from RX to RW on write */ + + /* NOTE: Assumed to be only 4 bits right now */ +} p2m_access_t; + typedef enum { p2m_query = 0, /* Do not populate a PoD entries */ p2m_alloc = 1, /* Automatically populate PoD entries */ @@ -182,18 +207,30 @@ int (*set_entry )(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, unsigned int page_order, - p2m_type_t p2mt); + p2m_type_t p2mt, + p2m_access_t p2ma); mfn_t (*get_entry )(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *p2mt, + p2m_access_t *p2ma, p2m_query_t q); mfn_t (*get_entry_current)(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *p2mt, + p2m_access_t *p2ma, p2m_query_t q); void (*change_entry_type_global)(struct p2m_domain *p2m, p2m_type_t ot, p2m_type_t nt); + + /* Default P2M access type for each page in the the domain: new pages, + * swapped in pages, cleared pages, and pages that are ambiquously + * retyped get this access type. See definition of p2m_access_t. */ + p2m_access_t default_access; + + /* If true, and an access fault comes in and there is no mem_event listener, + * pause domain. Otherwise, remove access restrictions. */ + bool_t access_required; /* Highest guest frame that's ever been mapped in the p2m */ unsigned long max_mapped_pfn; @@ -284,9 +321,10 @@ /* Read the current domain's p2m table. Do not populate PoD pages. */ static inline mfn_t gfn_to_mfn_type_current(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *t, + p2m_access_t *a, p2m_query_t q) { - return p2m->get_entry_current(p2m, gfn, t, q); + return p2m->get_entry_current(p2m, gfn, t, a, q); } /* Read P2M table, mapping pages as we go. @@ -295,7 +333,8 @@ gfn_to_mfn_type_p2m(struct p2m_domain *p2m, unsigned long gfn, p2m_type_t *t, p2m_query_t q) { - return p2m->get_entry(p2m, gfn, t, q); + p2m_access_t a = 0; + return p2m->get_entry(p2m, gfn, t, &a, q); } @@ -305,6 +344,7 @@ p2m_query_t q) { mfn_t mfn; + p2m_access_t a; if ( !p2m || !paging_mode_translate(p2m->domain) ) { @@ -314,7 +354,7 @@ mfn = _mfn(gfn); } else if ( likely(current->domain == p2m->domain) ) - mfn = gfn_to_mfn_type_current(p2m, gfn, t, q); + mfn = gfn_to_mfn_type_current(p2m, gfn, t, &a, q); else mfn = gfn_to_mfn_type_p2m(p2m, gfn, t, q); @@ -382,7 +422,7 @@ } /* Init the datastructures for later use by the p2m code */ -int p2m_init(struct domain *d); +int p2m_init(struct domain *d, unsigned int domcr_flags); /* Allocate a new p2m table for a domain. * @@ -482,6 +522,14 @@ { } #endif +/* Send mem event based on the access (gla is -1ull if not available), + * return true if the event will be taken care of by a mem event listener. Handles + * rw2rx conversion */ +int p2m_mem_access_check(unsigned long gpa, bool_t gla_valid, unsigned long gla, + bool_t access_r, bool_t access_w, bool_t access_x); +/* Resumes the running of the VCPU, restarting the last instruction */ +void p2m_mem_access_resume(struct p2m_domain *p2m); + struct page_info *p2m_alloc_ptp(struct p2m_domain *p2m, unsigned long type); #endif /* _XEN_P2M_H */ --- repo/xen/include/public/domctl.h 2011-01-02 14:01:19.321697965 -0800 +++ new/xen/include/public/domctl.h 2010-12-29 16:31:09.579929790 -0800 @@ -47,17 +47,20 @@ uint32_t ssidref; xen_domain_handle_t handle; /* Is this an HVM guest (as opposed to a PV guest)? */ -#define _XEN_DOMCTL_CDF_hvm_guest 0 -#define XEN_DOMCTL_CDF_hvm_guest (1U<<_XEN_DOMCTL_CDF_hvm_guest) +#define _XEN_DOMCTL_CDF_hvm_guest 0 +#define XEN_DOMCTL_CDF_hvm_guest (1U<<_XEN_DOMCTL_CDF_hvm_guest) /* Use hardware-assisted paging if available? */ -#define _XEN_DOMCTL_CDF_hap 1 -#define XEN_DOMCTL_CDF_hap (1U<<_XEN_DOMCTL_CDF_hap) +#define _XEN_DOMCTL_CDF_hap 1 +#define XEN_DOMCTL_CDF_hap (1U<<_XEN_DOMCTL_CDF_hap) /* Should domain memory integrity be verifed by tboot during Sx? */ -#define _XEN_DOMCTL_CDF_s3_integrity 2 -#define XEN_DOMCTL_CDF_s3_integrity (1U<<_XEN_DOMCTL_CDF_s3_integrity) +#define _XEN_DOMCTL_CDF_s3_integrity 2 +#define XEN_DOMCTL_CDF_s3_integrity (1U<<_XEN_DOMCTL_CDF_s3_integrity) /* Disable out-of-sync shadow page tables? */ -#define _XEN_DOMCTL_CDF_oos_off 3 -#define XEN_DOMCTL_CDF_oos_off (1U<<_XEN_DOMCTL_CDF_oos_off) +#define _XEN_DOMCTL_CDF_oos_off 3 +#define XEN_DOMCTL_CDF_oos_off (1U<<_XEN_DOMCTL_CDF_oos_off) + /* Require mem_event listener for access; else pause */ +#define _XEN_DOMCTL_CDF_access_required 4 +#define XEN_DOMCTL_CDF_access_required (1U<<_XEN_DOMCTL_CDF_access_required) uint32_t flags; }; typedef struct xen_domctl_createdomain xen_domctl_createdomain_t; @@ -714,7 +717,7 @@ /* * Page memory in and out. */ -#define XEN_DOMCTL_MEM_EVENT_OP_PAGING (1 << 0) +#define XEN_DOMCTL_MEM_EVENT_OP_PAGING 1 /* Domain memory paging */ #define XEN_DOMCTL_MEM_EVENT_OP_PAGING_NOMINATE 0 @@ -722,6 +725,12 @@ #define XEN_DOMCTL_MEM_EVENT_OP_PAGING_PREP 2 #define XEN_DOMCTL_MEM_EVENT_OP_PAGING_RESUME 3 +/* + * Access permissions + */ +#define XEN_DOMCTL_MEM_EVENT_OP_ACCESS 2 +#define XEN_DOMCTL_MEM_EVENT_OP_ACCESS_RESUME 0 + struct xen_domctl_mem_event_op { uint32_t op; /* XEN_DOMCTL_MEM_EVENT_OP_* */ uint32_t mode; /* XEN_DOMCTL_MEM_EVENT_ENABLE_* */ --- repo/xen/include/public/hvm/hvm_op.h 2011-01-02 14:01:19.321697965 -0800 +++ new/xen/include/public/hvm/hvm_op.h 2011-01-02 10:54:13.457647887 -0800 @@ -158,4 +158,63 @@ typedef struct xen_hvm_xentrace xen_hvm_xentrace_t; DEFINE_XEN_GUEST_HANDLE(xen_hvm_xentrace_t); +#define HVMOP_set_mem_access 12 +typedef enum { + HVMMEM_access_n, + HVMMEM_access_r, + HVMMEM_access_w, + HVMMEM_access_rw, + HVMMEM_access_x, + HVMMEM_access_rx, + HVMMEM_access_wx, + HVMMEM_access_rwx, + HVMMEM_access_rx2rw, /* Page starts off as read-execute, but automatically change + * to read-write on a write */ + HVMMEM_access_default /* Take the domain default */ +} hvmmem_access_t; +/* Notify that a region of memory is to have specific access types */ +struct xen_hvm_set_mem_access { + /* Domain to be updated. */ + domid_t domid; + /* Memory type */ + hvmmem_access_t hvmmem_access; + /* First pfn, or ~0ull to set the default access for new pages */ + uint64_t first_pfn; + /* Number of pages, ignored on setting default access */ + uint64_t nr; +}; +typedef struct xen_hvm_set_mem_access xen_hvm_set_mem_access_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_mem_access_t); + +#define HVMOP_get_mem_access 13 +/* Get the specific access type for that region of memory */ +struct xen_hvm_get_mem_access { + /* Domain to be queried. */ + domid_t domid; + /* Memory type: OUT */ + hvmmem_access_t hvmmem_access; + /* pfn, or ~0ull for default access for new pages. IN */ + uint64_t pfn; +}; +typedef struct xen_hvm_get_mem_access xen_hvm_get_mem_access_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_mem_access_t); + +#define HVMOP_inject_trap 14 +/* Inject a trap into a VCPU, which will get taken up on the next + * scheduling of it */ +struct xen_hvm_inject_trap { + /* Domain to be queried. */ + domid_t domid; + /* VCPU */ + uint32_t vcpuid; + /* Trap number */ + uint32_t trap; + /* Error code, or -1 to skip */ + uint32_t error_code; + /* CR2 for page faults */ + uint64_t cr2; +}; +typedef struct xen_hvm_inject_trap xen_hvm_inject_trap_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_trap_t); + #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ --- repo/xen/include/public/hvm/params.h 2011-01-02 14:01:19.321697965 -0800 +++ new/xen/include/public/hvm/params.h 2011-01-01 18:32:21.519762490 -0800 @@ -124,6 +124,19 @@ */ #define HVM_PARAM_ACPI_IOPORTS_LOCATION 19 -#define HVM_NR_PARAMS 20 +/* Enable blocking memory events, async or sync (pause vcpu until response) + * onchangeonly indicates messages only on a change of value */ +#define HVM_PARAM_MEMORY_EVENT_CR0 20 +#define HVM_PARAM_MEMORY_EVENT_CR3 21 +#define HVM_PARAM_MEMORY_EVENT_CR4 22 +#define HVM_PARAM_MEMORY_EVENT_INT3 23 + +#define HVMPME_MODE_MASK (3 << 0) +#define HVMPME_mode_disabled 0 +#define HVMPME_mode_async 1 +#define HVMPME_mode_sync 2 +#define HVMPME_onchangeonly (1 << 2) + +#define HVM_NR_PARAMS 24 #endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ --- repo/xen/include/public/mem_event.h 2011-01-02 14:01:19.332783666 -0800 +++ new/xen/include/public/mem_event.h 2011-01-01 22:32:50.101693245 -0800 @@ -26,18 +26,44 @@ #include "xen.h" #include "io/ring.h" +/* Memory event type */ +#define MEM_EVENT_TYPE_SHARED 0 +#define MEM_EVENT_TYPE_PAGING 1 +#define MEM_EVENT_TYPE_ACCESS 2 + /* Memory event flags */ #define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0) +/* Reasons for the memory event request */ +#define MEM_EVENT_REASON_UNKNOWN 0 /* typical reason */ +#define MEM_EVENT_REASON_VIOLATION 1 /* access violation, GFN is address */ +#define MEM_EVENT_REASON_CR0 2 /* CR0 was hit: gfn is CR0 value */ +#define MEM_EVENT_REASON_CR3 3 /* CR3 was hit: gfn is CR3 value */ +#define MEM_EVENT_REASON_CR4 4 /* CR4 was hit: gfn is CR4 value */ +#define MEM_EVENT_REASON_INT3 5 /* int3 was hit: gla/gfn are RIP */ + typedef struct mem_event_shared_page { uint32_t port; } mem_event_shared_page_t; typedef struct mem_event_st { + uint16_t type; + uint16_t flags; + uint32_t vcpu_id; + uint64_t gfn; + uint64_t offset; + uint64_t gla; /* if gla_valid */ + uint32_t p2mt; - uint32_t vcpu_id; - uint64_t flags; + + uint16_t access_r:1; + uint16_t access_w:1; + uint16_t access_x:1; + uint16_t gla_valid:1; + uint16_t available:12; + + uint16_t reason; } mem_event_request_t, mem_event_response_t; DEFINE_RING_TYPES(mem_event, mem_event_request_t, mem_event_response_t); --- repo/xen/include/xen/sched.h 2011-01-02 14:01:19.389576534 -0800 +++ new/xen/include/xen/sched.h 2010-12-29 16:31:09.599918686 -0800 @@ -403,22 +403,24 @@ struct domain *domain_create( domid_t domid, unsigned int domcr_flags, ssidref_t ssidref); /* DOMCRF_hvm: Create an HVM domain, as opposed to a PV domain. */ -#define _DOMCRF_hvm 0 -#define DOMCRF_hvm (1U<<_DOMCRF_hvm) +#define _DOMCRF_hvm 0 +#define DOMCRF_hvm (1U<<_DOMCRF_hvm) /* DOMCRF_hap: Create a domain with hardware-assisted paging. */ -#define _DOMCRF_hap 1 -#define DOMCRF_hap (1U<<_DOMCRF_hap) +#define _DOMCRF_hap 1 +#define DOMCRF_hap (1U<<_DOMCRF_hap) /* DOMCRF_s3_integrity: Create a domain with tboot memory integrity protection by tboot */ -#define _DOMCRF_s3_integrity 2 -#define DOMCRF_s3_integrity (1U<<_DOMCRF_s3_integrity) +#define _DOMCRF_s3_integrity 2 +#define DOMCRF_s3_integrity (1U<<_DOMCRF_s3_integrity) /* DOMCRF_dummy: Create a dummy domain (not scheduled; not on domain list) */ -#define _DOMCRF_dummy 3 -#define DOMCRF_dummy (1U<<_DOMCRF_dummy) +#define _DOMCRF_dummy 3 +#define DOMCRF_dummy (1U<<_DOMCRF_dummy) /* DOMCRF_oos_off: dont use out-of-sync optimization for shadow page tables */ -#define _DOMCRF_oos_off 4 -#define DOMCRF_oos_off (1U<<_DOMCRF_oos_off) - +#define _DOMCRF_oos_off 4 +#define DOMCRF_oos_off (1U<<_DOMCRF_oos_off) +/* DOMCRF_access_required: mem_event listener required for access; else pause */ +#define _DOMCRF_access_required 5 +#define DOMCRF_access_required (1U<<_DOMCRF_access_required) /* * rcu_lock_domain_by_id() is more efficient than get_domain_by_id(). * This is the preferred function if the returned domain reference --- /dev/null 2011-01-02 12:53:14.150286069 -0800 +++ new/tools/libxc/xc_mem_access.c 2011-01-02 13:13:36.329120558 -0800 @@ -0,0 +1,42 @@ +/****************************************************************************** + * + * tools/libxc/xc_mem_access.c + * + * Interface to low-level memory access mode functionality + * + * Copyright (c) 2009 by Citrix Systems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xc_private.h" + + +int xc_mem_access_resume(xc_interface *xch, domid_t domain_id, unsigned long gfn) +{ + return xc_mem_event_control(xch, domain_id, + XEN_DOMCTL_MEM_EVENT_OP_ACCESS_RESUME, + XEN_DOMCTL_MEM_EVENT_OP_ACCESS, NULL, NULL, + gfn); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null 2011-01-02 12:53:14.150286069 -0800 +++ new/xen/arch/x86/mm/mem_access.c 2010-12-28 21:28:22.579732080 -0800 @@ -0,0 +1,59 @@ +/****************************************************************************** + * arch/x86/mm/mem_access.c + * + * Memory access support. + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * 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 + */ + + +#include +#include + + +int mem_access_domctl(struct domain *d, xen_domctl_mem_event_op_t *mec, + XEN_GUEST_HANDLE(void) u_domctl) +{ + int rc; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + switch( mec->op ) + { + case XEN_DOMCTL_MEM_EVENT_OP_ACCESS_RESUME: + { + p2m_mem_access_resume(p2m); + rc = 0; + } + break; + + default: + rc = -ENOSYS; + break; + } + + return rc; +} + + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null 2011-01-02 12:53:14.150286069 -0800 +++ new/xen/include/asm-x86/mem_access.h 2010-12-28 21:55:37.804285418 -0800 @@ -0,0 +1,35 @@ +/****************************************************************************** + * include/asm-x86/mem_paging.h + * + * Memory access support. + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * 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 + */ + + +int mem_access_domctl(struct domain *d, xen_domctl_mem_event_op_t *mec, + XEN_GUEST_HANDLE(void) u_domctl); + + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */