diff mbox

[v1,6/9] livepatch: Initial ARM64 support.

Message ID 1471216074-3007-7-git-send-email-konrad.wilk@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Konrad Rzeszutek Wilk Aug. 14, 2016, 11:07 p.m. UTC
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 <konrad.wilk@oracle.com>

---
Cc: Ross Lagerwall <ross.lagerwall@citrix.com>
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Julien Grall <julien.grall@arm.com>
Cc  Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>

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

Comments

Jan Beulich Aug. 15, 2016, 8:21 a.m. UTC | #1
>>> On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
> --- 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"

Would this better become a black list?

> @@ -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

Conditionals like this are ugly - can't this be properly abstracted?

> --- 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

Aren't these ARM32 definitions, which should be unneeded for
ARM64 support?

> @@ -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 */

I think this tries to be sorted by number.

> +/*
> + * 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 */

No R_AARCH64_TSTBR14?

> +#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 */

What about R_AARCH64_LDST128_ABS_LO12_NC?

Jan
Konrad Rzeszutek Wilk Aug. 15, 2016, 2:09 p.m. UTC | #2
On Mon, Aug 15, 2016 at 02:21:48AM -0600, Jan Beulich wrote:
> >>> On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
> > --- 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"
> 
> Would this better become a black list?

OK, that would make it easier.
> 
> > @@ -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
> 
> Conditionals like this are ugly - can't this be properly abstracted?

Yes, I can introduce an apply_alternatives_nocheck on ARM that will
hava the same set of arguments on x86.

Or I can make a new function name?
> 
> > --- 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
> 
> Aren't these ARM32 definitions, which should be unneeded for
> ARM64 support?

Correct. Let me move that and also the arch/arm/arm32/livepatch:arch_livepatch_verify_elf
code out of this patch.
> 
> > @@ -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 */
> 
> I think this tries to be sorted by number.
> 
> > +/*
> > + * 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 */
> 
> No R_AARCH64_TSTBR14?
> 
> > +#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 */
> 
> What about R_AARCH64_LDST128_ABS_LO12_NC?

I didnt' see it in the ELF of xen-syms or the livepatch tests cases so I omitted them.
But then I added some that weren't in the ELF files either: LDST16 and LDST8.

I will add the two missing ones and also sort this.

Thanks!
> 
> Jan
>
Jan Beulich Aug. 15, 2016, 2:23 p.m. UTC | #3
>>> On 15.08.16 at 16:09, <konrad.wilk@oracle.com> wrote:
> On Mon, Aug 15, 2016 at 02:21:48AM -0600, Jan Beulich wrote:
>> >>> On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
>> > @@ -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
>> 
>> Conditionals like this are ugly - can't this be properly abstracted?
> 
> Yes, I can introduce an apply_alternatives_nocheck on ARM that will
> hava the same set of arguments on x86.
> 
> Or I can make a new function name?

Either way is fine with me, with a slight preference to the former
one.

Jan
Julien Grall Aug. 15, 2016, 2:57 p.m. UTC | #4
Hi Jan and Konrad,

On 15/08/2016 16:23, Jan Beulich wrote:
>>>> On 15.08.16 at 16:09, <konrad.wilk@oracle.com> wrote:
>> On Mon, Aug 15, 2016 at 02:21:48AM -0600, Jan Beulich wrote:
>>>>>> On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
>>>> @@ -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
>>>
>>> Conditionals like this are ugly - can't this be properly abstracted?
>>
>> Yes, I can introduce an apply_alternatives_nocheck on ARM that will
>> hava the same set of arguments on x86.
>>
>> Or I can make a new function name?
>
> Either way is fine with me, with a slight preference to the former
> one.

I am fine with the prototype of the function apply_alternatives_nocheck 
but I don't think the name is relevant for ARM.

Is there any reason we don't want to call directly apply_alternatives in 
x86?

Regards,
Konrad Rzeszutek Wilk Aug. 15, 2016, 3:17 p.m. UTC | #5
On Mon, Aug 15, 2016 at 04:57:26PM +0200, Julien Grall wrote:
> Hi Jan and Konrad,
> 
> On 15/08/2016 16:23, Jan Beulich wrote:
> > > > > On 15.08.16 at 16:09, <konrad.wilk@oracle.com> wrote:
> > > On Mon, Aug 15, 2016 at 02:21:48AM -0600, Jan Beulich wrote:
> > > > > > > On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
> > > > > @@ -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
> > > > 
> > > > Conditionals like this are ugly - can't this be properly abstracted?
> > > 
> > > Yes, I can introduce an apply_alternatives_nocheck on ARM that will
> > > hava the same set of arguments on x86.
> > > 
> > > Or I can make a new function name?
> > 
> > Either way is fine with me, with a slight preference to the former
> > one.
> 
> I am fine with the prototype of the function apply_alternatives_nocheck but
> I don't think the name is relevant for ARM.
> 
> Is there any reason we don't want to call directly apply_alternatives in
> x86?

It assumes (and has an ASSERT) that it is called with interrupts disabled.
And we don't need to do that (as during livepatch loading we can modify the
livepatch payload without worrying about interrupts).

P.S.
loading != applying.

I could do a patch where we rename 'apply_alternatives' -> 'apply_alternatives_boot'
and 'apply_alternatives_nocheck' to 'apply_alternatives'.

Also the x86 version instead of having:

apply_alternatives(struct alt_instr *start, struct alt_instr *end)

would end up with:

apply_alternatives(void *start, size_t length)

to comply with ARM version.
> 
> Regards,
> 
> -- 
> Julien Grall
Julien Grall Aug. 15, 2016, 3:17 p.m. UTC | #6
Hi Konrad,

On 15/08/2016 01:07, Konrad Rzeszutek Wilk wrote:

[...]

> 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
> -

Spurious change?

> 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 <xen/errno.h>
> +#include <xen/lib.h>
> +#include <xen/livepatch_elf.h>
> +#include <xen/livepatch.h>
> +
> +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;

I would prefer if you introduce ARM32 implementation in one go. I.e can 
you only return -EOPNOTSUPP here for now?


[...]

> +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;

As you borrow the code from Linux, could we keep the abstraction with 
reloc_data and defer the overflow check? It would avoid to have the same 
if in multiple place in this code.

> +            *(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;
> +

It seems the implementation of R_AARCH64_ADR_PREL_PG_HI21_NC is missing.

> +        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;

Hmmm, I think we want to avoid the payload having 
R_AARCH64_ADR_PREL_PG_HI21* relocations due to the erratum #843419 on 
some Cortex-A53.

I haven't yet looked at how you build the payload, but this may also 
affects the process to do it. I will give a look on it.

> +
> +        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:
> + */

Cheers,
Julien Grall Aug. 15, 2016, 3:25 p.m. UTC | #7
On 15/08/2016 17:17, Konrad Rzeszutek Wilk wrote:
> On Mon, Aug 15, 2016 at 04:57:26PM +0200, Julien Grall wrote:
>> Hi Jan and Konrad,
>>
>> On 15/08/2016 16:23, Jan Beulich wrote:
>>>>>> On 15.08.16 at 16:09, <konrad.wilk@oracle.com> wrote:
>>>> On Mon, Aug 15, 2016 at 02:21:48AM -0600, Jan Beulich wrote:
>>>>>>>> On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
>>>>>> @@ -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
>>>>>
>>>>> Conditionals like this are ugly - can't this be properly abstracted?
>>>>
>>>> Yes, I can introduce an apply_alternatives_nocheck on ARM that will
>>>> hava the same set of arguments on x86.
>>>>
>>>> Or I can make a new function name?
>>>
>>> Either way is fine with me, with a slight preference to the former
>>> one.
>>
>> I am fine with the prototype of the function apply_alternatives_nocheck but
>> I don't think the name is relevant for ARM.
>>
>> Is there any reason we don't want to call directly apply_alternatives in
>> x86?
>
> It assumes (and has an ASSERT) that it is called with interrupts disabled.
> And we don't need to do that (as during livepatch loading we can modify the
> livepatch payload without worrying about interrupts).

Oh, it makes more sense now.

>
> P.S.
> loading != applying.
>
> I could do a patch where we rename 'apply_alternatives' -> 'apply_alternatives_boot'
> and 'apply_alternatives_nocheck' to 'apply_alternatives'.
>
> Also the x86 version instead of having:
>
> apply_alternatives(struct alt_instr *start, struct alt_instr *end)
>
> would end up with:
>
> apply_alternatives(void *start, size_t length)

I would be fine with the prototype
apply_alternatives(struct alt_instr *start, struct alt_instr *end)

for ARM. It is up to you.

Cheers,
Andrew Cooper Aug. 15, 2016, 3:27 p.m. UTC | #8
On 15/08/16 16:25, Julien Grall wrote:
>
>
> On 15/08/2016 17:17, Konrad Rzeszutek Wilk wrote:
>> On Mon, Aug 15, 2016 at 04:57:26PM +0200, Julien Grall wrote:
>>> Hi Jan and Konrad,
>>>
>>> On 15/08/2016 16:23, Jan Beulich wrote:
>>>>>>> On 15.08.16 at 16:09, <konrad.wilk@oracle.com> wrote:
>>>>> On Mon, Aug 15, 2016 at 02:21:48AM -0600, Jan Beulich wrote:
>>>>>>>>> On 15.08.16 at 01:07, <konrad.wilk@oracle.com> wrote:
>>>>>>> @@ -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
>>>>>>
>>>>>> Conditionals like this are ugly - can't this be properly abstracted?
>>>>>
>>>>> Yes, I can introduce an apply_alternatives_nocheck on ARM that will
>>>>> hava the same set of arguments on x86.
>>>>>
>>>>> Or I can make a new function name?
>>>>
>>>> Either way is fine with me, with a slight preference to the former
>>>> one.
>>>
>>> I am fine with the prototype of the function
>>> apply_alternatives_nocheck but
>>> I don't think the name is relevant for ARM.
>>>
>>> Is there any reason we don't want to call directly
>>> apply_alternatives in
>>> x86?
>>
>> It assumes (and has an ASSERT) that it is called with interrupts
>> disabled.
>> And we don't need to do that (as during livepatch loading we can
>> modify the
>> livepatch payload without worrying about interrupts).
>
> Oh, it makes more sense now.
>
>>
>> P.S.
>> loading != applying.
>>
>> I could do a patch where we rename 'apply_alternatives' ->
>> 'apply_alternatives_boot'
>> and 'apply_alternatives_nocheck' to 'apply_alternatives'.

The only reason apply_alternatives() is named thusly is to match Linux. 
I am not fussed if it changes.

~Andrew

>>
>> Also the x86 version instead of having:
>>
>> apply_alternatives(struct alt_instr *start, struct alt_instr *end)
>>
>> would end up with:
>>
>> apply_alternatives(void *start, size_t length)
>
> I would be fine with the prototype
> apply_alternatives(struct alt_instr *start, struct alt_instr *end)
>
> for ARM. It is up to you.
>
> Cheers,
>
Konrad Rzeszutek Wilk Aug. 17, 2016, 1:50 a.m. UTC | #9
> > +int arch_livepatch_perform_rela(struct livepatch_elf *elf,
> > +                                const struct livepatch_elf_sec *base,
> > +                                const struct livepatch_elf_sec *rela)
> > +{
.. snip..
> > +        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;
> 
> As you borrow the code from Linux, could we keep the abstraction with
> reloc_data and defer the overflow check? It would avoid to have the same if
> in multiple place in this code.

The above 'if' conditional is a check to make sure that we don't
go past the section (sh_size). In other words it is a boundary check to
make sure the Elf file is not messed up.

I can still copy the reloc_data so we lessen the:
> > +            if ( (int64_t)val !=  *(int32_t *)dest )
> > +                err = -EOVERFLOW;

And such.
Julien Grall Aug. 17, 2016, 5:12 p.m. UTC | #10
Hi Konrad,

On 15/08/16 16:17, Julien Grall wrote:
> On 15/08/2016 01:07, Konrad Rzeszutek Wilk wrote:
>> +        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;
>
> Hmmm, I think we want to avoid the payload having
> R_AARCH64_ADR_PREL_PG_HI21* relocations due to the erratum #843419 on
> some Cortex-A53.
>
> I haven't yet looked at how you build the payload, but this may also
> affects the process to do it. I will give a look on it.

Actually, I am not sure I will have time to look at it during the next 
few weeks. Could you add a TODO for now?

Cheers,
Julien Grall Aug. 17, 2016, 6:22 p.m. UTC | #11
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
Konrad Rzeszutek Wilk Aug. 17, 2016, 6:57 p.m. UTC | #12
> > +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?

Ugh. This is a thorny one. I concur we need to check this - and better of
do it when we load the payload to make sure the distance is correct.

And that should also be on x86, so will spin of a seperate patch.

And in here add an ASSERT that the insn != AARCH64_BREAK_FAULT


.. snip..
> > -/* 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]?

Sure.
> 
> > +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".

OK.
> 
> >  {
> > +    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 :/.

Heheh.
> 
> 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 agree.
> 
> I forgot to mention it in the code, so I will send a patch to update it.

OK.
> 
> > +
> > +    /*
> > +     * 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.

OK.
> 
> >  }
> > 
> >  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.

LIVEPATCH_VMAP_START and LIVEPATCH_VMAP_END ?

> 
> > +
> > +    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.

That is not exposed on x86 thought.

I can expose HAVE_ALTERNATIVE on x86 and use that instead.

> 
> >      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

Would need to create an arch specific hook for this.

> define a new config option HAVE_ARCH_EX_TABLE to avoid architecture specific
> check in the common code.

That could do it too.
> 
> > @@ -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?

Of course.

> 
> >   *
> >   *   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
Julien Grall Aug. 17, 2016, 7:44 p.m. UTC | #13
On 17/08/2016 02:50, Konrad Rzeszutek Wilk wrote:
>>> +int arch_livepatch_perform_rela(struct livepatch_elf *elf,
>>> +                                const struct livepatch_elf_sec *base,
>>> +                                const struct livepatch_elf_sec *rela)
>>> +{
> .. snip..
>>> +        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;
>>
>> As you borrow the code from Linux, could we keep the abstraction with
>> reloc_data and defer the overflow check? It would avoid to have the same if
>> in multiple place in this code.
>
> The above 'if' conditional is a check to make sure that we don't
> go past the section (sh_size). In other words it is a boundary check to
> make sure the Elf file is not messed up.

Oh, Linux does not do those check. Sorry I though it was done. So I am 
fine with that.

Cheers,
Julien Grall Aug. 17, 2016, 7:50 p.m. UTC | #14
Hi Konrad,

On 17/08/2016 19:57, Konrad Rzeszutek Wilk wrote:
>>> +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?
>
> Ugh. This is a thorny one. I concur we need to check this - and better of
> do it when we load the payload to make sure the distance is correct.
>
> And that should also be on x86, so will spin of a seperate patch.
>
> And in here add an ASSERT that the insn != AARCH64_BREAK_FAULT

It sounds good to me.

[...]

>>> +
>>> +    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.
>
> LIVEPATCH_VMAP_START and LIVEPATCH_VMAP_END ?

Something like that.

>
>>
>>> +
>>> +    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.
>
> That is not exposed on x86 thought.
>
> I can expose HAVE_ALTERNATIVE on x86 and use that instead.

True. I would like to avoid using  CONFIG_ARM_* in the common code as it 
is a call to forget to remove the #ifdef when ARM32 will gain 
alternative patching.

You are the maintainer of this code, so it is your call :).

Regards,
Konrad Rzeszutek Wilk Aug. 22, 2016, 7:22 p.m. UTC | #15
> > > -/* 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]?
> 
> Sure.

The patch [1] changed where it does not have <len> anymore. And because
of that (and some other changes) this patch is the one that introduces
the #define. So I think having it in include/asm-arm/livepatch.h would
work. When I send out the patchset that hopefully should be more clarified.
..
> > [1]
> > https://lists.xenproject.org/archives/html/xen-devel/2016-08/msg01832.html
> > 
> > -- 
> > Julien Grall
Konrad Rzeszutek Wilk Aug. 24, 2016, 2:14 a.m. UTC | #16
>> +    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.

That code also flushes the I-cache (which sounds redundant with
invalidate_icache).
And it also does 'isb' ("Ensure synchronization with previous changes
to text ") which
invalidate_icache() does not.

I am wondering if should just move 'isb' in invalidate_icache() and
drop altogether the call to flush_xen_text_tlb_local()?

It works fine in simulator

>
> 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.

I had no trouble adding invalidate_icache to asm-arm/arm32/page.h and
having it do :

    asm volatile (
        CMD_CP32(ICIALLUIS)     /* Flush I-cache. */
        CMD_CP32(BPIALLIS)      /* Flush branch predictor. */
        : : : "memory");

But obviously that does not help ARM64.

And that is is where I hit an snag. "bp iallis" instruction does not
compile? (I also tried "bp iall", "bpiallis"). The error is:

{standard input}: Assembler messages:
{standard input}:454: Error: unknown mnemonic `bpiallis' -- `bpiallis'

Ideas?
Julien Grall Aug. 25, 2016, 1:54 p.m. UTC | #17
Hi Konrad,

On 23/08/2016 22:14, Konrad Rzeszutek Wilk wrote:
>>> +    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.
>
> That code also flushes the I-cache (which sounds redundant with
> invalidate_icache).
> And it also does 'isb' ("Ensure synchronization with previous changes
> to text ") which
> invalidate_icache() does not.
>
> I am wondering if should just move 'isb' in invalidate_icache() and
> drop altogether the call to flush_xen_text_tlb_local()?

flush_xen_text_tlb_local will flush the TLB for the local processor, I 
am not sure why we would want to do that. It would be better to have a 
invalidate_icache helpers on ARM32.

>
> It works fine in simulator
>
>>
>> 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.
>
> I had no trouble adding invalidate_icache to asm-arm/arm32/page.h and
> having it do :
>
>     asm volatile (
>         CMD_CP32(ICIALLUIS)     /* Flush I-cache. */
>         CMD_CP32(BPIALLIS)      /* Flush branch predictor. */
>         : : : "memory");
>
> But obviously that does not help ARM64.
>
> And that is is where I hit an snag. "bp iallis" instruction does not
> compile? (I also tried "bp iall", "bpiallis"). The error is:
>
> {standard input}: Assembler messages:
> {standard input}:454: Error: unknown mnemonic `bpiallis' -- `bpiallis'

The instruction bpiallis does not exist on ARM64 because the branch 
predictor does not require explicit flush.

Regards,
diff mbox

Patch

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 <xen/errno.h>
+#include <xen/lib.h>
+#include <xen/livepatch_elf.h>
+#include <xen/livepatch.h>
+
+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 <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);
+    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 <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.
  */
+
+#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
+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   <COMMON>
+ *   0  -  10M   <COMMON>
  *
  *  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>
  *
  *   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);