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

[RFC 5/5] xen/x86: livepatch: enable attaching callbacks


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Ryo Takakura <takakura@xxxxxxxxxxxxx>
  • Date: Fri, 23 Oct 2026 13:58:08 +0900
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=valinux.co.jp; dmarc=pass action=none header.from=valinux.co.jp; dkim=pass header.d=valinux.co.jp; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ZlKIeSunT1jOHyfhh+rhjOJdcYlWVJgHKK+gdl4jasA=; b=cXXVvr4QzT4SqdvSuI7MxlMU9DLQP00OJW2OcRmkfleHL/BpJZmUsVoueFhz/SZ1zFsIkFq/OvL0Xi6lTgk4denuQDONU8e8FID7lNxg+p+l53byenjkJRDNkxrMAd99b8hhU6UKhfQS1kVo2FfqrdowzpzSLVFvyJfLu5DCpbyM7nugYZSAkgPXP+uFjVWQ2F/PqHkmoQekBYjK2oLqCHNUAuWdHqvGSSCSxAUND2duHQLar9t0t/TZLj2hBTwShW4wBl0iFg1hkwDS/MQRtWefTjw1kXKTlO77BDNH3crCo8HDTv5YQhKl8empmB/RC5jTz1WOmI51D7yDH/7VJA==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=E+dj1esqJJFrUPP9yd6LpRA4OnidB04JDaYWQFpTTpLR4pxjxYKKZ6GmOo0TFYalLyfXdMbe8fjXDgwieA/v764r/26517ItxO7NobmlKgBNPjWG5ja7vpjSSaaBbLXougpV7cFmBb6qVwfXzRyHAdl1sAFrYkpOi/DlWX+Tde8AkxPd/7fGYoh7OfUQCPOfBsdUJgCeqwRA/mqsqjjC7SDjhiXYLlQFISzYaeH2SNAtivZROnPBmnenp1+m5d0c1Qk0bm15CjJk04GJfS1JtBsEFPRonZFrokikU7WXrDG6rh8IBlgfGoshZB9GOyXC9/l2Zfb2umTCLKdnfBlbRQ==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=valinux.co.jp header.i="@valinux.co.jp" header.h="From:Date:Subject:Message-Id:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=valinux.co.jp;
  • Cc: roger@xxxxxxxxxxxxxx, ross.lagerwall@xxxxxxxxxx, sstabellini@xxxxxxxxxx, julien@xxxxxxx, bertrand.marquis@xxxxxxx, michal.orzel@xxxxxxx, Volodymyr_Babchuk@xxxxxxxx, andrew.cooper3@xxxxxxxxxx, anthony.perard@xxxxxxxxxx, jbeulich@xxxxxxxx, teddy.astie@xxxxxxxxxx, Ryo Takakura <takakura@xxxxxxxxxxxxx>
  • Delivery-date: Fri, 23 Oct 2026 04:58:32 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

Add x86 support for attaching callbacks following
the ARM64 implementation.

It reserves function entry(5 NOPs) and replaces them with:

    call    xen_livepatch_trace_caller

xen_livepatch_trace_caller serves as a trampoline for saving
registers before calling xen_livepatch_trace_dispatcher where
callbacks are invoked. Callbacks receive:

    ip: Address of the traced function
    parent_ip: Address where the traced function was called

One can request the feature by specifying the section
for struct livepatch_func as .livepatch.traces.

This patch will also let x86 use the livepatch tracer.

Signed-off-by: Ryo Takakura <takakura@xxxxxxxxxxxxx>
---
 xen/arch/arm/arm64/livepatch.c |  5 +++
 xen/arch/x86/Kconfig           | 13 ++++++++
 xen/arch/x86/Makefile          |  1 +
 xen/arch/x86/arch.mk           |  3 ++
 xen/arch/x86/livepatch-asm.S   | 60 ++++++++++++++++++++++++++++++++++
 xen/arch/x86/livepatch.c       | 33 +++++++++++++++++++
 xen/arch/x86/xen.lds.S         |  5 +++
 xen/common/hypfs_trace.c       |  4 ++-
 xen/common/livepatch.c         |  2 +-
 xen/include/xen/livepatch.h    |  1 +
 10 files changed, 125 insertions(+), 2 deletions(-)
 create mode 100644 xen/arch/x86/livepatch-asm.S

diff --git a/xen/arch/arm/arm64/livepatch.c b/xen/arch/arm/arm64/livepatch.c
index 17753f4463..b4ae381ef5 100644
--- a/xen/arch/arm/arm64/livepatch.c
+++ b/xen/arch/arm/arm64/livepatch.c
@@ -497,6 +497,11 @@ int arch_livepatch_perform_rela(struct livepatch_elf *elf,
 }
 
 #ifdef CONFIG_ARM64_PATCHABLE_FUNCTION_ENTRY
+unsigned long arch_livepatch_trace_entry(unsigned long addr)
+{
+    return addr;
+}
+
 void arch_livepatch_apply_trace(const struct livepatch_func *trace)
 {
     uint32_t *new_ptr;
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index e5535ac484..bfb6a4d78e 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -355,6 +355,19 @@ config MICROCODE_SCAN_DEFAULT
          Enable if you have a Linux-based dom0 with microcode attached to the
          initramfs.
 
+config X86_PATCHABLE_FUNCTION_ENTRY
+       bool "Reserve five NOPs at function entry for tracing"
+       depends on LIVEPATCH && $(cc-option,-fpatchable-function-entry=5)
+       select HAS_LIVEPATCH_TRACE
+       default n
+       help
+         This option compiles Xen with -fpatchable-function-entry=5, which
+         reserves five NOP instructions at the beginning of functions. The NOPs
+         will be livepatched with a branch instruction to callback functions
+         registered by user.
+
+         Enable this to use livepatch tracing feature.
+
 endmenu
 
 source "common/Kconfig"
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 2be90ea098..06b39b7530 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_PV) += ioport_emulate.o
 obj-y += io_apic.o
 obj-y += irq.o
 obj-$(CONFIG_LIVEPATCH) += livepatch.o
+obj-$(CONFIG_X86_PATCHABLE_FUNCTION_ENTRY) += livepatch-asm.o
 obj-$(CONFIG_KEXEC) += machine_kexec.o
 obj-y += mm.o x86_64/mm.o
 obj-$(CONFIG_VM_EVENT) += monitor.o
diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk
index cd0602a79a..31af1a15c4 100644
--- a/xen/arch/x86/arch.mk
+++ b/xen/arch/x86/arch.mk
@@ -129,6 +129,9 @@ endif
 # Set up the assembler include path properly for older toolchains.
 CFLAGS += -Wa,-I$(objtree)/include -Wa,-I$(srctree)/include
 
+# Reserve entries for livepatch tracing
+CFLAGS-$(CONFIG_X86_PATCHABLE_FUNCTION_ENTRY) += -fpatchable-function-entry=5
+
 # The GNU assembler will interpret '/' as a comment start marker instead of a
 # divide for some ELF targets.  Pass --divide when when available to signal '/'
 # is always used as an operator in assembly.
diff --git a/xen/arch/x86/livepatch-asm.S b/xen/arch/x86/livepatch-asm.S
new file mode 100644
index 0000000000..005765b476
--- /dev/null
+++ b/xen/arch/x86/livepatch-asm.S
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <asm/asm_defns.h>
+
+        .text
+
+/*
+ * xen/arch/x86/livepatch-asm.S
+ *
+ * Trampoline for livepatch tracer.
+ *
+ * If livepatch tracer is enabled, function entries are placed
+ * with five NOPs. arch_livepatch_apply_trace() will be livepatching
+ * those NOPs to:
+ *
+ *     CALL      xen_livepatch_trace_caller
+ *
+ * 0(%rsp): traced function + 5
+ * 8(%rsp): return address to the traced function's caller
+ */
+FUNC(xen_livepatch_trace_caller)
+        pushq   %rbp
+        movq    %rsp, %rbp
+        pushfq
+
+        /* Preserve general-purpose registers. */
+        pushq   %rax
+        pushq   %rcx
+        pushq   %rdx
+        pushq   %rsi
+        pushq   %rdi
+        pushq   %r8
+        pushq   %r9
+        pushq   %r10
+        pushq   %r11
+
+        /*
+         * xen_livepatch_trace_dispatcher(ip, parent_ip)
+         *
+         * ip: Address of the traced function.
+         * parent_ip: Address where the traced function was called.
+         */
+        movq    8(%rbp), %rdi
+        subq    $5, %rdi
+        movq    16(%rbp), %rsi
+        call    xen_livepatch_trace_dispatcher
+
+        /* Restore the saved state. */
+        popq    %r11
+        popq    %r10
+        popq    %r9
+        popq    %r8
+        popq    %rdi
+        popq    %rsi
+        popq    %rdx
+        popq    %rcx
+        popq    %rax
+        popfq
+        popq    %rbp
+        ret
+END(xen_livepatch_trace_caller)
diff --git a/xen/arch/x86/livepatch.c b/xen/arch/x86/livepatch.c
index 5158e91f7e..1dfcc3e750 100644
--- a/xen/arch/x86/livepatch.c
+++ b/xen/arch/x86/livepatch.c
@@ -19,6 +19,10 @@
 #include <asm/nmi.h>
 #include <asm/setup.h>
 
+#ifdef CONFIG_X86_PATCHABLE_FUNCTION_ENTRY
+extern void xen_livepatch_trace_caller(void);
+#endif
+
 static bool has_active_waitqueue(const struct vm_event_domain *ved)
 {
     /* ved may be xzalloc()'d without INIT_LIST_HEAD() yet. */
@@ -365,6 +369,35 @@ int arch_livepatch_secure(const void *va, unsigned int 
pages, enum va_type type)
     return modify_xen_mappings(start, start + pages * PAGE_SIZE, flag);
 }
 
+#ifdef CONFIG_X86_PATCHABLE_FUNCTION_ENTRY
+unsigned long arch_livepatch_trace_entry(unsigned long addr)
+{
+    if ( (is_endbr64((const void *)addr) || is_endbr64_poison((const void 
*)addr)) )
+        addr += ENDBR64_LEN;
+
+    return addr;
+}
+
+void noinline arch_livepatch_apply_trace(const struct livepatch_func *trace)
+{
+    void *site = (void *)arch_livepatch_trace_entry((unsigned 
long)trace->old_addr);
+    const void *new_addr = xen_livepatch_trace_caller;
+    uint8_t insn[ARCH_PATCH_INSN_SIZE];
+    int32_t val = new_addr - (site + ARCH_PATCH_INSN_SIZE);
+
+    insn[0] = 0xe8; /* CALL rel32. */
+    memcpy(&insn[1], &val, sizeof(val));
+    memcpy(site, insn, sizeof(insn));
+}
+
+void noinline arch_livepatch_revert_trace(const struct livepatch_func *trace)
+{
+    void *site = (void *)arch_livepatch_trace_entry((unsigned 
long)trace->old_addr);
+
+    memset(site, 0x90, ARCH_PATCH_INSN_SIZE);
+}
+#endif
+
 void __init arch_livepatch_init(void)
 {
     void *start, *end;
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 8f943e11ea..1f7d052c30 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -126,6 +126,11 @@ SECTIONS
        *(.data.rel.ro)
        *(.data.rel.ro.*)
 
+       . = ALIGN(8);
+       __start_patchable_function_entries = .;
+       KEEP(*(__patchable_function_entries))
+       __stop_patchable_function_entries = .;
+
 #if defined(BUILD_ID) && defined(EFI) && !defined(BUILD_ID_EFI)
 /*
  * No mechanism to put an PT_NOTE in the EFI file - so put
diff --git a/xen/common/hypfs_trace.c b/xen/common/hypfs_trace.c
index d9e06e092e..314cce09bc 100644
--- a/xen/common/hypfs_trace.c
+++ b/xen/common/hypfs_trace.c
@@ -141,7 +141,7 @@ void trace_dispatch_callbacks(unsigned long ip, unsigned 
long parent_ip)
     if ( !global_trace_func.old_addr )
         return;
 
-    if ( (unsigned long)global_trace_func.old_addr != ip )
+    if ( arch_livepatch_trace_entry((unsigned long)global_trace_func.old_addr) 
!= ip )
         return;
 
     callback = global_trace_func.new_addr;
@@ -194,6 +194,8 @@ static bool is_patchable_function_entry(unsigned long addr)
 {
     const unsigned long *p;
 
+    addr = arch_livepatch_trace_entry(addr);
+
     for ( p = __start_patchable_function_entries;
           p < __stop_patchable_function_entries; p++ )
     {
diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
index 352a2df62c..295a3ebd57 100644
--- a/xen/common/livepatch.c
+++ b/xen/common/livepatch.c
@@ -224,7 +224,7 @@ xen_livepatch_trace_dispatcher(unsigned long ip, unsigned 
long parent_ip)
         {
             f = &data->funcs[i];
 
-            if ( (unsigned long)f->old_addr != ip )
+            if ( arch_livepatch_trace_entry((unsigned long)f->old_addr) != ip )
                 continue;
 
             ASSERT(f->new_addr);
diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
index 1b927af8ee..0e5af5fdc5 100644
--- a/xen/include/xen/livepatch.h
+++ b/xen/include/xen/livepatch.h
@@ -130,6 +130,7 @@ void arch_livepatch_apply_trace(const struct livepatch_func 
*trace);
 void arch_livepatch_revert(const struct livepatch_func *func,
                            struct livepatch_fstate *state);
 void arch_livepatch_revert_trace(const struct livepatch_func *trace);
+unsigned long arch_livepatch_trace_entry(unsigned long addr);
 void arch_livepatch_post_action(void);
 
 void arch_livepatch_mask(void);
-- 
2.34.1




 


Rackspace

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