|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC 5/5] xen/x86: livepatch: enable attaching callbacks
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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |