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

[Xen-devel] [PATCH v1 08/11] xsplice: Implement support for applying patches



Implement support for the apply, revert and replace actions.

To perform and action on a payload, the hypercall sets up a data
structure to schedule the work.  A hook is added in all the
return-to-guest paths to check for work to do and execute it if needed.
In this way, patches can be applied with all CPUs idle and without
stacks.  The first CPU to do_xsplice() becomes the master and triggers a
reschedule softirq to trigger all the other CPUs to enter do_xsplice()
with no stack.  Once all CPUs have rendezvoused, all CPUs disable IRQs
and NMIs are ignored. The system is then quiscient and the master
performs the action.  After this, all CPUs enable IRQs and NMIs are
re-enabled.

The action to perform is one of:
- APPLY: For each function in the module, store the first 5 bytes of the
  old function and replace it with a jump to the new function.
- REVERT: Copy the previously stored bytes into the first 5 bytes of the
  old function.
- REPLACE: Revert each applied module and then apply the new module.

To prevent a deadlock with any other barrier in the system, the master
will wait for up to 30ms before timing out.  I've taken some
measurements and found the patch application to take about 100 Îs on a
72 CPU system, whether idle or fully loaded.

Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
---
 xen/arch/arm/xsplice.c      |   8 ++
 xen/arch/x86/domain.c       |   4 +
 xen/arch/x86/hvm/svm/svm.c  |   2 +
 xen/arch/x86/hvm/vmx/vmcs.c |   2 +
 xen/arch/x86/xsplice.c      |  19 ++++
 xen/common/xsplice.c        | 264 ++++++++++++++++++++++++++++++++++++++++++--
 xen/include/asm-arm/nmi.h   |  13 +++
 xen/include/xen/xsplice.h   |   7 +-
 8 files changed, 306 insertions(+), 13 deletions(-)

diff --git a/xen/arch/arm/xsplice.c b/xen/arch/arm/xsplice.c
index 8d85fa9..3c34eb3 100644
--- a/xen/arch/arm/xsplice.c
+++ b/xen/arch/arm/xsplice.c
@@ -3,6 +3,14 @@
 #include <xen/xsplice_elf.h>
 #include <xen/xsplice.h>
 
+void xsplice_apply_jmp(struct xsplice_patch_func *func)
+{
+}
+
+void xsplice_revert_jmp(struct xsplice_patch_func *func)
+{
+}
+
 int xsplice_verify_elf(uint8_t *data, ssize_t len)
 {
     return -ENOSYS;
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index fe3be30..4420cfc 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -36,6 +36,7 @@
 #include <xen/cpu.h>
 #include <xen/wait.h>
 #include <xen/guest_access.h>
+#include <xen/xsplice.h>
 #include <public/sysctl.h>
 #include <asm/regs.h>
 #include <asm/mc146818rtc.h>
@@ -120,6 +121,7 @@ static void idle_loop(void)
         (*pm_idle)();
         do_tasklet();
         do_softirq();
+        do_xsplice();
     }
 }
 
@@ -136,6 +138,7 @@ void startup_cpu_idle_loop(void)
 
 static void noreturn continue_idle_domain(struct vcpu *v)
 {
+    do_xsplice();
     reset_stack_and_jump(idle_loop);
 }
 
@@ -143,6 +146,7 @@ static void noreturn continue_nonidle_domain(struct vcpu *v)
 {
     check_wakeup_from_wait();
     mark_regs_dirty(guest_cpu_user_regs());
+    do_xsplice();
     reset_stack_and_jump(ret_from_intr);
 }
 
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 8de41fa..65bf7e9 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -26,6 +26,7 @@
 #include <xen/hypercall.h>
 #include <xen/domain_page.h>
 #include <xen/xenoprof.h>
+#include <xen/xsplice.h>
 #include <asm/current.h>
 #include <asm/io.h>
 #include <asm/paging.h>
@@ -1071,6 +1072,7 @@ static void noreturn svm_do_resume(struct vcpu *v)
 
     hvm_do_resume(v);
 
+    do_xsplice();
     reset_stack_and_jump(svm_asm_do_resume);
 }
 
diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c
index 4ea1ad1..d996f47 100644
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -25,6 +25,7 @@
 #include <xen/kernel.h>
 #include <xen/keyhandler.h>
 #include <xen/vm_event.h>
+#include <xen/xsplice.h>
 #include <asm/current.h>
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
@@ -1685,6 +1686,7 @@ void vmx_do_resume(struct vcpu *v)
     }
 
     hvm_do_resume(v);
+    do_xsplice();
     reset_stack_and_jump(vmx_asm_do_vmentry);
 }
 
diff --git a/xen/arch/x86/xsplice.c b/xen/arch/x86/xsplice.c
index dbff0d5..31e4124 100644
--- a/xen/arch/x86/xsplice.c
+++ b/xen/arch/x86/xsplice.c
@@ -3,6 +3,25 @@
 #include <xen/xsplice_elf.h>
 #include <xen/xsplice.h>
 
+#define PATCH_INSN_SIZE 5
+
+void xsplice_apply_jmp(struct xsplice_patch_func *func)
+{
+    uint32_t val;
+    uint8_t *old_ptr;
+
+    old_ptr = (uint8_t *)func->old_addr;
+    memcpy(func->undo, old_ptr, PATCH_INSN_SIZE);
+    *old_ptr++ = 0xe9; /* Relative jump */
+    val = func->new_addr - func->old_addr - PATCH_INSN_SIZE;
+    memcpy(old_ptr, &val, sizeof val);
+}
+
+void xsplice_revert_jmp(struct xsplice_patch_func *func)
+{
+    memcpy((void *)func->old_addr, func->undo, PATCH_INSN_SIZE);
+}
+
 int xsplice_verify_elf(uint8_t *data, ssize_t len)
 {
 
diff --git a/xen/common/xsplice.c b/xen/common/xsplice.c
index 5e88c55..4476be5 100644
--- a/xen/common/xsplice.c
+++ b/xen/common/xsplice.c
@@ -11,16 +11,21 @@
 #include <xen/guest_access.h>
 #include <xen/stdbool.h>
 #include <xen/sched.h>
+#include <xen/softirq.h>
 #include <xen/lib.h>
+#include <xen/wait.h>
 #include <xen/xsplice_elf.h>
 #include <xen/xsplice.h>
 #include <public/sysctl.h>
 
 #include <asm/event.h>
+#include <asm/nmi.h>
 
 static DEFINE_SPINLOCK(payload_list_lock);
 static LIST_HEAD(payload_list);
 
+static LIST_HEAD(applied_list);
+
 static unsigned int payload_cnt;
 static unsigned int payload_version = 1;
 
@@ -29,15 +34,34 @@ struct payload {
     int32_t rc;         /* 0 or -EXX. */
 
     struct list_head   list;   /* Linked to 'payload_list'. */
+    struct list_head   applied_list;   /* Linked to 'applied_list'. */
 
+    struct xsplice_patch_func *funcs;
+    int nfuncs;
     void *module_address;
     size_t module_pages;
 
     char  id[XEN_XSPLICE_NAME_SIZE + 1];          /* Name of it. */
 };
 
+/* Defines an outstanding patching action. */
+struct xsplice_work
+{
+    atomic_t semaphore;          /* Used for rendezvous */
+    atomic_t irq_semaphore;      /* Used to signal all IRQs disabled */
+    struct payload *data;        /* The payload on which to act */
+    volatile bool_t do_work;     /* Signals work to do */
+    volatile bool_t ready;       /* Signals all CPUs synchronized */
+    uint32_t cmd;                /* Action request. XSPLICE_ACTION_* */
+};
+
+static DEFINE_SPINLOCK(xsplice_work_lock);
+/* There can be only one outstanding patching action. */
+static struct xsplice_work xsplice_work;
+
 static int load_module(struct payload *payload, uint8_t *raw, ssize_t len);
 static void free_module(struct payload *payload);
+static int schedule_work(struct payload *data, uint32_t cmd);
 
 static const char *state2str(int32_t state)
 {
@@ -341,28 +365,22 @@ static int xsplice_action(xen_sysctl_xsplice_action_t 
*action)
     case XSPLICE_ACTION_REVERT:
         if ( data->state == XSPLICE_STATE_APPLIED )
         {
-            /* No implementation yet. */
-            data->state = XSPLICE_STATE_CHECKED;
-            data->rc = 0;
-            rc = 0;
+            data->rc = -EAGAIN;
+            rc = schedule_work(data, action->cmd);
         }
         break;
     case XSPLICE_ACTION_APPLY:
         if ( (data->state == XSPLICE_STATE_CHECKED) )
         {
-            /* No implementation yet. */
-            data->state = XSPLICE_STATE_APPLIED;
-            data->rc = 0;
-            rc = 0;
+            data->rc = -EAGAIN;
+            rc = schedule_work(data, action->cmd);
         }
         break;
     case XSPLICE_ACTION_REPLACE:
         if ( data->state == XSPLICE_STATE_CHECKED )
         {
-            /* No implementation yet. */
-            data->state = XSPLICE_STATE_CHECKED;
-            data->rc = 0;
-            rc = 0;
+            data->rc = -EAGAIN;
+            rc = schedule_work(data, action->cmd);
         }
         break;
     default:
@@ -637,6 +655,24 @@ static int perform_relocs(struct xsplice_elf *elf)
     return 0;
 }
 
+static int find_special_sections(struct payload *payload,
+                                 struct xsplice_elf *elf)
+{
+    struct xsplice_elf_sec *sec;
+
+    sec = xsplice_elf_sec_by_name(elf, ".xsplice.funcs");
+    if ( !sec )
+    {
+        printk(XENLOG_ERR ".xsplice.funcs is missing\n");
+        return -1;
+    }
+
+    payload->funcs = (struct xsplice_patch_func *)sec->load_addr;
+    payload->nfuncs = sec->sec->sh_size / (sizeof *payload->funcs);
+
+    return 0;
+}
+
 static int load_module(struct payload *payload, uint8_t *raw, ssize_t len)
 {
     struct xsplice_elf elf;
@@ -662,6 +698,10 @@ static int load_module(struct payload *payload, uint8_t 
*raw, ssize_t len)
     if ( rc )
         goto err_module;
 
+    rc = find_special_sections(payload, &elf);
+    if ( rc )
+        goto err_module;
+
     return 0;
 
  err_module:
@@ -672,6 +712,206 @@ static int load_module(struct payload *payload, uint8_t 
*raw, ssize_t len)
     return rc;
 }
 
+
+/*
+ * The following functions get the CPUs into an appropriate state and
+ * apply (or revert) each of the module's functions.
+ */
+
+/*
+ * This function is executed having all other CPUs with no stack and IRQs
+ * disabled.
+ */
+static int apply_payload(struct payload *data)
+{
+    int i;
+
+    printk(XENLOG_DEBUG "Applying payload: %s\n", data->id);
+
+    for ( i = 0; i < data->nfuncs; i++ )
+        xsplice_apply_jmp(data->funcs + i);
+
+    list_add_tail(&data->applied_list, &applied_list);
+
+    return 0;
+}
+
+/*
+ * This function is executed having all other CPUs with no stack and IRQs
+ * disabled.
+ */
+static int revert_payload(struct payload *data)
+{
+    int i;
+
+    printk(XENLOG_DEBUG "Reverting payload: %s\n", data->id);
+
+    for ( i = 0; i < data->nfuncs; i++ )
+        xsplice_revert_jmp(data->funcs + i);
+
+    list_del(&data->applied_list);
+
+    return 0;
+}
+
+/* Must be holding the payload_list lock */
+static int schedule_work(struct payload *data, uint32_t cmd)
+{
+    /* Fail if an operation is already scheduled */
+    if ( xsplice_work.do_work )
+        return -EAGAIN;
+
+    xsplice_work.cmd = cmd;
+    xsplice_work.data = data;
+    atomic_set(&xsplice_work.semaphore, 0);
+    atomic_set(&xsplice_work.irq_semaphore, 0);
+    xsplice_work.ready = false;
+    smp_mb();
+    xsplice_work.do_work = true;
+    smp_mb();
+
+    return 0;
+}
+
+static int mask_nmi_callback(const struct cpu_user_regs *regs, int cpu)
+{
+    return 1;
+}
+
+static void reschedule_fn(void *unused)
+{
+    smp_mb(); /* Synchronize with setting do_work */
+    raise_softirq(SCHEDULE_SOFTIRQ);
+}
+
+/*
+ * The main function which manages the work of quiescing the system and
+ * patching code.
+ */
+void do_xsplice(void)
+{
+    int id;
+    unsigned int total_cpus;
+    nmi_callback_t saved_nmi_callback;
+
+    /* Fast path: no work to do */
+    if ( likely(!xsplice_work.do_work) )
+        return;
+
+    ASSERT(local_irq_is_enabled());
+
+    spin_lock(&xsplice_work_lock);
+    id = atomic_read(&xsplice_work.semaphore);
+    atomic_inc(&xsplice_work.semaphore);
+    spin_unlock(&xsplice_work_lock);
+
+    total_cpus = num_online_cpus();
+
+    if ( id == 0 )
+    {
+        s_time_t timeout, start;
+
+        /* Trigger other CPUs to execute do_xsplice */
+        smp_call_function(reschedule_fn, NULL, 0);
+
+        /* Wait for other CPUs with a timeout */
+        start = NOW();
+        timeout = start + MILLISECS(30);
+        while ( atomic_read(&xsplice_work.semaphore) != total_cpus &&
+                NOW() < timeout )
+            cpu_relax();
+
+        if ( atomic_read(&xsplice_work.semaphore) == total_cpus )
+        {
+            struct payload *data2;
+
+            /* "Mask" NMIs */
+            saved_nmi_callback = set_nmi_callback(mask_nmi_callback);
+
+            /* All CPUs are waiting, now signal to disable IRQs */
+            xsplice_work.ready = true;
+            smp_mb();
+
+            /* Wait for irqs to be disabled */
+            while ( atomic_read(&xsplice_work.irq_semaphore) != (total_cpus - 
1) )
+                cpu_relax();
+
+            local_irq_disable();
+            /* Now this function should be the only one on any stack.
+             * No need to lock the payload list or applied list. */
+            switch ( xsplice_work.cmd )
+            {
+                case XSPLICE_ACTION_APPLY:
+                        xsplice_work.data->rc = 
apply_payload(xsplice_work.data);
+                        if ( xsplice_work.data->rc == 0 )
+                            xsplice_work.data->state = XSPLICE_STATE_APPLIED;
+                        break;
+                case XSPLICE_ACTION_REVERT:
+                        xsplice_work.data->rc = 
revert_payload(xsplice_work.data);
+                        if ( xsplice_work.data->rc == 0 )
+                            xsplice_work.data->state = XSPLICE_STATE_CHECKED;
+                        break;
+                case XSPLICE_ACTION_REPLACE:
+                        list_for_each_entry ( data2, &payload_list, list )
+                        {
+                            if ( data2->state != XSPLICE_STATE_APPLIED )
+                                continue;
+
+                            data2->rc = revert_payload(data2);
+                            if ( data2->rc == 0 )
+                                data2->state = XSPLICE_STATE_CHECKED;
+                            else
+                            {
+                                xsplice_work.data->rc = -EINVAL;
+                                break;
+                            }
+                        }
+                        if ( xsplice_work.data->rc != -EINVAL )
+                        {
+                            xsplice_work.data->rc = 
apply_payload(xsplice_work.data);
+                            if ( xsplice_work.data->rc == 0 )
+                                xsplice_work.data->state = 
XSPLICE_STATE_APPLIED;
+                        }
+                        break;
+                default:
+                        xsplice_work.data->rc = -EINVAL;
+                        break;
+            }
+
+            local_irq_enable();
+            set_nmi_callback(saved_nmi_callback);
+        }
+        else
+        {
+            xsplice_work.data->rc = -EBUSY;
+        }
+
+        xsplice_work.do_work = 0;
+        smp_mb(); /* Synchronize with waiting CPUs */
+    }
+    else
+    {
+        /* Wait for all CPUs to rendezvous */
+        while ( xsplice_work.do_work && !xsplice_work.ready )
+        {
+            cpu_relax();
+            smp_mb();
+        }
+
+        /* Disable IRQs and signal */
+        local_irq_disable();
+        atomic_inc(&xsplice_work.irq_semaphore);
+
+        /* Wait for patching to complete */
+        while ( xsplice_work.do_work )
+        {
+            cpu_relax();
+            smp_mb();
+        }
+        local_irq_enable();
+    }
+}
+
 static int __init xsplice_init(void)
 {
     register_keyhandler('x', xsplice_printall, "print xsplicing info", 1);
diff --git a/xen/include/asm-arm/nmi.h b/xen/include/asm-arm/nmi.h
index a60587e..82aff35 100644
--- a/xen/include/asm-arm/nmi.h
+++ b/xen/include/asm-arm/nmi.h
@@ -4,6 +4,19 @@
 #define register_guest_nmi_callback(a)  (-ENOSYS)
 #define unregister_guest_nmi_callback() (-ENOSYS)
 
+typedef int (*nmi_callback_t)(const struct cpu_user_regs *regs, int cpu);
+
+/**
+ * set_nmi_callback
+ *
+ * Set a handler for an NMI. Only one handler may be
+ * set. Return the old nmi callback handler.
+ */
+static inline nmi_callback_t set_nmi_callback(nmi_callback_t callback)
+{
+    return NULL;
+}
+
 #endif /* ASM_NMI_H */
 /*
  * Local variables:
diff --git a/xen/include/xen/xsplice.h b/xen/include/xen/xsplice.h
index a3946a3..507829c 100644
--- a/xen/include/xen/xsplice.h
+++ b/xen/include/xen/xsplice.h
@@ -11,7 +11,8 @@ struct xsplice_patch_func {
     unsigned long old_addr;
     unsigned long old_size;
     char *name;
-    unsigned char undo[8];
+    uint8_t undo[8];
+    uint8_t pad[56];
 };
 
 struct xen_sysctl_xsplice_op;
@@ -19,6 +20,8 @@ int xsplice_control(struct xen_sysctl_xsplice_op *);
 
 extern void xsplice_printall(unsigned char key);
 
+void do_xsplice(void);
+
 /* Arch hooks */
 int xsplice_verify_elf(uint8_t *data, ssize_t len);
 int xsplice_perform_rel(struct xsplice_elf *elf,
@@ -27,5 +30,7 @@ int xsplice_perform_rel(struct xsplice_elf *elf,
 int xsplice_perform_rela(struct xsplice_elf *elf,
                          struct xsplice_elf_sec *base,
                          struct xsplice_elf_sec *rela);
+void xsplice_apply_jmp(struct xsplice_patch_func *func);
+void xsplice_revert_jmp(struct xsplice_patch_func *func);
 
 #endif /* __XEN_XSPLICE_H__ */
-- 
2.4.3


_______________________________________________
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®.