From patchwork Sun Aug 14 23:07:51 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Konrad Rzeszutek Wilk X-Patchwork-Id: 9280213 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1B01260780 for ; Sun, 14 Aug 2016 23:11:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0AE7A28A13 for ; Sun, 14 Aug 2016 23:11:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F3C8428A3B; Sun, 14 Aug 2016 23:11:49 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 7A2B028A13 for ; Sun, 14 Aug 2016 23:11:48 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bZ4WD-00019a-1b; Sun, 14 Aug 2016 23:08:37 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bZ4WB-00019G-N4 for xen-devel@lists.xenproject.org; Sun, 14 Aug 2016 23:08:36 +0000 Received: from [85.158.139.211] by server-17.bemta-5.messagelabs.com id 87/58-24990-2F9F0B75; Sun, 14 Aug 2016 23:08:34 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrHLMWRWlGSWpSXmKPExsUyZ7p8oO6nnxv CDfbOYbb4vmUykwOjx+EPV1gCGKNYM/OS8isSWDNe7X3FVrD4BmPFtPtnmRsYXyxj7GLk4hAS aGOSWL5zOhuE841RYtrWGVCZjYwSu9/PY4VwJjBKXPmyGaiMg4NNwETizSrHLkZODhGBCInel 6dYQGqYBWYzSjyafZENJCEsYCmx+MVzZhCbRUBV4ue5PkYQm1fAVaJp1UxWkDkSAvISz27Xg4 Q5Bdwk/t87ywJiCwGVvDjRCTZGQsBQ4vTDbYwTGPkWMDKsYtQoTi0qSy3SNTTXSyrKTM8oyU3 MzNE1NDDVy00tLk5MT81JTCrWS87P3cQIDBcGINjBePG05yFGSQ4mJVFeHfN14UJ8SfkplRmJ xRnxRaU5qcWHGGU4OJQkeBf82BAuJFiUmp5akZaZAwxcmLQEB4+SCO9ukDRvcUFibnFmOkTqF KOilDjvEpCEAEgiozQPrg0WLZcYZaWEeRmBDhHiKUgtys0sQZV/xSjOwagkzLsPZApPZl4J3P RXQIuZgBbrS4MtLklESEk1ME55beXyf8LOA1/VctT+maWtiX4aPPXwwpN8W7SKjui/WV5998I FBr3+Gwz2mzn2r70k+FrJq6n78q9Xtqvfnw6MPXLp7dEHD/heOtxTik7WrXST5XE8/ZpVO33W P7mIV/XBAT/FU/ddj7j0Qc9arYd59dTTT40+u0d1zHp8dNHL/JTFN0qvFZQpsRRnJBpqMRcVJ wIArtGtr5ECAAA= X-Env-Sender: konrad.wilk@oracle.com X-Msg-Ref: server-3.tower-206.messagelabs.com!1471216111!50839062!1 X-Originating-IP: [156.151.31.81] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogMTU2LjE1MS4zMS44MSA9PiAyODgzMzk=\n X-StarScan-Received: X-StarScan-Version: 8.84; banners=-,-,- X-VirusChecked: Checked Received: (qmail 6547 invoked from network); 14 Aug 2016 23:08:33 -0000 Received: from userp1040.oracle.com (HELO userp1040.oracle.com) (156.151.31.81) by server-3.tower-206.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 14 Aug 2016 23:08:33 -0000 Received: from userv0021.oracle.com (userv0021.oracle.com [156.151.31.71]) by userp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id u7EN8M9O024694 (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Sun, 14 Aug 2016 23:08:23 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by userv0021.oracle.com (8.13.8/8.13.8) with ESMTP id u7EN8MvW002005 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Sun, 14 Aug 2016 23:08:22 GMT Received: from abhmp0010.oracle.com (abhmp0010.oracle.com [141.146.116.16]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id u7EN8Mxb001511; Sun, 14 Aug 2016 23:08:22 GMT Received: from localhost.localdomain.com (/209.6.196.81) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Sun, 14 Aug 2016 16:08:21 -0700 From: Konrad Rzeszutek Wilk To: xen-devel@lists.xenproject.org, konrad@kernel.org, ross.lagerwall@citrix.com Date: Sun, 14 Aug 2016 19:07:51 -0400 Message-Id: <1471216074-3007-7-git-send-email-konrad.wilk@oracle.com> X-Mailer: git-send-email 2.4.11 In-Reply-To: <1471216074-3007-1-git-send-email-konrad.wilk@oracle.com> References: <1471216074-3007-1-git-send-email-konrad.wilk@oracle.com> X-Source-IP: userv0021.oracle.com [156.151.31.71] Cc: Andrew Cooper , julien.grall@arm.com, sstabellini@kernel.org, Konrad Rzeszutek Wilk Subject: [Xen-devel] [PATCH v1 6/9] livepatch: Initial ARM64 support. X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP As compared to x86 the va of the hypervisor .text is locked down - we cannot modify the running pagetables to have the .ro flag unset. We borrow the same idea that alternative patching has - which is to vmap the entire .text region and use the alternative virtual address for patching. Since we are doing vmap we may fail, hence the arch_livepatch_quiesce is changed to return an error value which will be bubbled in payload->rc and provided to the user (along with messages in the ring buffer). And the livepatch virtual address space (where the new functions are) needs to be close to the hypervisor virtual address so that the trampoline can reach it. As such we re-use the BOOT_RELOC_VIRT_START which is not used after bootup (alternatively we can also use the space after the _end to FIXMAP_ADDR(0), but that may be too small). The ELF relocation engine at the start was coded from the "ELF for the ARM 64-bit Architecture (AArch64)" (http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf) but after a while of trying to write the correct bit shifting and masking from scratch I ended up borrowing from Linux, the 'reloc_insn_imm' (Linux v4.7 arch/arm64/kernel/module.c function. See 257cb251925f854da435cbf79b140984413871ac "arm64: Loadable modules") In the livepatch payload loading code we tweak the #ifdef to only exclude ARM_32. The exceptions are not part of ARM 32/64 hence they are still behind the #ifdef. We also expand the MAINTAINERS file to include the arm64 and arm32 platform specific livepatch file. Signed-off-by: Konrad Rzeszutek Wilk --- Cc: Ross Lagerwall Cc: Stefano Stabellini Cc: Julien Grall Cc Jan Beulich Cc: Andrew Cooper RFC: Wholy cow! It works! v1: Finished the various TODOs and reviews outlined by Julien in RFC. --- MAINTAINERS | 1 + xen/arch/arm/Makefile | 13 ++- xen/arch/arm/arm32/Makefile | 2 +- xen/arch/arm/arm32/livepatch.c | 55 +++++++++ xen/arch/arm/arm64/Makefile | 1 + xen/arch/arm/arm64/livepatch.c | 247 +++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/domain.c | 6 + xen/arch/arm/livepatch.c | 99 +++++++++++++---- xen/arch/arm/livepatch.h | 28 +++++ xen/arch/x86/livepatch.c | 4 +- xen/common/Kconfig | 2 +- xen/common/livepatch.c | 25 ++++- xen/include/asm-arm/config.h | 8 +- xen/include/asm-arm/current.h | 7 ++ xen/include/xen/elfstructs.h | 40 ++++++- xen/include/xen/livepatch.h | 2 +- 16 files changed, 503 insertions(+), 37 deletions(-) create mode 100644 xen/arch/arm/arm32/livepatch.c create mode 100644 xen/arch/arm/arm64/livepatch.c create mode 100644 xen/arch/arm/livepatch.h diff --git a/MAINTAINERS b/MAINTAINERS index 97720a8..ae0b6bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,6 +269,7 @@ S: Supported F: docs/misc/livepatch.markdown F: tools/misc/xen-livepatch.c F: xen/arch/*/livepatch* +F: xen/arch/*/*/livepatch* F: xen/common/livepatch* F: xen/include/xen/livepatch* diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 0a96713..9f75c5c 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -58,6 +58,15 @@ ALL_OBJS := $(TARGET_SUBARCH)/head.o $(ALL_OBJS) DEPS += $(TARGET_SUBARCH)/.head.o.d +ifdef CONFIG_LIVEPATCH +all_symbols = --all-symbols +ifdef CONFIG_FAST_SYMBOL_LOOKUP +all_symbols = --all-symbols --sort-by-name +endif +else +all_symbols = +endif + $(TARGET): $(TARGET)-syms $(TARGET).axf $(OBJCOPY) -O binary -S $< $@ ifeq ($(CONFIG_ARM_64),y) @@ -93,12 +102,12 @@ $(TARGET)-syms: prelink.o xen.lds $(BASEDIR)/common/symbols-dummy.o $(LD) $(LDFLAGS) -T xen.lds -N prelink.o \ $(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).0 $(NM) -pa --format=sysv $(@D)/.$(@F).0 \ - | $(BASEDIR)/tools/symbols --sysv --sort >$(@D)/.$(@F).0.S + | $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).0.S $(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0.o $(LD) $(LDFLAGS) -T xen.lds -N prelink.o \ $(@D)/.$(@F).0.o -o $(@D)/.$(@F).1 $(NM) -pa --format=sysv $(@D)/.$(@F).1 \ - | $(BASEDIR)/tools/symbols --sysv --sort >$(@D)/.$(@F).1.S + | $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).1.S $(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1.o $(LD) $(LDFLAGS) -T xen.lds -N prelink.o $(build_id_linker) \ $(@D)/.$(@F).1.o -o $@ diff --git a/xen/arch/arm/arm32/Makefile b/xen/arch/arm/arm32/Makefile index b20db64..5966de0 100644 --- a/xen/arch/arm/arm32/Makefile +++ b/xen/arch/arm/arm32/Makefile @@ -4,8 +4,8 @@ obj-$(EARLY_PRINTK) += debug.o obj-y += domctl.o obj-y += domain.o obj-y += entry.o +obj-$(CONFIG_LIVEPATCH) += livepatch.o obj-y += proc-v7.o proc-caxx.o obj-y += smpboot.o obj-y += traps.o obj-y += vfp.o - diff --git a/xen/arch/arm/arm32/livepatch.c b/xen/arch/arm/arm32/livepatch.c new file mode 100644 index 0000000..89ee2f5 --- /dev/null +++ b/xen/arch/arm/arm32/livepatch.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include + +void arch_livepatch_apply_jmp(struct livepatch_func *func) +{ +} + +void arch_livepatch_revert_jmp(const struct livepatch_func *func) +{ +} + +int arch_livepatch_verify_elf(const struct livepatch_elf *elf) +{ + const Elf_Ehdr *hdr = elf->hdr; + + if ( hdr->e_machine != EM_ARM || + hdr->e_ident[EI_CLASS] != ELFCLASS32 ) + { + dprintk(XENLOG_ERR, LIVEPATCH "%s: Unsupported ELF Machine type!\n", + elf->name); + return -EOPNOTSUPP; + } + + if ( (hdr->e_flags & EF_ARM_EABI_MASK) != EF_ARM_EABI_VER5 ) + { + dprintk(XENLOG_ERR, LIVEPATCH "%s: Unsupported ELF EABI(%x)!\n", + elf->name, hdr->e_flags); + return -EOPNOTSUPP; + } + + return 0; +} + +int arch_livepatch_perform_rela(struct livepatch_elf *elf, + const struct livepatch_elf_sec *base, + const struct livepatch_elf_sec *rela) +{ + return -ENOSYS; +} + +/* + * 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/arm/arm64/Makefile b/xen/arch/arm/arm64/Makefile index c1fa43f..149b6b3 100644 --- a/xen/arch/arm/arm64/Makefile +++ b/xen/arch/arm/arm64/Makefile @@ -6,6 +6,7 @@ obj-y += domctl.o obj-y += domain.o obj-y += entry.o obj-y += insn.o +obj-$(CONFIG_LIVEPATCH) += livepatch.o obj-y += smpboot.o obj-y += traps.o obj-y += vfp.o 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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); + 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; +} + +static int reloc_insn_imm(void *dest, u64 val, int lsb, int len, + enum aarch64_insn_imm_type imm_type) +{ + u64 imm, imm_mask; + s64 sval = val; + u32 insn = *(u32 *)dest; + + /* Calculate the relocation value. */ + sval >>= lsb; + + /* Extract the value bits and shift them to bit 0. */ + imm_mask = (BIT(lsb + len) - 1) >> lsb; + imm = sval & imm_mask; + + /* Update the instruction's immediate field. */ + insn = aarch64_insn_encode_immediate(imm_type, insn, imm); + *(u32 *)dest = insn; + + /* + * Extract the upper value bits (including the sign bit) and + * shift them to bit 0. + */ + sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1); + + /* + * Overflow has occurred if the upper bits are not all equal to + * the sign bit of the value. + */ + if ((u64)(sval + 1) >= 2) + return -EOVERFLOW; + return 0; +} + +int arch_livepatch_perform_rela(struct livepatch_elf *elf, + const struct livepatch_elf_sec *base, + const struct livepatch_elf_sec *rela) +{ + const Elf_RelA *r; + unsigned int symndx, i; + uint64_t val; + void *dest; + + for ( i = 0; i < (rela->sec->sh_size / rela->sec->sh_entsize); i++ ) + { + int err = 0; + + r = rela->data + i * rela->sec->sh_entsize; + + symndx = ELF64_R_SYM(r->r_info); + + if ( symndx > elf->nsym ) + { + dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative relocation wants symbol@%u which is past end!\n", + elf->name, symndx); + return -EINVAL; + } + + dest = base->load_addr + r->r_offset; /* P */ + val = elf->sym[symndx].sym->st_value + r->r_addend; /* S+A */ + + /* ARM64 operations at minimum are always 32-bit. */ + if ( r->r_offset >= base->sec->sh_size || + (r->r_offset + sizeof(uint32_t)) > base->sec->sh_size ) + goto bad_offset; + + switch ( ELF64_R_TYPE(r->r_info) ) { + /* Data */ + case R_AARCH64_ABS64: + if ( r->r_offset + sizeof(uint64_t) > base->sec->sh_size ) + goto bad_offset; + *(int64_t *)dest = val; + break; + + case R_AARCH64_ABS32: + *(int32_t *)dest = val; + if ( (int64_t)val != *(int32_t *)dest ) + err = -EOVERFLOW; + break; + + case R_AARCH64_PREL64: + if ( r->r_offset + sizeof(uint64_t) > base->sec->sh_size ) + goto bad_offset; + + val -= (uint64_t)dest; + *(int64_t *)dest = val; + break; + + case R_AARCH64_PREL32: + val -= (uint64_t)dest; + *(int32_t *)dest = val; + if ( (int64_t)val != *(int32_t *)dest ) + err = -EOVERFLOW; + break; + + /* Instructions. */ + case R_AARCH64_ADR_PREL_LO21: + val -= (uint64_t)dest; + err = reloc_insn_imm(dest, val, 0, 21, AARCH64_INSN_IMM_ADR); + break; + + case R_AARCH64_ADR_PREL_PG_HI21: + val = (val & ~0xfff) - ((u64)dest & ~0xfff); + err = reloc_insn_imm(dest, val, 12, 21, AARCH64_INSN_IMM_ADR); + break; + + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_ADD_ABS_LO12_NC: + err = reloc_insn_imm(dest, val, 0, 12, AARCH64_INSN_IMM_12); + if ( err == -EOVERFLOW ) + err = 0; + break; + + case R_AARCH64_LDST16_ABS_LO12_NC: + err = reloc_insn_imm(dest, val, 1, 11, AARCH64_INSN_IMM_12); + if ( err == -EOVERFLOW ) + err = 0; + break; + + case R_AARCH64_LDST32_ABS_LO12_NC: + err = reloc_insn_imm(dest, val, 2, 10, AARCH64_INSN_IMM_12); + if ( err == -EOVERFLOW ) + err = 0; + break; + + case R_AARCH64_LDST64_ABS_LO12_NC: + err = reloc_insn_imm(dest, val, 3, 9, AARCH64_INSN_IMM_12); + if ( err == -EOVERFLOW ) + err = 0; + break; + + case R_AARCH64_CONDBR19: + err = reloc_insn_imm(dest, val, 2, 19, AARCH64_INSN_IMM_19); + break; + + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + val -= (uint64_t)dest; + err = reloc_insn_imm(dest, val, 2, 26, AARCH64_INSN_IMM_26); + break; + + default: + dprintk(XENLOG_ERR, LIVEPATCH "%s: Unhandled relocation %lu\n", + elf->name, ELF64_R_TYPE(r->r_info)); + return -EOPNOTSUPP; + } + + if ( err ) + { + dprintk(XENLOG_ERR, LIVEPATCH "%s: Overflow in relocation %u in %s for %s!\n", + elf->name, i, rela->name, base->name); + return err; + } + } + return 0; + + bad_offset: + dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative relocation offset is past %s section!\n", + elf->name, base->name); + return -EINVAL; +} + +/* + * 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/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 #include #include +#include #include #include #include @@ -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. */ + +#include "livepatch.h" #include #include #include #include #include +#include + +#include -/* On ARM32,64 instructions are always 4 bytes long. */ -#define PATCH_INSN_SIZE 4 +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) { + 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); + + /* + * 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 + * arch_livepatch_apply_jmp. + */ + 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 + * architecturally visible to the software (see B2.2.4 in ARM DDI 0406C.b). + */ + flush_xen_text_tlb_local(); } 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 */ + + 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); + + 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 @@ -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 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 ) { @@ -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 + * 0 - 10M * * 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 + * 0 - 10M * * 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 #define reset_stack_and_jump(fn) switch_stack_and_jump(get_cpu_info(), fn) diff --git a/xen/include/xen/elfstructs.h b/xen/include/xen/elfstructs.h index 5f2082e..a68874b 100644 --- a/xen/include/xen/elfstructs.h +++ b/xen/include/xen/elfstructs.h @@ -103,6 +103,15 @@ typedef uint64_t Elf64_Xword; (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ (ehdr).e_ident[EI_MAG3] == ELFMAG3) +/* e_flags */ +#define EF_ARM_EABI_MASK 0xff000000 +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + /* ELF Header */ typedef struct elfhdr { unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ @@ -171,6 +180,7 @@ typedef struct { #define EM_PPC 20 /* PowerPC */ #define EM_PPC64 21 /* PowerPC 64-bit */ #define EM_ARM 40 /* Advanced RISC Machines ARM */ +#define EM_AARCH64 183 /* ARM 64-bit */ #define EM_ALPHA 41 /* DEC ALPHA */ #define EM_SPARCV9 43 /* SPARC version 9 */ #define EM_ALPHA_EXP 0x9026 /* DEC ALPHA */ @@ -353,12 +363,40 @@ typedef struct { #define ELF64_R_TYPE(info) ((info) & 0xFFFFFFFF) #define ELF64_R_INFO(s,t) (((s) << 32) + (u_int32_t)(t)) -/* x86-64 relocation types. We list only the ones Live Patch implements. */ +/* + * Relocation types for x86_64 and ARM 64. We list only the ones Live Patch + * implements. + */ #define R_X86_64_NONE 0 /* No reloc */ #define R_X86_64_64 1 /* Direct 64 bit */ #define R_X86_64_PC32 2 /* PC relative 32 bit signed */ #define R_X86_64_PLT32 4 /* 32 bit PLT address */ +/* + * S - address of symbol. + * A - addend for relocation (r_addend) + * P - address of the dest being relocated (derieved from r_offset) + * NC - No check for overflow. + * + * The defines also use _PREL for PC-relative address, and _NC is No Check. + */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. S+A, NC*/ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. S+A */ +#define R_AARCH64_PREL64 260 /* S+A-P, NC */ +#define R_AARCH64_PREL32 261 /* S+A-P */ + +#define R_AARCH64_ADR_PREL_LO21 274 /* ADR imm, [20:0]. S+A-P */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* ADRP imm, [32:12]. Page(S+A) - Page(P).*/ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* ADD imm. [11:0]. S+A, NC */ + +#define R_AARCH64_CONDBR19 280 /* Bits 20:2, S+A-P */ +#define R_AARCH64_JUMP26 282 /* Bits 27:2, S+A-P */ +#define R_AARCH64_CALL26 283 /* Bits 27:2, S+A-P */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* LD/ST to bits 11:1, S+A, NC */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* LD/ST to bits 11:2, S+A, NC */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* LD/ST to bits 11:3, S+A, NC */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* LD/ST to bits 11:0, S+A, NC */ + /* Program Header */ typedef struct { Elf32_Word p_type; /* segment type */ 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);