[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] vm_event: Implement ARM SMC events



From: Tamas K Lengyel <tklengyel@xxxxxxxxxxxxx>

The ARM SMC instructions are already configured to trap to Xen by default. In
this patch we allow a user-space process in a privileged domain to receive
notification of when such event happens through the vm_event subsystem.

This patch will likely needs to be broken up into several smaller patches.
Right now what this patch adds (and could be broken into smaller patches
accordingly):
    - Implement monitor_op domctl handler for SOFTWARE_BREAKPOINTs on ARM
    - Implement vm_event register fill/set routines for ARM. This required
        removing the function from common as the function prototype now
        differs on the two archs.
    - Sending notification as SOFTWARE_BREAKPOINT vm_event from the SMC trap
        handlers.
    - Extend the xen-access test tool to receive SMC notification and step
        the PC manually in the reply.

I'm sending it as an RFC to gather feedback on what has been overlooked in this
revision. This patch has been tested on a Cubietruck board and works fine,
but would probably not work on 64-bit boards.

Example output:

domu# insmod smc.ko
[   59.518258] Issuing SMC! Function @ bf000000

dom0# ./xen-access 1 breakpoint
xenaccess init
max_gpfn = 48000
starting breakpoint 1
Got event from Xen
Got event from Xen
SMC: pc=bf000020 gfn=bf000 (vcpu 0)

Signed-off-by: Tamas K Lengyel <tamas@xxxxxxxxxxxxx>
Cc: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Cc: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
Cc: Wei Liu <wei.liu2@xxxxxxxxxx>
Cc: Julien Grall <julien.grall@xxxxxxx>
Cc: Keir Fraser <keir@xxxxxxx>
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
 tools/tests/xen-access/xen-access.c | 15 ++++--
 xen/arch/arm/Makefile               |  2 +
 xen/arch/arm/monitor.c              | 65 +++++++++++++++++++++++++
 xen/arch/arm/traps.c                | 20 +++++++-
 xen/arch/arm/vm_event.c             | 95 +++++++++++++++++++++++++++++++++++++
 xen/arch/x86/hvm/event.c            |  2 +
 xen/common/vm_event.c               |  1 -
 xen/include/asm-arm/domain.h        |  5 ++
 xen/include/asm-arm/monitor.h       | 15 +-----
 xen/include/asm-arm/vm_event.h      | 18 +++----
 xen/include/asm-x86/vm_event.h      |  4 +-
 xen/include/public/vm_event.h       | 26 ++++++++++
 12 files changed, 235 insertions(+), 33 deletions(-)
 create mode 100644 xen/arch/arm/monitor.c
 create mode 100644 xen/arch/arm/vm_event.c

diff --git a/tools/tests/xen-access/xen-access.c 
b/tools/tests/xen-access/xen-access.c
index ef89246..a1cde82 100644
--- a/tools/tests/xen-access/xen-access.c
+++ b/tools/tests/xen-access/xen-access.c
@@ -333,7 +333,7 @@ void usage(char* progname)
 {
     fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
 #if defined(__i386__) || defined(__x86_64__)
-            fprintf(stderr, "|breakpoint|altp2m_write|altp2m_exec");
+            fprintf(stderr, "|altp2m_write|altp2m_exec");
 #endif
             fprintf(stderr,
             "\n"
@@ -397,11 +397,11 @@ int main(int argc, char *argv[])
         default_access = XENMEM_access_rw;
         after_first_access = XENMEM_access_rwx;
     }
-#if defined(__i386__) || defined(__x86_64__)
     else if ( !strcmp(argv[0], "breakpoint") )
     {
         breakpoint = 1;
     }
+#if defined(__i386__) || defined(__x86_64__)
     else if ( !strcmp(argv[0], "altp2m_write") )
     {
         default_access = XENMEM_access_rx;
@@ -634,7 +634,8 @@ int main(int argc, char *argv[])
                 rsp.u.mem_access = req.u.mem_access;
                 break;
             case VM_EVENT_REASON_SOFTWARE_BREAKPOINT:
-                printf("Breakpoint: rip=%016"PRIx64", gfn=%"PRIx64" (vcpu 
%d)\n",
+#if defined(__i386__) || defined(__x86_64__)
+                printf("Breakpoint: rip=%"PRIx64" gfn=%"PRIx64" (vcpu %d)\n",
                        req.data.regs.x86.rip,
                        req.u.software_breakpoint.gfn,
                        req.vcpu_id);
@@ -649,7 +650,15 @@ int main(int argc, char *argv[])
                     interrupted = -1;
                     continue;
                 }
+#elif defined(__arm__) || defined(__aarch64__)
+                printf("SMC: pc=%"PRIx64" (vcpu %d)\n",
+                       req.data.regs.arm.pc,
+                       req.vcpu_id);
 
+                rsp.data.regs.arm = req.data.regs.arm;
+                rsp.data.regs.arm.pc += 4;
+                rsp.flags |= VM_EVENT_FLAG_SET_REGISTERS;
+#endif
                 break;
             case VM_EVENT_REASON_SINGLESTEP:
                 printf("Singlestep: rip=%016"PRIx64", vcpu %d\n",
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 0328b50..118be99 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -40,6 +40,8 @@ obj-y += device.o
 obj-y += decode.o
 obj-y += processor.o
 obj-y += smc.o
+obj-y += monitor.o
+obj-y += vm_event.o
 
 #obj-bin-y += ....o
 
diff --git a/xen/arch/arm/monitor.c b/xen/arch/arm/monitor.c
new file mode 100644
index 0000000..49bfddb
--- /dev/null
+++ b/xen/arch/arm/monitor.c
@@ -0,0 +1,65 @@
+/*
+ * arch/x86/monitor.c
+ *
+ * Arch-specific monitor_op domctl handler.
+ *
+ * Copyright (c) 2015-2016 Tamas K Lengyel (tamas@xxxxxxxxxxxxx)
+ * Copyright (c) 2016, Bitdefender S.R.L.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <asm/monitor.h>
+#include <public/vm_event.h>
+
+int arch_monitor_domctl_event(struct domain *d,
+                              struct xen_domctl_monitor_op *mop)
+{
+    struct arch_domain *ad = &d->arch;
+    bool_t requested_status = (XEN_DOMCTL_MONITOR_OP_ENABLE == mop->op);
+
+    switch ( mop->event )
+    {
+    case XEN_DOMCTL_MONITOR_EVENT_SOFTWARE_BREAKPOINT:
+    {
+        bool_t old_status = ad->monitor.software_breakpoint_enabled;
+
+        if ( unlikely(old_status == requested_status) )
+            return -EEXIST;
+
+        domain_pause(d);
+        ad->monitor.software_breakpoint_enabled = requested_status;
+        domain_unpause(d);
+        break;
+    }
+
+    default:
+        /*
+         * Should not be reached unless vm_event_monitor_get_capabilities() is
+         * not properly implemented.
+         */
+        ASSERT_UNREACHABLE();
+        return -EOPNOTSUPP;
+    }
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 83744e8..9c018c9 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -41,6 +41,7 @@
 #include <asm/mmio.h>
 #include <asm/cpufeature.h>
 #include <asm/flushtlb.h>
+#include <asm/vm_event.h>
 
 #include "decode.h"
 #include "vtimer.h"
@@ -2449,6 +2450,21 @@ bad_data_abort:
     inject_dabt_exception(regs, info.gva, hsr.len);
 }
 
+static void do_trap_smc(struct cpu_user_regs *regs)
+{
+    int rc = 0;
+    if ( current->domain->arch.monitor.software_breakpoint_enabled )
+    {
+        rc = vm_event_smc(regs);
+    }
+
+    if ( rc != 1 )
+    {
+        GUEST_BUG_ON(!psr_mode_is_32bit(regs->cpsr));
+        inject_undef32_exception(regs);
+    }
+}
+
 static void enter_hypervisor_head(struct cpu_user_regs *regs)
 {
     if ( guest_mode(regs) )
@@ -2524,7 +2540,7 @@ asmlinkage void do_trap_hypervisor(struct cpu_user_regs 
*regs)
          */
         GUEST_BUG_ON(!psr_mode_is_32bit(regs->cpsr));
         perfc_incr(trap_smc32);
-        inject_undef32_exception(regs);
+        do_trap_smc(regs);
         break;
     case HSR_EC_HVC32:
         GUEST_BUG_ON(!psr_mode_is_32bit(regs->cpsr));
@@ -2557,7 +2573,7 @@ asmlinkage void do_trap_hypervisor(struct cpu_user_regs 
*regs)
          */
         GUEST_BUG_ON(psr_mode_is_32bit(regs->cpsr));
         perfc_incr(trap_smc64);
-        inject_undef64_exception(regs, hsr.len);
+        do_trap_smc(regs);
         break;
     case HSR_EC_SYSREG:
         GUEST_BUG_ON(psr_mode_is_32bit(regs->cpsr));
diff --git a/xen/arch/arm/vm_event.c b/xen/arch/arm/vm_event.c
new file mode 100644
index 0000000..d997313
--- /dev/null
+++ b/xen/arch/arm/vm_event.c
@@ -0,0 +1,95 @@
+/*
+ * arch/arm/vm_event.c
+ *
+ * Architecture-specific vm_event handling routines
+ *
+ * Copyright (c) 2015 Tamas K Lengyel (tamas@xxxxxxxxxxxxx)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/sched.h>
+#include <asm/vm_event.h>
+
+static inline
+void vm_event_fill_regs(vm_event_request_t *req,
+                        const struct cpu_user_regs *regs)
+{
+    req->data.regs.arm.r0 = regs->r0;
+    req->data.regs.arm.r1 = regs->r1;
+    req->data.regs.arm.r2 = regs->r2;
+    req->data.regs.arm.r3 = regs->r3;
+    req->data.regs.arm.r4 = regs->r4;
+    req->data.regs.arm.r5 = regs->r5;
+    req->data.regs.arm.r6 = regs->r6;
+    req->data.regs.arm.r7 = regs->r7;
+    req->data.regs.arm.r8 = regs->r8;
+    req->data.regs.arm.r9 = regs->r9;
+    req->data.regs.arm.r10 = regs->r10;
+    req->data.regs.arm.r11 = regs->r11;
+    req->data.regs.arm.r12 = regs->r12;
+    req->data.regs.arm.sp = regs->sp;
+    req->data.regs.arm.lr = regs->lr;
+    req->data.regs.arm.pc = regs->pc;
+    req->data.regs.arm.cpsr = regs->cpsr;
+    req->data.regs.arm.ttbr0 = READ_SYSREG64(TTBR0_EL1);
+    req->data.regs.arm.ttbr1 = READ_SYSREG64(TTBR1_EL1);
+}
+
+void vm_event_set_registers(struct vcpu *v, vm_event_response_t *rsp)
+{
+    v->arch.cpu_info->guest_cpu_user_regs.r0 = rsp->data.regs.arm.r0;
+    v->arch.cpu_info->guest_cpu_user_regs.r1 = rsp->data.regs.arm.r1;
+    v->arch.cpu_info->guest_cpu_user_regs.r2 = rsp->data.regs.arm.r2;
+    v->arch.cpu_info->guest_cpu_user_regs.r3 = rsp->data.regs.arm.r3;
+    v->arch.cpu_info->guest_cpu_user_regs.r4 = rsp->data.regs.arm.r4;
+    v->arch.cpu_info->guest_cpu_user_regs.r5 = rsp->data.regs.arm.r5;
+    v->arch.cpu_info->guest_cpu_user_regs.r6 = rsp->data.regs.arm.r6;
+    v->arch.cpu_info->guest_cpu_user_regs.r7 = rsp->data.regs.arm.r7;
+    v->arch.cpu_info->guest_cpu_user_regs.r8 = rsp->data.regs.arm.r8;
+    v->arch.cpu_info->guest_cpu_user_regs.r9 = rsp->data.regs.arm.r9;
+    v->arch.cpu_info->guest_cpu_user_regs.r10 = rsp->data.regs.arm.r10;
+    v->arch.cpu_info->guest_cpu_user_regs.r11 = rsp->data.regs.arm.r11;
+    v->arch.cpu_info->guest_cpu_user_regs.r12 = rsp->data.regs.arm.r12;
+    v->arch.cpu_info->guest_cpu_user_regs.sp = rsp->data.regs.arm.sp;
+    v->arch.cpu_info->guest_cpu_user_regs.lr = rsp->data.regs.arm.lr;
+    v->arch.cpu_info->guest_cpu_user_regs.pc = rsp->data.regs.arm.pc;
+    v->arch.cpu_info->guest_cpu_user_regs.cpsr = rsp->data.regs.arm.cpsr;
+    v->arch.ttbr0 = rsp->data.regs.arm.ttbr0;
+    v->arch.ttbr1 = rsp->data.regs.arm.ttbr1;
+}
+
+int vm_event_smc(const struct cpu_user_regs *regs) {
+    struct vcpu *curr = current;
+    struct arch_domain *ad = &curr->domain->arch;
+    vm_event_request_t req = { 0 };
+
+    if ( !ad->monitor.software_breakpoint_enabled )
+        return 0;
+
+    req.reason = VM_EVENT_REASON_SOFTWARE_BREAKPOINT;
+    req.vcpu_id = curr->vcpu_id;
+
+    vm_event_fill_regs(&req, regs);
+
+    return vm_event_monitor_traps(curr, 1, &req);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/x86/hvm/event.c b/xen/arch/x86/hvm/event.c
index 56c5514..f7d1418 100644
--- a/xen/arch/x86/hvm/event.c
+++ b/xen/arch/x86/hvm/event.c
@@ -47,6 +47,7 @@ bool_t hvm_event_cr(unsigned int index, unsigned long value, 
unsigned long old)
             .u.write_ctrlreg.old_value = old
         };
 
+        vm_event_fill_regs(&req);
         vm_event_monitor_traps(curr, sync, &req);
         return 1;
     }
@@ -115,6 +116,7 @@ int hvm_event_breakpoint(unsigned long rip,
     }
 
     req.vcpu_id = curr->vcpu_id;
+    vm_event_fill_regs(&req);
 
     return vm_event_monitor_traps(curr, 1, &req);
 }
diff --git a/xen/common/vm_event.c b/xen/common/vm_event.c
index 2906407..a29bda8 100644
--- a/xen/common/vm_event.c
+++ b/xen/common/vm_event.c
@@ -818,7 +818,6 @@ int vm_event_monitor_traps(struct vcpu *v, uint8_t sync,
         req->altp2m_idx = altp2m_vcpu_idx(v);
     }
 
-    vm_event_fill_regs(req);
     vm_event_put_request(d, &d->vm_event->monitor, req);
 
     return 1;
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index c35ed40..b2eb101 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -129,6 +129,11 @@ struct arch_domain
     paddr_t efi_acpi_gpa;
     paddr_t efi_acpi_len;
 #endif
+
+    /* Monitor options */
+    struct {
+        uint8_t software_breakpoint_enabled : 1;
+    } monitor;
 }  __cacheline_aligned;
 
 struct arch_vcpu
diff --git a/xen/include/asm-arm/monitor.h b/xen/include/asm-arm/monitor.h
index 6e36e99..e1c2ff2 100644
--- a/xen/include/asm-arm/monitor.h
+++ b/xen/include/asm-arm/monitor.h
@@ -3,7 +3,7 @@
  *
  * Arch-specific monitor_op domctl handler.
  *
- * Copyright (c) 2015 Tamas K Lengyel (tamas@xxxxxxxxxxxxx)
+ * Copyright (c) 2015-2016 Tamas K Lengyel (tamas@xxxxxxxxxxxxx)
  * Copyright (c) 2016, Bitdefender S.R.L.
  *
  * This program is free software; you can redistribute it and/or
@@ -32,18 +32,7 @@ int arch_monitor_domctl_op(struct domain *d, struct 
xen_domctl_monitor_op *mop)
     return -EOPNOTSUPP;
 }
 
-static inline
 int arch_monitor_domctl_event(struct domain *d,
-                              struct xen_domctl_monitor_op *mop)
-{
-    /*
-     * No arch-specific monitor vm-events on ARM.
-     *
-     * Should not be reached unless vm_event_monitor_get_capabilities() is not
-     * properly implemented.
-     */
-    ASSERT_UNREACHABLE();
-    return -EOPNOTSUPP;
-}
+                              struct xen_domctl_monitor_op *mop);
 
 #endif /* __ASM_ARM_MONITOR_H__ */
diff --git a/xen/include/asm-arm/vm_event.h b/xen/include/asm-arm/vm_event.h
index 014d9ba..e4526fc 100644
--- a/xen/include/asm-arm/vm_event.h
+++ b/xen/include/asm-arm/vm_event.h
@@ -1,7 +1,7 @@
 /*
  * vm_event.h: architecture specific vm_event handling routines
  *
- * Copyright (c) 2015 Tamas K Lengyel (tamas@xxxxxxxxxxxxx)
+ * Copyright (c) 2015-2016 Tamas K Lengyel (tamas@xxxxxxxxxxxxx)
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -48,24 +48,18 @@ void vm_event_register_write_resume(struct vcpu *v, 
vm_event_response_t *rsp)
     /* Not supported on ARM. */
 }
 
-static inline
-void vm_event_set_registers(struct vcpu *v, vm_event_response_t *rsp)
-{
-    /* Not supported on ARM. */
-}
-
-static inline void vm_event_fill_regs(vm_event_request_t *req)
-{
-    /* Not supported on ARM. */
-}
+void vm_event_set_registers(struct vcpu *v, vm_event_response_t *rsp);
 
 static inline uint32_t vm_event_monitor_get_capabilities(struct domain *d)
 {
     uint32_t capabilities = 0;
 
-    capabilities = (1U << XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST);
+    capabilities = (1U << XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST |
+                    1U << XEN_DOMCTL_MONITOR_EVENT_SOFTWARE_BREAKPOINT);
 
     return capabilities;
 }
 
+int vm_event_smc(const struct cpu_user_regs *regs);
+
 #endif /* __ASM_ARM_VM_EVENT_H__ */
diff --git a/xen/include/asm-x86/vm_event.h b/xen/include/asm-x86/vm_event.h
index 0470240..de7816f 100644
--- a/xen/include/asm-x86/vm_event.h
+++ b/xen/include/asm-x86/vm_event.h
@@ -42,8 +42,6 @@ void vm_event_register_write_resume(struct vcpu *v, 
vm_event_response_t *rsp);
 
 void vm_event_set_registers(struct vcpu *v, vm_event_response_t *rsp);
 
-void vm_event_fill_regs(vm_event_request_t *req);
-
 static inline uint32_t vm_event_monitor_get_capabilities(struct domain *d)
 {
     uint32_t capabilities = 0;
@@ -67,4 +65,6 @@ static inline uint32_t 
vm_event_monitor_get_capabilities(struct domain *d)
     return capabilities;
 }
 
+void vm_event_fill_regs(vm_event_request_t *req);
+
 #endif /* __ASM_X86_VM_EVENT_H__ */
diff --git a/xen/include/public/vm_event.h b/xen/include/public/vm_event.h
index 9270d52..5858711 100644
--- a/xen/include/public/vm_event.h
+++ b/xen/include/public/vm_event.h
@@ -166,6 +166,31 @@ struct vm_event_regs_x86 {
     uint32_t _pad;
 };
 
+struct vm_event_regs_arm {
+    uint32_t r0;
+    uint32_t r1;
+    uint32_t r2;
+    uint32_t r3;
+    uint32_t r4;
+    uint32_t r5;
+    uint32_t r6;
+    uint32_t r7;
+    uint32_t r8;
+    uint32_t r9;
+    uint32_t r10;
+    uint32_t r11;
+    uint32_t r12;
+
+    uint32_t sp; /* r13 - SP: Valid for Hyp. frames only, o/w banked (see 
below) */
+    uint32_t lr; /* r14 - LR: Valid for Hyp. Same physical register as lr_usr. 
*/
+
+    uint32_t cpsr; /* Return mode */
+    uint64_t pc;
+    uint64_t ttbr0;
+    uint64_t ttbr1;
+    uint32_t _pad;
+};
+
 /*
  * mem_access flag definitions
  *
@@ -254,6 +279,7 @@ typedef struct vm_event_st {
     union {
         union {
             struct vm_event_regs_x86 x86;
+            struct vm_event_regs_arm arm;
         } regs;
 
         struct vm_event_emul_read_data emul_read_data;
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.