]> xenbits.xensource.com Git - people/tklengyel/xen.git/commitdiff
x86/monitor: Add new monitor event to catch all vmexits vm_forking_ng3
authorTamas K Lengyel <tamas.lengyel@intel.com>
Fri, 11 Mar 2022 20:43:55 +0000 (20:43 +0000)
committerTamas K Lengyel <tamas.lengyel@intel.com>
Wed, 6 Apr 2022 14:45:54 +0000 (10:45 -0400)
Add monitor event that hooks the vmexit handler allowing for both sync and
async monitoring of events. With async monitoring an event is placed on the
monitor ring for each exit and the rest of the vmexit handler resumes normally.
If there are additional monitor events configured those will also place their
respective events on the monitor ring. With the sync version an event is placed
on the monitor ring but the handler does not get resumed, thus the sync version
is only useful when the VM is not expected to resume normally after the vmexit.
Our use-case is primarily with the sync version with VM forks where the fork
gets reset after sync vmexit event, thus the rest of the vmexit handler can be
safely skipped.

Signed-off-by: Tamas K Lengyel <tamas.lengyel@intel.com>
---
Note: making the sync version resume-friendly is specifically out-of-scope as
it would require significant rearrangement of the vmexit handler. As this
feature is not required for our use-case we opt for the version that minimizes
impact on the existing code.

tools/include/xenctrl.h
tools/libs/ctrl/xc_monitor.c
xen/arch/x86/hvm/monitor.c
xen/arch/x86/hvm/vmx/vmx.c
xen/arch/x86/include/asm/domain.h
xen/arch/x86/include/asm/hvm/monitor.h
xen/arch/x86/include/asm/monitor.h
xen/arch/x86/monitor.c
xen/include/public/domctl.h
xen/include/public/vm_event.h

index 1b089a2c0270e080c9caf9657e720396d2fd6844..159eaac050f23a8868f4005e110540e9e58871f7 100644 (file)
@@ -2096,6 +2096,8 @@ int xc_monitor_privileged_call(xc_interface *xch, uint32_t domain_id,
                                bool enable);
 int xc_monitor_emul_unimplemented(xc_interface *xch, uint32_t domain_id,
                                   bool enable);
+int xc_monitor_vmexit(xc_interface *xch, uint32_t domain_id, bool enable,
+                      bool sync);
 /**
  * This function enables / disables emulation for each REP for a
  * REP-compatible instruction.
index 4ac823e775656ae825138fea71af4392c1d8b5a0..c5fa62ff30d2bc3107e52ed70242644d182730fc 100644 (file)
@@ -246,6 +246,21 @@ int xc_monitor_emul_unimplemented(xc_interface *xch, uint32_t domain_id,
     return do_domctl(xch, &domctl);
 }
 
+int xc_monitor_vmexit(xc_interface *xch, uint32_t domain_id, bool enable,
+                      bool sync)
+{
+    DECLARE_DOMCTL;
+
+    domctl.cmd = XEN_DOMCTL_monitor_op;
+    domctl.domain = domain_id;
+    domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
+                                    : XEN_DOMCTL_MONITOR_OP_DISABLE;
+    domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_VMEXIT;
+    domctl.u.monitor_op.u.vmexit.sync = sync;
+
+    return do_domctl(xch, &domctl);
+}
+
 /*
  * Local variables:
  * mode: C
index b44a1e1dfee8430ef84a9bf9c84222ba8e4e1323..d68fbffafe23c8fbed708e1e108358e47d2af468 100644 (file)
@@ -328,6 +328,24 @@ bool hvm_monitor_check_p2m(unsigned long gla, gfn_t gfn, uint32_t pfec,
     return monitor_traps(curr, true, &req) >= 0;
 }
 
+int hvm_monitor_vmexit(unsigned long exit_reason,
+                       unsigned long exit_qualification)
+{
+    struct vcpu *curr = current;
+    struct arch_domain *ad = &curr->domain->arch;
+    vm_event_request_t req = {};
+
+    ASSERT(ad->monitor.vmexit_enabled);
+
+    req.reason = VM_EVENT_REASON_VMEXIT;
+    req.u.vmexit.reason = exit_reason;
+    req.u.vmexit.qualification = exit_qualification;
+
+    set_npt_base(curr, &req);
+
+    return monitor_traps(curr, !!ad->monitor.vmexit_sync, &req);
+}
+
 /*
  * Local variables:
  * mode: C
index c075370f642ad898fca9c55be9a8f94f369a7431..149db9db83193717e20d37fb377e2e566565e224 100644 (file)
@@ -4008,6 +4008,14 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
         }
     }
 
+    if ( unlikely(currd->arch.monitor.vmexit_enabled) )
+    {
+        __vmread(EXIT_QUALIFICATION, &exit_qualification);
+
+        if ( hvm_monitor_vmexit(exit_reason, exit_qualification) )
+            return;
+    }
+
     /* XXX: This looks ugly, but we need a mechanism to ensure
      * any pending vmresume has really happened
      */
index e62e109598014529d642ee5efd0426626287c3a7..855db352c0623a9fe385038533977fe80e0726f0 100644 (file)
@@ -430,6 +430,8 @@ struct arch_domain
          */
         unsigned int inguest_pagefault_disabled                            : 1;
         unsigned int control_register_values                               : 1;
+        unsigned int vmexit_enabled                                        : 1;
+        unsigned int vmexit_sync                                           : 1;
         struct monitor_msr_bitmap *msr_bitmap;
         uint64_t write_ctrlreg_mask[4];
     } monitor;
index a75cd8545cef8a9b6ec2ed1048a7dadd0ac1158d..639f6dfa374cf18b9f9088bd45007c1962eb452f 100644 (file)
@@ -51,6 +51,8 @@ bool hvm_monitor_emul_unimplemented(void);
 
 bool hvm_monitor_check_p2m(unsigned long gla, gfn_t gfn, uint32_t pfec,
                            uint16_t kind);
+int hvm_monitor_vmexit(unsigned long exit_reason,
+                       unsigned long exit_qualification);
 
 #endif /* __ASM_X86_HVM_MONITOR_H__ */
 
index 01c6d63bb9c9e9a01528103fbd5f7c640bdac54d..d8d54c5f23d691fd1af049680552b11fd7b2267f 100644 (file)
@@ -89,7 +89,8 @@ static inline uint32_t arch_monitor_get_capabilities(struct domain *d)
                     (1U << XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION) |
                     (1U << XEN_DOMCTL_MONITOR_EVENT_WRITE_CTRLREG) |
                     (1U << XEN_DOMCTL_MONITOR_EVENT_EMUL_UNIMPLEMENTED) |
-                    (1U << XEN_DOMCTL_MONITOR_EVENT_INGUEST_PAGEFAULT));
+                    (1U << XEN_DOMCTL_MONITOR_EVENT_INGUEST_PAGEFAULT) |
+                    (1U << XEN_DOMCTL_MONITOR_EVENT_VMEXIT));
 
     if ( hvm_is_singlestep_supported() )
         capabilities |= (1U << XEN_DOMCTL_MONITOR_EVENT_SINGLESTEP);
index 3079726a8bb02bb0945339b920003ea7e11b4272..30ca71432c0b822dea660b7ee391b79ed2951f76 100644 (file)
@@ -332,6 +332,20 @@ int arch_monitor_domctl_event(struct domain *d,
         break;
     }
 
+    case XEN_DOMCTL_MONITOR_EVENT_VMEXIT:
+    {
+        bool old_status = ad->monitor.vmexit_enabled;
+
+        if ( unlikely(old_status == requested_status) )
+            return -EEXIST;
+
+        domain_pause(d);
+        ad->monitor.vmexit_enabled = requested_status;
+        ad->monitor.vmexit_sync = mop->u.vmexit.sync;
+        domain_unpause(d);
+        break;
+    }
+
     default:
         /*
          * Should not be reached unless arch_monitor_get_capabilities() is
index b85e6170b0aa9ca104b70cc59ef02fdd5c637946..4803ed7afc538d36581ece5d9d82082d0b3b3144 100644 (file)
@@ -1057,6 +1057,7 @@ struct xen_domctl_psr_cmt_op {
 #define XEN_DOMCTL_MONITOR_EVENT_EMUL_UNIMPLEMENTED    10
 /* Enabled by default */
 #define XEN_DOMCTL_MONITOR_EVENT_INGUEST_PAGEFAULT     11
+#define XEN_DOMCTL_MONITOR_EVENT_VMEXIT                12
 
 struct xen_domctl_monitor_op {
     uint32_t op; /* XEN_DOMCTL_MONITOR_OP_* */
@@ -1107,6 +1108,11 @@ struct xen_domctl_monitor_op {
             /* Pause vCPU until response */
             uint8_t sync;
         } debug_exception;
+
+        struct {
+            /* Send event and don't process vmexit */
+            uint8_t sync;
+        } vmexit;
     } u;
 };
 
index 81c2ee28ccd0a2832102c452eed09b1c1b09b6d1..07f106f81184be123a83952a54ff5b4977a7ceef 100644 (file)
 #define VM_EVENT_REASON_DESCRIPTOR_ACCESS       13
 /* Current instruction is not implemented by the emulator */
 #define VM_EVENT_REASON_EMUL_UNIMPLEMENTED      14
+/* VMEXIT */
+#define VM_EVENT_REASON_VMEXIT                  15
 
 /* Supported values for the vm_event_write_ctrlreg index. */
 #define VM_EVENT_X86_CR0    0
@@ -394,6 +396,11 @@ struct vm_event_emul_insn_data {
     uint8_t data[16]; /* Has to be completely filled */
 };
 
+struct vm_event_vmexit {
+    uint64_t reason;
+    uint64_t qualification;
+};
+
 typedef struct vm_event_st {
     uint32_t version;   /* VM_EVENT_INTERFACE_VERSION */
     uint32_t flags;     /* VM_EVENT_FLAG_* */
@@ -414,6 +421,7 @@ typedef struct vm_event_st {
         struct vm_event_debug                 software_breakpoint;
         struct vm_event_debug                 debug_exception;
         struct vm_event_cpuid                 cpuid;
+        struct vm_event_vmexit                vmexit;
         union {
             struct vm_event_interrupt_x86     x86;
         } interrupt;