diff mbox series

[2/3] livepatch: add a dummy hypercall for testing purposes

Message ID 20231123112338.14477-3-roger.pau@citrix.com (mailing list archive)
State New, archived
Headers show
Series automation: add livepatch testing | expand

Commit Message

Roger Pau Monné Nov. 23, 2023, 11:23 a.m. UTC
Introduce a dummy XEN_SYSCTL_LIVEPATCH_TEST hypercall to be used in order to
test livepatch functionality.  The hypercall fills a value in the passed
structure, which is returned to the caller.

The xen-livepatch utility is expanded to allow calling that hypercall, and
printing the returned value on stdout.

Finally, add dummy patch that changes the returned value of the hypercall from
1 to 2.  Such patch can be used with livepatch-build-tools in order to generate
a livepatch payload, that when applied to the hypervisor change the printed
value of `xen-livepatch test`.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
The whole logic is very simple now.  I think it's enough to have a skeleton we
can later expand.

Unsure whether we should do some kind of test (with `patch -F0`) that the patch
still applies cleanly as part of Xen build.
---
 tools/include/xenctrl.h                |  3 +++
 tools/libs/ctrl/xc_misc.c              | 14 ++++++++++++++
 tools/misc/xen-livepatch.c             | 25 +++++++++++++++++++++++++
 xen/common/Makefile                    |  2 +-
 xen/common/livepatch-test.c            | 20 ++++++++++++++++++++
 xen/common/livepatch.c                 |  4 ++++
 xen/include/public/sysctl.h            |  7 +++++++
 xen/include/xen/livepatch.h            |  4 ++++
 xen/test/livepatch/patches/test1.patch | 13 +++++++++++++
 9 files changed, 91 insertions(+), 1 deletion(-)
 create mode 100644 xen/common/livepatch-test.c
 create mode 100644 xen/test/livepatch/patches/test1.patch

Comments

Andrew Cooper Nov. 23, 2023, 4:56 p.m. UTC | #1
On 23/11/2023 11:23 am, Roger Pau Monne wrote:
> Introduce a dummy XEN_SYSCTL_LIVEPATCH_TEST hypercall to be used in order to
> test livepatch functionality.  The hypercall fills a value in the passed
> structure, which is returned to the caller.
>
> The xen-livepatch utility is expanded to allow calling that hypercall, and
> printing the returned value on stdout.
>
> Finally, add dummy patch that changes the returned value of the hypercall from
> 1 to 2.  Such patch can be used with livepatch-build-tools in order to generate
> a livepatch payload, that when applied to the hypervisor change the printed
> value of `xen-livepatch test`.
>
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> ---
> The whole logic is very simple now.  I think it's enough to have a skeleton we
> can later expand.
>
> Unsure whether we should do some kind of test (with `patch -F0`) that the patch
> still applies cleanly as part of Xen build.

Thanks for looking into this.  I think one tweak towards the larger plan
might help here.

When I envisaged this originally, it was something along the lines of
test_self_modify_code() which would be called on boot after alternatives
were called, which would sanity check the result of certain simple cases.

Then, for livepatching, I was thinking of something like this:

obj-y += test_smc.o
targets-y += test_smc_alt.o

i.e. we have test_smc.c and test_smc_alt.c which are two slightly
different copies of the same thing, and we compile both but don't link
the second one in.

Then, we can diff the two C files in order to make the patch to build as
a livepatch.  This way we're not maintaining a patch committed into the
tree, which I suspect will make everyone's lives easier.  I suspect in
practice that we'll want test_smc_asm.S pairs too.

Not necessarily for now, but I was also thinking we'd have a test stage
where we know exactly what the livepatch ought to look like, so we audit
the eventual livepatch elf that it has all the expected differences
encoded, and no extraneous ones.

Finally, I was thinking that the hypercall would (re)run
test_self_modify_code().  I'm on the fence about making it part of the
livepatch infrastructure, given that the nature of the test is wider,
but I can't think of any case that we'd be wanting runtime self
modifying code (e.g. rerun alternatives after ucode load) which isn't
linked to a new livepatch to begin with.

Thoughts?

~Andrew
diff mbox series

Patch

diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h
index 2ef8b4e05422..83a00d4974dd 100644
--- a/tools/include/xenctrl.h
+++ b/tools/include/xenctrl.h
@@ -2645,6 +2645,9 @@  int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout, uint32_
 int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
 int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
 
+/* Dummy hypercall to test livepatch functionality. */
+int xc_livepatch_test(xc_interface *xch, uint32_t *result);
+
 /*
  * Ensure cache coherency after memory modifications. A call to this function
  * is only required on ARM as the x86 architecture provides cache coherency
diff --git a/tools/libs/ctrl/xc_misc.c b/tools/libs/ctrl/xc_misc.c
index 5ecdfa2c7934..0ca86a53d097 100644
--- a/tools/libs/ctrl/xc_misc.c
+++ b/tools/libs/ctrl/xc_misc.c
@@ -1021,6 +1021,20 @@  int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout, uint32
     return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REPLACE, timeout, flags);
 }
 
+int xc_livepatch_test(xc_interface *xch, uint32_t *result)
+{
+    struct xen_sysctl sysctl = {
+        .cmd = XEN_SYSCTL_livepatch_op,
+        .u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_TEST,
+    };
+    int rc = do_sysctl(xch, &sysctl);
+
+    if ( !rc )
+        *result = sysctl.u.livepatch.u.test.result;
+
+    return rc;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/misc/xen-livepatch.c b/tools/misc/xen-livepatch.c
index 5bf9d9a32b65..5f6fd20d8814 100644
--- a/tools/misc/xen-livepatch.c
+++ b/tools/misc/xen-livepatch.c
@@ -37,6 +37,7 @@  void show_help(void)
             "  replace <name>         apply <name> patch and revert all others.\n"
             "  unload <name>          unload name <name> patch.\n"
             "  load <file> [flags]    upload and apply <file> with name as the <file> name\n"
+            "  test                   print the result of the test hypercall (for testing purposes only)\n"
             "    Supported flags:\n"
             "      --nodeps           Disable inter-module buildid dependency check.\n"
             "                         Check only against hypervisor buildid.\n",
@@ -542,6 +543,29 @@  error:
     return rc;
 }
 
+static int test_func(int argc, char *argv[])
+{
+    int rc;
+    uint32_t result = 0;
+
+    if ( argc != 0 )
+    {
+        show_help();
+        return -1;
+    }
+
+    rc = xc_livepatch_test(xch, &result);
+    if ( rc )
+    {
+        fprintf(stderr, "test operation failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    printf("%u\n", result);
+
+    return 0;
+}
+
 /*
  * These are also functions in action_options that are called in case
  * none of the ones in main_options match.
@@ -554,6 +578,7 @@  struct {
     { "list", list_func },
     { "upload", upload_func },
     { "load", load_func },
+    { "test", test_func },
 };
 
 int main(int argc, char *argv[])
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 69d6aa626c7f..ab073d41f1d2 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -23,7 +23,7 @@  obj-y += kernel.o
 obj-y += keyhandler.o
 obj-$(CONFIG_KEXEC) += kexec.o
 obj-$(CONFIG_KEXEC) += kimage.o
-obj-$(CONFIG_LIVEPATCH) += livepatch.o livepatch_elf.o
+obj-$(CONFIG_LIVEPATCH) += livepatch.o livepatch_elf.o livepatch-test.o
 obj-$(CONFIG_MEM_ACCESS) += mem_access.o
 obj-y += memory.o
 obj-y += multicall.o
diff --git a/xen/common/livepatch-test.c b/xen/common/livepatch-test.c
new file mode 100644
index 000000000000..05b638b2ac67
--- /dev/null
+++ b/xen/common/livepatch-test.c
@@ -0,0 +1,20 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Dummy file for testing livepatch functionality. */
+#include <xen/livepatch.h>
+
+int livepatch_test(struct xen_sysctl_livepatch_test *test)
+{
+    test->result = 1;
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
index 1209fea2566c..e8894db1cc93 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -2116,6 +2116,10 @@  int livepatch_op(struct xen_sysctl_livepatch_op *livepatch)
         rc = livepatch_action(&livepatch->u.action);
         break;
 
+    case XEN_SYSCTL_LIVEPATCH_TEST:
+        rc = livepatch_test(&livepatch->u.test);
+        break;
+
     default:
         rc = -EOPNOTSUPP;
         break;
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index 9b19679caeb1..9c13a7fdb22c 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -1137,6 +1137,12 @@  struct xen_sysctl_livepatch_action {
     uint32_t pad;                           /* IN: Always zero. */
 };
 
+/* Dummy hypercall for testing live patches. */
+#define XEN_SYSCTL_LIVEPATCH_TEST 4
+struct xen_sysctl_livepatch_test {
+    uint32_t result;                        /* OUT: dummy result for testing. */
+};
+
 struct xen_sysctl_livepatch_op {
     uint32_t cmd;                           /* IN: XEN_SYSCTL_LIVEPATCH_*. */
     uint32_t pad;                           /* IN: Always zero. */
@@ -1145,6 +1151,7 @@  struct xen_sysctl_livepatch_op {
         struct xen_sysctl_livepatch_list list;
         struct xen_sysctl_livepatch_get get;
         struct xen_sysctl_livepatch_action action;
+        struct xen_sysctl_livepatch_test test;
     } u;
 };
 
diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
index df339a134e40..60d11d037dfb 100644
--- a/xen/include/xen/livepatch.h
+++ b/xen/include/xen/livepatch.h
@@ -11,6 +11,8 @@  struct livepatch_elf_sec;
 struct livepatch_elf_sym;
 struct xen_sysctl_livepatch_op;
 
+#include <xen/types.h> /* For elfstructs.h */
+
 #include <xen/elfstructs.h>
 #include <xen/errno.h> /* For -ENOSYS or -EOVERFLOW */
 
@@ -165,6 +167,8 @@  static inline void common_livepatch_revert(const struct livepatch_func *func,
     arch_livepatch_revert(func, state);
     state->applied = LIVEPATCH_FUNC_NOT_APPLIED;
 }
+
+int livepatch_test(struct xen_sysctl_livepatch_test *test);
 #else
 
 /*
diff --git a/xen/test/livepatch/patches/test1.patch b/xen/test/livepatch/patches/test1.patch
new file mode 100644
index 000000000000..c07d697cc8de
--- /dev/null
+++ b/xen/test/livepatch/patches/test1.patch
@@ -0,0 +1,13 @@ 
+diff --git a/xen/common/livepatch-test.c b/xen/common/livepatch-test.c
+index 05b638b2ac..876173ab6f 100644
+--- a/xen/common/livepatch-test.c
++++ b/xen/common/livepatch-test.c
+@@ -5,7 +5,7 @@
+ 
+ int livepatch_test(struct xen_sysctl_livepatch_test *test)
+ {
+-    test->result = 1;
++    test->result = 2;
+     return 0;
+ }
+