From: Julien Grall <julien.grall@arm.com>
To: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>,
xen-devel@lists.xenproject.org, konrad@kernel.org,
ross.lagerwall@citrix.com
Cc: Andrew Cooper <andrew.cooper3@citrix.com>, sstabellini@kernel.org
Subject: Re: [PATCH v1 6/9] livepatch: Initial ARM64 support.
Date: Wed, 17 Aug 2016 19:22:54 +0100 [thread overview]
Message-ID: <c544f4fc-3a6d-bca5-e374-061a2cf37ddd@arm.com> (raw)
In-Reply-To: <1471216074-3007-7-git-send-email-konrad.wilk@oracle.com>
Hi Konrad,
On 15/08/16 00:07, Konrad Rzeszutek Wilk wrote:
[...]
> diff --git a/xen/arch/arm/arm64/livepatch.c b/xen/arch/arm/arm64/livepatch.c
> new file mode 100644
> index 0000000..e348942
> --- /dev/null
> +++ b/xen/arch/arm/arm64/livepatch.c
> @@ -0,0 +1,247 @@
> +/*
> + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
> + */
> +
> +#include "../livepatch.h"
> +#include <xen/bitops.h>
> +#include <xen/errno.h>
> +#include <xen/lib.h>
> +#include <xen/livepatch_elf.h>
> +#include <xen/livepatch.h>
> +#include <xen/mm.h>
> +#include <xen/vmap.h>
> +
> +#include <asm/bitops.h>
> +#include <asm/byteorder.h>
> +#include <asm/insn.h>
> +
> +void arch_livepatch_apply_jmp(struct livepatch_func *func)
> +{
> + uint32_t insn;
> + uint32_t *old_ptr;
> + uint32_t *new_ptr;
> +
> + BUILD_BUG_ON(PATCH_INSN_SIZE > sizeof(func->opaque));
> + BUILD_BUG_ON(PATCH_INSN_SIZE != sizeof(insn));
> +
> + ASSERT(vmap_of_xen_text);
> +
> + /* Save old one. */
> + old_ptr = func->old_addr;
> + memcpy(func->opaque, old_ptr, PATCH_INSN_SIZE);
> +
> + if ( func->new_size )
> + insn = aarch64_insn_gen_branch_imm((unsigned long)func->old_addr,
> + (unsigned long)func->new_addr,
> + AARCH64_INSN_BRANCH_NOLINK);
The function aarch64_insn_gen_branch_imm will fail to create the branch
if the difference between old_addr and new_addr is greater than 128MB.
In this case, the function will return AARCH64_BREAK_FAULT which will
crash Xen when the instruction is executed.
I cannot find any sanity check in the hypervisor code. So are we
trusting the payload?
> + else
> + insn = aarch64_insn_gen_nop();
> +
> + new_ptr = func->old_addr - (void *)_start + vmap_of_xen_text;
> +
> + /* PATCH! */
> + *(new_ptr) = cpu_to_le32(insn);
> +
> + clean_and_invalidate_dcache_va_range(new_ptr, sizeof(*new_ptr));
> +}
> +
> +void arch_livepatch_revert_jmp(const struct livepatch_func *func)
> +{
> + uint32_t *new_ptr;
> + uint32_t insn;
> +
> + memcpy(&insn, func->opaque, PATCH_INSN_SIZE);
> +
> + new_ptr = (uint32_t *)func->old_addr - (u32 *)_start + vmap_of_xen_text;
> +
> + /* PATCH! */
> + *(new_ptr) = cpu_to_le32(insn);
> +
> + clean_and_invalidate_dcache_va_range(new_ptr, sizeof(*new_ptr));
> +}
> +
> +int arch_livepatch_verify_elf(const struct livepatch_elf *elf)
> +{
> + const Elf_Ehdr *hdr = elf->hdr;
> +
> + if ( hdr->e_machine != EM_AARCH64 ||
> + hdr->e_ident[EI_CLASS] != ELFCLASS64 )
> + {
> + dprintk(XENLOG_ERR, LIVEPATCH "%s: Unsupported ELF Machine type!\n",
> + elf->name);
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
[...]
> diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
> index 20bb2ba..607ee59 100644
> --- a/xen/arch/arm/domain.c
> +++ b/xen/arch/arm/domain.c
> @@ -13,6 +13,7 @@
> #include <xen/hypercall.h>
> #include <xen/init.h>
> #include <xen/lib.h>
> +#include <xen/livepatch.h>
> #include <xen/sched.h>
> #include <xen/softirq.h>
> #include <xen/wait.h>
> @@ -55,6 +56,11 @@ void idle_loop(void)
>
> do_tasklet();
> do_softirq();
> + /*
> + * We MUST be last (or before dsb, wfi). Otherwise after we get the
> + * softirq we would execute dsb,wfi (and sleep) and not patch.
> + */
> + check_for_livepatch_work();
> }
> }
>
> diff --git a/xen/arch/arm/livepatch.c b/xen/arch/arm/livepatch.c
> index 3093554..19f6033 100644
> --- a/xen/arch/arm/livepatch.c
> +++ b/xen/arch/arm/livepatch.c
> @@ -1,56 +1,93 @@
> /*
> * Copyright (C) 2016 Citrix Systems R&D Ltd.
> */
> +
Spurious change?
> +#include "livepatch.h"
> #include <xen/errno.h>
> #include <xen/init.h>
> #include <xen/lib.h>
> #include <xen/livepatch_elf.h>
> #include <xen/livepatch.h>
> +#include <xen/vmap.h>
> +
> +#include <asm/mm.h>
>
> -/* On ARM32,64 instructions are always 4 bytes long. */
> -#define PATCH_INSN_SIZE 4
Rather than moving again PATCH_INSN_SIZE in this patch. Can you directly
move it in patch [1]?
> +void *vmap_of_xen_text;
>
> int arch_verify_insn_length(unsigned long len)
> {
> return len != PATCH_INSN_SIZE;
> }
>
> -void arch_livepatch_quiesce(void)
> +int arch_livepatch_quiesce(void)
It would have been nice to move the prototype change out of this patch
to keep it "straight forward".
> {
> + mfn_t text_mfn;
> + unsigned int text_order;
> +
> + if ( vmap_of_xen_text )
> + return -EINVAL;
> +
> + text_mfn = _mfn(virt_to_mfn(_stext));
> + text_order = get_order_from_bytes(_end - _start);
It is a bit odd that you use _stext before and the _start. But I won't
complain as I did the same in alternatives.c :/.
However, I think it should be enough to remap _stext -> _etext. I had to
map all the Xen binary for the alternatives because we may patch the
init text.
I forgot to mention it in the code, so I will send a patch to update it.
> +
> + /*
> + * The text section is read-only. So re-map Xen to be able to patch
> + * the code.
> + */
> + vmap_of_xen_text = __vmap(&text_mfn, 1 << text_order, 1, 1, PAGE_HYPERVISOR,
> + VMAP_DEFAULT);
> +
> + if ( !vmap_of_xen_text )
> + {
> + printk(XENLOG_ERR LIVEPATCH "Failed to setup vmap of hypervisor! (order=%u)\n",
> + text_order);
> + return -ENOMEM;
> + }
> + return 0;
> }
>
> void arch_livepatch_revive(void)
> {
> + /*
> + * Nuke the instruction cache. It has been cleaned before in
I guess you want to replace "It" by "Data cache" otherwise it does not
make much sense.
> + * arch_livepatch_apply_jmp.
What about the payload? It may contain instructions, so we need to
ensure that all the data reached the memory.
> + */
> + invalidate_icache();
> +
> + if ( vmap_of_xen_text )
> + vunmap(vmap_of_xen_text);
> +
> + vmap_of_xen_text = NULL;
> +
> + /*
> + * Need to flush the branch predicator for ARMv7 as it may be
s/predicator/predictor/
> + * architecturally visible to the software (see B2.2.4 in ARM DDI 0406C.b).
> + */
> + flush_xen_text_tlb_local();
I am a bit confused. In your comment you mention the branch but flush
the TLBs. The two are not related.
However, I would prefer the branch predictor to be flushed directly in
invalidate_icache by calling BPIALLIS. This is because flushing the
cache means that you likely want to flush the branch predictor too.
> }
>
> int arch_livepatch_verify_func(const struct livepatch_func *func)
> {
> - return -ENOSYS;
> -}
> -
> -void arch_livepatch_apply_jmp(struct livepatch_func *func)
> -{
> -}
> + if ( func->old_size < PATCH_INSN_SIZE )
> + return -EINVAL;
>
> -void arch_livepatch_revert_jmp(const struct livepatch_func *func)
> -{
> + return 0;
> }
>
> void arch_livepatch_post_action(void)
> {
> + /* arch_livepatch_revive has nuked the instruction cache. */
> }
>
> void arch_livepatch_mask(void)
> {
> + /* Mask System Error (SError) */
> + local_abort_disable();
> }
>
> void arch_livepatch_unmask(void)
> {
> -}
> -
> -int arch_livepatch_verify_elf(const struct livepatch_elf *elf)
> -{
> - return -ENOSYS;
> + local_abort_enable();
> }
>
> int arch_livepatch_perform_rel(struct livepatch_elf *elf,
> @@ -60,20 +97,34 @@ int arch_livepatch_perform_rel(struct livepatch_elf *elf,
> return -ENOSYS;
> }
>
> -int arch_livepatch_perform_rela(struct livepatch_elf *elf,
> - const struct livepatch_elf_sec *base,
> - const struct livepatch_elf_sec *rela)
> -{
> - return -ENOSYS;
> -}
> -
> int arch_livepatch_secure(const void *va, unsigned int pages, enum va_type type)
> {
> - return -ENOSYS;
> + unsigned long start = (unsigned long)va;
> + unsigned int flags;
> +
> + ASSERT(va);
> + ASSERT(pages);
> +
> + if ( type == LIVEPATCH_VA_RX )
> + flags = PTE_RO; /* R set, NX clear */
> + else if ( type == LIVEPATCH_VA_RW )
> + flags = PTE_NX; /* R clear, NX set */
> + else
> + flags = PTE_NX | PTE_RO; /* R set, NX set */
va_type is an enum. So I would prefer to see a switch. So we can catch
more easily any addition of a new member.
> +
> + modify_xen_mappings(start, start + pages * PAGE_SIZE, flags);
> +
> + return 0;
> }
>
> void __init arch_livepatch_init(void)
> {
> + void *start, *end;
> +
> + start = (void *)LIVEPATCH_VMAP;
> + end = start + MB(2);
Could you define the in config.h? So in the future we can rework more
easily the memory layout.
> +
> + vm_init_type(VMAP_XEN, start, end);
> }
>
> /*
> diff --git a/xen/arch/arm/livepatch.h b/xen/arch/arm/livepatch.h
> new file mode 100644
> index 0000000..8c8d625
> --- /dev/null
> +++ b/xen/arch/arm/livepatch.h
I am not sure why this header is living in arch/arm/ and not
include/asm-arm/
> @@ -0,0 +1,28 @@
> +/*
> + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
> + *
> + */
> +
> +#ifndef __XEN_ARM_LIVEPATCH_H__
> +#define __XEN_ARM_LIVEPATCH_H__
> +
> +/* On ARM32,64 instructions are always 4 bytes long. */
> +#define PATCH_INSN_SIZE 4
> +
> +/*
> + * The va of the hypervisor .text region. We need this as the
> + * normal va are write protected.
> + */
> +extern void *vmap_of_xen_text;
> +
> +#endif /* __XEN_ARM_LIVEPATCH_H__ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/x86/livepatch.c b/xen/arch/x86/livepatch.c
> index 06c67bc..e3f3f37 100644
> --- a/xen/arch/x86/livepatch.c
> +++ b/xen/arch/x86/livepatch.c
> @@ -15,10 +15,12 @@
>
> #define PATCH_INSN_SIZE 5
>
> -void arch_livepatch_quiesce(void)
> +int arch_livepatch_quiesce(void)
> {
> /* Disable WP to allow changes to read-only pages. */
> write_cr0(read_cr0() & ~X86_CR0_WP);
> +
> + return 0;
> }
>
> void arch_livepatch_revive(void)
> diff --git a/xen/common/Kconfig b/xen/common/Kconfig
> index 51afa24..2fc76b6 100644
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -222,7 +222,7 @@ endmenu
> config LIVEPATCH
> bool "Live patching support (TECH PREVIEW)"
> default n
> - depends on X86 && HAS_BUILD_ID = "y"
> + depends on (X86 || ARM_64) && HAS_BUILD_ID = "y"
> ---help---
> Allows a running Xen hypervisor to be dynamically patched using
> binary patches without rebooting. This is primarily used to binarily
> diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
> index 9c45270..af9443d 100644
> --- a/xen/common/livepatch.c
> +++ b/xen/common/livepatch.c
> @@ -682,7 +682,7 @@ static int prepare_payload(struct payload *payload,
> sizeof(*region->frame[i].bugs);
> }
>
> -#ifndef CONFIG_ARM
> +#ifndef CONFIG_ARM_32
I would prefer if you use CONFIG_ALTERNATIVE rather than CONFIG_ARM_32.
> sec = livepatch_elf_sec_by_name(elf, ".altinstructions");
> if ( sec )
> {
> @@ -711,9 +711,15 @@ static int prepare_payload(struct payload *payload,
> return -EINVAL;
> }
> }
> +#ifndef CONFIG_ARM
> apply_alternatives_nocheck(start, end);
> +#else
> + apply_alternatives(start, sec->sec->sh_size);
> +#endif
> }
> +#endif
>
> +#ifndef CONFIG_ARM
> sec = livepatch_elf_sec_by_name(elf, ".ex_table");
> if ( sec )
> {
Any reason to not move .ex_table in an x86 specific file? Or maybe we
should define a new config option HAVE_ARCH_EX_TABLE to avoid
architecture specific check in the common code.
> @@ -1083,12 +1089,17 @@ static int livepatch_list(xen_sysctl_livepatch_list_t *list)
> static int apply_payload(struct payload *data)
> {
> unsigned int i;
> + int rc;
>
> printk(XENLOG_INFO LIVEPATCH "%s: Applying %u functions\n",
> data->name, data->nfuncs);
>
> - arch_livepatch_quiesce();
> -
> + rc = arch_livepatch_quiesce();
> + if ( rc )
> + {
> + printk(XENLOG_ERR LIVEPATCH "%s: unable to quiesce!\n", data->name);
> + return rc;
> + }
> /*
> * Since we are running with IRQs disabled and the hooks may call common
> * code - which expects the spinlocks to run with IRQs enabled - we temporarly
> @@ -1119,10 +1130,16 @@ static int apply_payload(struct payload *data)
> static int revert_payload(struct payload *data)
> {
> unsigned int i;
> + int rc;
>
> printk(XENLOG_INFO LIVEPATCH "%s: Reverting\n", data->name);
>
> - arch_livepatch_quiesce();
> + rc = arch_livepatch_quiesce();
> + if ( rc )
> + {
> + printk(XENLOG_ERR LIVEPATCH "%s: unable to quiesce!\n", data->name);
> + return rc;
> + }
>
> for ( i = 0; i < data->nfuncs; i++ )
> arch_livepatch_revert_jmp(&data->funcs[i]);
> diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
> index a96f845..8d876f6 100644
> --- a/xen/include/asm-arm/config.h
> +++ b/xen/include/asm-arm/config.h
> @@ -80,9 +80,10 @@
> * 4M - 6M Fixmap: special-purpose 4K mapping slots
> * 6M - 8M Early boot mapping of FDT
> * 8M - 10M Early relocation address (used when relocating Xen)
> + * and later for livepatch vmap (if compiled in)
> *
> * ARM32 layout:
> - * 0 - 8M <COMMON>
> + * 0 - 10M <COMMON>
May I ask to have this change and ...
> *
> * 32M - 128M Frametable: 24 bytes per page for 16GB of RAM
> * 256M - 1G VMAP: ioremap and early_ioremap use this virtual address
> @@ -93,7 +94,7 @@
> *
> * ARM64 layout:
> * 0x0000000000000000 - 0x0000007fffffffff (512GB, L0 slot [0])
> - * 0 - 8M <COMMON>
> + * 0 - 10M <COMMON>
this change in a separate patch to keep this patch livepatch only?
> *
> * 1G - 2G VMAP: ioremap and early_ioremap
> *
> @@ -113,6 +114,9 @@
> #define FIXMAP_ADDR(n) (_AT(vaddr_t,0x00400000) + (n) * PAGE_SIZE)
> #define BOOT_FDT_VIRT_START _AT(vaddr_t,0x00600000)
> #define BOOT_RELOC_VIRT_START _AT(vaddr_t,0x00800000)
> +#ifdef CONFIG_LIVEPATCH
> +#define LIVEPATCH_VMAP _AT(vaddr_t,0x00800000)
> +#endif
>
> #define HYPERVISOR_VIRT_START XEN_VIRT_START
>
> diff --git a/xen/include/asm-arm/current.h b/xen/include/asm-arm/current.h
> index 65c0cdf..f4fcfd6 100644
> --- a/xen/include/asm-arm/current.h
> +++ b/xen/include/asm-arm/current.h
> @@ -33,8 +33,15 @@ static inline struct cpu_info *get_cpu_info(void)
>
> #define guest_cpu_user_regs() (&get_cpu_info()->guest_cpu_user_regs)
>
> +#ifdef CONFIG_LIVEPATCH
> +#define switch_stack_and_jump(stack, fn) \
> + asm volatile ("mov sp,%0;" \
> + "bl check_for_livepatch_work;" \
> + "b " STR(fn) : : "r" (stack) : "memory" )
> +#else
> #define switch_stack_and_jump(stack, fn) \
> asm volatile ("mov sp,%0; b " STR(fn) : : "r" (stack) : "memory" )
> +#endif
I have commented on the previous version about switch_stack_and_jump a
while after you send this series. So I will spare you new comments here :).
> #define reset_stack_and_jump(fn) switch_stack_and_jump(get_cpu_info(), fn)
>
[...]
> diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
> index 2e64686..6f30c0d 100644
> --- a/xen/include/xen/livepatch.h
> +++ b/xen/include/xen/livepatch.h
> @@ -72,7 +72,7 @@ int arch_livepatch_verify_func(const struct livepatch_func *func);
> * These functions are called around the critical region patching live code,
> * for an architecture to take make appropratie global state adjustments.
> */
> -void arch_livepatch_quiesce(void);
> +int arch_livepatch_quiesce(void);
> void arch_livepatch_revive(void);
>
> void arch_livepatch_apply_jmp(struct livepatch_func *func);
>
[1]
https://lists.xenproject.org/archives/html/xen-devel/2016-08/msg01832.html
--
Julien Grall
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
next prev parent reply other threads:[~2016-08-17 18:23 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-08-14 23:07 [PATCH v1] Livepatch ARM 64 implementation Konrad Rzeszutek Wilk
2016-08-14 23:07 ` [PATCH v1 1/9] livepatch: Bubble up sanity checks on Elf relocs Konrad Rzeszutek Wilk
2016-08-17 11:56 ` Jan Beulich
2016-08-14 23:07 ` [PATCH v1 2/9] x86/arm: Make 'make debug' work properly Konrad Rzeszutek Wilk
2016-08-17 12:00 ` Jan Beulich
2016-08-22 17:05 ` Konrad Rzeszutek Wilk
2016-08-14 23:07 ` [PATCH v1 3/9] x86/arm64: Move the ALT_[ORIG|REPL]_PTR macros to header files Konrad Rzeszutek Wilk
2016-08-17 12:15 ` Jan Beulich
2016-08-17 16:26 ` Julien Grall
2016-08-14 23:07 ` [PATCH v1 4/9] arm/mm: Introduce modify_xen_mappings Konrad Rzeszutek Wilk
2016-08-17 16:54 ` Julien Grall
2016-08-14 23:07 ` [PATCH v1 5/9] arm64/insn: introduce aarch64_insn_gen_{nop|branch_imm}() helper functions Konrad Rzeszutek Wilk
2016-08-14 23:07 ` [PATCH v1 6/9] livepatch: Initial ARM64 support Konrad Rzeszutek Wilk
2016-08-15 8:21 ` Jan Beulich
2016-08-15 14:09 ` Konrad Rzeszutek Wilk
2016-08-15 14:23 ` Jan Beulich
2016-08-15 14:57 ` Julien Grall
2016-08-15 15:17 ` Konrad Rzeszutek Wilk
2016-08-15 15:25 ` Julien Grall
2016-08-15 15:27 ` Andrew Cooper
2016-08-17 2:45 ` Konrad Rzeszutek Wilk
2016-08-17 9:02 ` Andrew Cooper
2016-08-15 15:17 ` Julien Grall
2016-08-17 1:50 ` Konrad Rzeszutek Wilk
2016-08-17 19:44 ` Julien Grall
2016-08-17 17:12 ` Julien Grall
2016-08-17 18:22 ` Julien Grall [this message]
2016-08-17 18:57 ` Konrad Rzeszutek Wilk
2016-08-17 19:50 ` Julien Grall
2016-08-22 19:22 ` Konrad Rzeszutek Wilk
2016-08-24 2:14 ` Konrad Rzeszutek Wilk
2016-08-25 13:54 ` Julien Grall
2016-08-14 23:07 ` [PATCH v1 7/9] livepatch: ARM64: Ignore mapping symbols: $[a, d, x, p] Konrad Rzeszutek Wilk
2016-08-17 12:21 ` Jan Beulich
2016-08-22 17:14 ` Konrad Rzeszutek Wilk
2016-08-14 23:07 ` [PATCH v1 8/9] livepatch: Move test-cases to common Konrad Rzeszutek Wilk
2016-08-17 12:24 ` Jan Beulich
2016-08-14 23:07 ` [PATCH v1 9/9] livepatch: tests: Make them compile under ARM64 Konrad Rzeszutek Wilk
2016-08-17 18:29 ` Julien Grall
2016-08-17 19:00 ` Konrad Rzeszutek Wilk
2016-08-17 19:57 ` Julien Grall
2016-08-17 20:08 ` Andrew Cooper
2016-08-18 10:38 ` Jan Beulich
2016-08-22 17:41 ` Konrad Rzeszutek Wilk
2016-08-22 19:59 ` Konrad Rzeszutek Wilk
2016-08-15 14:52 ` [PATCH v1] Livepatch ARM 64 implementation Julien Grall
2016-08-15 15:49 ` Konrad Rzeszutek Wilk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c544f4fc-3a6d-bca5-e374-061a2cf37ddd@arm.com \
--to=julien.grall@arm.com \
--cc=andrew.cooper3@citrix.com \
--cc=konrad.wilk@oracle.com \
--cc=konrad@kernel.org \
--cc=ross.lagerwall@citrix.com \
--cc=sstabellini@kernel.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).