diff mbox series

[v2,4/6] aarch64: Introduce EL2 boot code for Armv8-R AArch64

Message ID 20240716142906.1502802-5-luca.fancellu@arm.com (mailing list archive)
State New, archived
Headers show
Series Add Armv8-R AArch64 support | expand

Commit Message

Luca Fancellu July 16, 2024, 2:29 p.m. UTC
The Armv8-R AArch64 profile does not support the EL3 exception level.
The Armv8-R AArch64 profile allows for an (optional) VMSAv8-64 MMU
at EL1, which allows to run off-the-shelf Linux. However EL2 only
supports a PMSA, which is not supported by Linux, so we need to drop
into EL1 before entering the kernel.

We add a new err_invalid_arch symbol as a dead loop. If we detect the
current Armv8-R aarch64 only supports with PMSA, meaning we cannot boot
Linux anymore, then we jump to err_invalid_arch.

During Armv8-R aarch64 init, to make sure nothing unexpected traps into
EL2, we auto-detect and config FIEN and EnSCXT in HCR_EL2.

The boot sequence is:
If CurrentEL == EL3, then goto EL3 initialisation and drop to lower EL
  before entering the kernel.
If CurrentEL == EL2 && id_aa64mmfr0_el1.MSA == 0xf (Armv8-R aarch64),
  if id_aa64mmfr0_el1.MSA_frac == 0x2,
    then goto Armv8-R AArch64 initialisation and drop to EL1 before
    entering the kernel.
  else, which means VMSA unsupported and cannot boot Linux,
    goto err_invalid_arch (dead loop).
Else, no initialisation and keep the current EL before entering the
  kernel.

Signed-off-by: Luca Fancellu <luca.fancellu@arm.com>
---
v2 changes:
 - when booting from aarch64 armv8-r EL2, jump to reset_no_el3 to
   avoid code duplication.
 - codestyle fixes
 - write into HCR_EL2.ENSCXT unconditionally inside cpu_init_armv8r_el2
---
 arch/aarch64/boot.S            | 57 ++++++++++++++++++++++++++++++++--
 arch/aarch64/include/asm/cpu.h | 11 +++++++
 arch/aarch64/init.c            | 29 +++++++++++++++++
 3 files changed, 95 insertions(+), 2 deletions(-)

Comments

Mark Rutland July 29, 2024, 3:01 p.m. UTC | #1
Hi Luca,

On Tue, Jul 16, 2024 at 03:29:04PM +0100, Luca Fancellu wrote:
> The Armv8-R AArch64 profile does not support the EL3 exception level.
> The Armv8-R AArch64 profile allows for an (optional) VMSAv8-64 MMU
> at EL1, which allows to run off-the-shelf Linux. However EL2 only
> supports a PMSA, which is not supported by Linux, so we need to drop
> into EL1 before entering the kernel.
> 
> We add a new err_invalid_arch symbol as a dead loop. If we detect the
> current Armv8-R aarch64 only supports with PMSA, meaning we cannot boot
> Linux anymore, then we jump to err_invalid_arch.
> 
> During Armv8-R aarch64 init, to make sure nothing unexpected traps into
> EL2, we auto-detect and config FIEN and EnSCXT in HCR_EL2.
> 
> The boot sequence is:
> If CurrentEL == EL3, then goto EL3 initialisation and drop to lower EL
>   before entering the kernel.
> If CurrentEL == EL2 && id_aa64mmfr0_el1.MSA == 0xf (Armv8-R aarch64),
>   if id_aa64mmfr0_el1.MSA_frac == 0x2,
>     then goto Armv8-R AArch64 initialisation and drop to EL1 before
>     entering the kernel.
>   else, which means VMSA unsupported and cannot boot Linux,
>     goto err_invalid_arch (dead loop).
> Else, no initialisation and keep the current EL before entering the
>   kernel.
> 
> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com>
> ---
> v2 changes:
>  - when booting from aarch64 armv8-r EL2, jump to reset_no_el3 to
>    avoid code duplication.
>  - codestyle fixes
>  - write into HCR_EL2.ENSCXT unconditionally inside cpu_init_armv8r_el2
> ---
>  arch/aarch64/boot.S            | 57 ++++++++++++++++++++++++++++++++--
>  arch/aarch64/include/asm/cpu.h | 11 +++++++
>  arch/aarch64/init.c            | 29 +++++++++++++++++
>  3 files changed, 95 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S
> index 211077af17c8..2a8234f7a17d 100644
> --- a/arch/aarch64/boot.S
> +++ b/arch/aarch64/boot.S
> @@ -22,7 +22,8 @@
>  	 *   EL2 must be implemented.
>  	 *
>  	 * - EL2 (Non-secure)
> -	 *   Entering at EL2 is partially supported.
> +	 *   Entering at EL2 is partially supported for Armv8-A.
> +	 *   Entering at EL2 is supported for Armv8-R.

Nit: IIUC ARMv8-R is Secure-only, so this isn't quite right.

>  	 *   PSCI is not supported when entered in this exception level.
>  	 */
>  ASM_FUNC(_start)
> @@ -76,6 +77,39 @@ reset_at_el2:
>  	msr	sctlr_el2, x0
>  	isb
>  
> +	/* Detect Armv8-R AArch64 */
> +	mrs	x1, id_aa64mmfr0_el1
> +	/*
> +	 * Check MSA, bits [51:48]:
> +	 * 0xf means Armv8-R AArch64.
> +	 * If not 0xf, proceed in Armv8-A EL2.
> +	 */
> +	ubfx	x0, x1, #48, #4			// MSA
> +	cmp	x0, 0xf
> +	bne	reset_no_el3
> +
> +	/*
> +	 * Armv8-R AArch64 is found, check if Linux can be booted.
> +	 * Check MSA_frac, bits [55:52]:
> +	 * 0x2 means EL1&0 translation regime also supports VMSAv8-64.
> +	 */
> +	ubfx	x0, x1, #52, #4			// MSA_frac
> +	cmp	x0, 0x2
> +	/*
> +	 * If not 0x2, no VMSA, so cannot boot Linux and dead loop.
> +	 * Also, since the architecture guarantees that those CPUID
> +	 * fields never lose features when the value in a field
> +	 * increases, we use blt to cover it.
> +	 */
> +	blt	err_invalid_arch
> +
> +	/* Start Armv8-R Linux at EL1 */
> +	mov	w0, #SPSR_KERNEL_EL1
> +	ldr	x1, =spsr_to_elx
> +	str	w0, [x1]

I'd prefer if we could do this in C code. I'll post a series shortly
where we'll have consistent cpu_init_arch() hook that we can do this
under.

> +
> +	bl	cpu_init_armv8r_el2
> +
>  	b	reset_no_el3
>  
>  	/*
> @@ -95,15 +129,22 @@ reset_no_el3:
>  	b.eq	err_invalid_id
>  	bl	setup_stack
>  
> +	ldr	w1, spsr_to_elx
> +	and w0, w1, 0xf
> +	cmp	w0, #SPSR_EL1H
> +	b.eq drop_el
> +
>  	mov	w0, #1
>  	ldr	x1, =flag_keep_el
>  	str	w0, [x1]
>  
> +drop_el:
>  	bl	cpu_init_bootwrapper
>  
>  	b	start_bootmethod
>  
>  err_invalid_id:
> +err_invalid_arch:
>  	b	.
>  
>  	/*
> @@ -121,10 +162,14 @@ ASM_FUNC(jump_kernel)
>  	ldr	x0, =SCTLR_EL1_KERNEL
>  	msr	sctlr_el1, x0
>  
> +	mrs	x5, CurrentEL
> +	cmp	x5, #CURRENTEL_EL2
> +	b.eq	1f
> +
>  	ldr	x0, =SCTLR_EL2_KERNEL
>  	msr	sctlr_el2, x0
>  
> -	cpuid	x0, x1
> +1:	cpuid	x0, x1
>  	bl	find_logical_id
>  	bl	setup_stack		// Reset stack pointer
>  
> @@ -147,10 +192,18 @@ ASM_FUNC(jump_kernel)
>  	 */
>  	bfi	x4, x19, #5, #1
>  
> +	mrs	x5, CurrentEL
> +	cmp	x5, #CURRENTEL_EL2
> +	b.eq	1f
> +
>  	msr	elr_el3, x19
>  	msr	spsr_el3, x4
>  	eret
>  
> +1:	msr	elr_el2, x19
> +	msr	spsr_el2, x4
> +	eret
> +
>  	.ltorg
>  
>  	.data
> diff --git a/arch/aarch64/include/asm/cpu.h b/arch/aarch64/include/asm/cpu.h
> index 846b89f8405d..280f488f267d 100644
> --- a/arch/aarch64/include/asm/cpu.h
> +++ b/arch/aarch64/include/asm/cpu.h
> @@ -58,7 +58,13 @@
>  #define SCR_EL3_TCR2EN			BIT(43)
>  #define SCR_EL3_PIEN			BIT(45)
>  
> +#define VTCR_EL2_MSA			BIT(31)
> +
>  #define HCR_EL2_RES1			BIT(1)
> +#define HCR_EL2_APK_NOTRAP		BIT(40)
> +#define HCR_EL2_API_NOTRAP		BIT(41)
> +#define HCR_EL2_FIEN_NOTRAP		BIT(47)
> +#define HCR_EL2_ENSCXT_NOTRAP		BIT(53)
>  
>  #define ID_AA64DFR0_EL1_PMSVER		BITS(35, 32)
>  #define ID_AA64DFR0_EL1_TRACEBUFFER	BITS(47, 44)
> @@ -88,7 +94,10 @@
>  
>  #define ID_AA64PFR1_EL1_MTE		BITS(11, 8)
>  #define ID_AA64PFR1_EL1_SME		BITS(27, 24)
> +#define ID_AA64PFR1_EL1_CSV2_frac	BITS(35, 32)
> +#define ID_AA64PFR0_EL1_RAS		BITS(31, 28)
>  #define ID_AA64PFR0_EL1_SVE		BITS(35, 32)
> +#define ID_AA64PFR0_EL1_CSV2		BITS(59, 56)
>  
>  #define ID_AA64SMFR0_EL1		s3_0_c0_c4_5
>  #define ID_AA64SMFR0_EL1_FA64		BIT(63)
> @@ -114,6 +123,7 @@
>  #define SPSR_I			(1 << 7)	/* IRQ masked */
>  #define SPSR_F			(1 << 6)	/* FIQ masked */
>  #define SPSR_T			(1 << 5)	/* Thumb */
> +#define SPSR_EL1H		(5 << 0)	/* EL1 Handler mode */
>  #define SPSR_EL2H		(9 << 0)	/* EL2 Handler mode */
>  #define SPSR_HYP		(0x1a << 0)	/* M[3:0] = hyp, M[4] = AArch32 */
>  
> @@ -153,6 +163,7 @@
>  #else
>  #define SCTLR_EL1_KERNEL	SCTLR_EL1_RES1
>  #define SPSR_KERNEL		(SPSR_A | SPSR_D | SPSR_I | SPSR_F | SPSR_EL2H)
> +#define SPSR_KERNEL_EL1		(SPSR_A | SPSR_D | SPSR_I | SPSR_F | SPSR_EL1H)
>  #endif
>  
>  #ifndef __ASSEMBLY__
> diff --git a/arch/aarch64/init.c b/arch/aarch64/init.c
> index 37cb45fde446..9402a01b9dca 100644
> --- a/arch/aarch64/init.c
> +++ b/arch/aarch64/init.c
> @@ -145,6 +145,35 @@ void cpu_init_el3(void)
>  	msr(CNTFRQ_EL0, COUNTER_FREQ);
>  }
>  
> +void cpu_init_armv8r_el2(void)
> +{
> +	unsigned long hcr = mrs(hcr_el2);
> +
> +	msr(vpidr_el2, mrs(midr_el1));
> +	msr(vmpidr_el2, mrs(mpidr_el1));
> +
> +	/* VTCR_MSA: VMSAv8-64 support */
> +	msr(vtcr_el2, VTCR_EL2_MSA);

I suspect we also need to initialize VSTCR_EL2?

... and don't we also need to initialize VSCTLR_EL2 to give all CPUs the
same VMID? Otherwise barriers won't work at EL1 and below...

> +
> +	/*
> +	 * HCR_EL2.ENSCXT is written unconditionally even if in some cases it's
> +	 * RES0 (when FEAT_CSV2_2 or FEAT_CSV2_1p2 are not implemented) in order
> +	 * to simplify the code, but it's safe in this case as the write would be
> +	 * ignored when not implemented and would remove the trap otherwise.
> +	 */
> +	hcr |= HCR_EL2_ENSCXT_NOTRAP;

I'd prefer if we can do the necessary checks. IIUC we can do this with a
helper, e.g.

	static bool cpu_has_scxt(void)
	{
		unsigned long csv2 = mrs_field(ID_AA64PFR0_EL1, CSV2);
		if (csv2 >= 2)
			return true;
		if (csv2 < 1)
			return false;
		return mrs_field(ID_AA64PFR1_EL1, CSV2_frac) >= 2;
	}

... then here we can have:

	if (cpu_has_scxt())
		 hcr |= HCR_EL2_ENSCXT_NOTRAP;

> +
> +	if (mrs_field(ID_AA64PFR0_EL1, RAS) >= 2)
> +		hcr |= HCR_EL2_FIEN_NOTRAP;
> +
> +	if (cpu_has_pauth())
> +		hcr |= HCR_EL2_APK_NOTRAP | HCR_EL2_API_NOTRAP;
> +
> +	msr(hcr_el2, hcr);
> +	isb();
> +	msr(CNTFRQ_EL0, COUNTER_FREQ);
> +}

I believe we also need to initialize:
	
* CNTVOFF_EL2 (for timers to work correctly)
* CNTHCTL_EL2 (for timers to not trap)
* CPTR_EL2 (for FP to not trap)
* MDCR_EL2 (for PMU & debug to not trap)

Mark.

> +
>  #ifdef PSCI
>  extern char psci_vectors[];
>  
> -- 
> 2.34.1
>
Luca Fancellu July 29, 2024, 3:27 p.m. UTC | #2
Hi Mark,

>> * - EL2 (Non-secure)
>> - *   Entering at EL2 is partially supported.
>> + *   Entering at EL2 is partially supported for Armv8-A.
>> + *   Entering at EL2 is supported for Armv8-R.
> 
> Nit: IIUC ARMv8-R is Secure-only, so this isn't quite right.

Ok I’ll drop this change

> 
>> *   PSCI is not supported when entered in this exception level.
>> */
>> ASM_FUNC(_start)
>> @@ -76,6 +77,39 @@ reset_at_el2:
>> msr sctlr_el2, x0
>> isb
>> 
>> + /* Detect Armv8-R AArch64 */
>> + mrs x1, id_aa64mmfr0_el1
>> + /*
>> + * Check MSA, bits [51:48]:
>> + * 0xf means Armv8-R AArch64.
>> + * If not 0xf, proceed in Armv8-A EL2.
>> + */
>> + ubfx x0, x1, #48, #4 // MSA
>> + cmp x0, 0xf
>> + bne reset_no_el3
>> +
>> + /*
>> + * Armv8-R AArch64 is found, check if Linux can be booted.
>> + * Check MSA_frac, bits [55:52]:
>> + * 0x2 means EL1&0 translation regime also supports VMSAv8-64.
>> + */
>> + ubfx x0, x1, #52, #4 // MSA_frac
>> + cmp x0, 0x2
>> + /*
>> + * If not 0x2, no VMSA, so cannot boot Linux and dead loop.
>> + * Also, since the architecture guarantees that those CPUID
>> + * fields never lose features when the value in a field
>> + * increases, we use blt to cover it.
>> + */
>> + blt err_invalid_arch
>> +
>> + /* Start Armv8-R Linux at EL1 */
>> + mov w0, #SPSR_KERNEL_EL1
>> + ldr x1, =spsr_to_elx
>> + str w0, [x1]
> 
> I'd prefer if we could do this in C code. I'll post a series shortly
> where we'll have consistent cpu_init_arch() hook that we can do this
> under.

Ok are you suggesting to base this serie on the one you’ll push?


>> 
>> 
>> +void cpu_init_armv8r_el2(void)
>> +{
>> + unsigned long hcr = mrs(hcr_el2);
>> +
>> + msr(vpidr_el2, mrs(midr_el1));
>> + msr(vmpidr_el2, mrs(mpidr_el1));
>> +
>> + /* VTCR_MSA: VMSAv8-64 support */
>> + msr(vtcr_el2, VTCR_EL2_MSA);
> 
> I suspect we also need to initialize VSTCR_EL2?

Ok, I’ve booted Linux and it seems to work fine, is this considered at all when HCR_EL2.VM is off?
Anyway I’ll initialise it, I noticed it’s not done in TF-A.

> 
> ... and don't we also need to initialize VSCTLR_EL2 to give all CPUs the
> same VMID? Otherwise barriers won't work at EL1 and below...

I can see TF-A is initialising it so I’ll do the same

> 
>> +
>> + /*
>> + * HCR_EL2.ENSCXT is written unconditionally even if in some cases it's
>> + * RES0 (when FEAT_CSV2_2 or FEAT_CSV2_1p2 are not implemented) in order
>> + * to simplify the code, but it's safe in this case as the write would be
>> + * ignored when not implemented and would remove the trap otherwise.
>> + */
>> + hcr |= HCR_EL2_ENSCXT_NOTRAP;
> 
> I'd prefer if we can do the necessary checks. IIUC we can do this with a
> helper, e.g.
> 
> static bool cpu_has_scxt(void)
> {
> unsigned long csv2 = mrs_field(ID_AA64PFR0_EL1, CSV2);
> if (csv2 >= 2)
> return true;
> if (csv2 < 1)
> return false;
> return mrs_field(ID_AA64PFR1_EL1, CSV2_frac) >= 2;
> }
> 
> ... then here we can have:
> 
> if (cpu_has_scxt())
> hcr |= HCR_EL2_ENSCXT_NOTRAP;

Ok I’ll do

> 
>> +
>> + if (mrs_field(ID_AA64PFR0_EL1, RAS) >= 2)
>> + hcr |= HCR_EL2_FIEN_NOTRAP;
>> +
>> + if (cpu_has_pauth())
>> + hcr |= HCR_EL2_APK_NOTRAP | HCR_EL2_API_NOTRAP;
>> +
>> + msr(hcr_el2, hcr);
>> + isb();
>> + msr(CNTFRQ_EL0, COUNTER_FREQ);
>> +}
> 
> I believe we also need to initialize:
> 
> * CNTVOFF_EL2 (for timers to work correctly)
> * CNTHCTL_EL2 (for timers to not trap)
> * CPTR_EL2 (for FP to not trap)
> * MDCR_EL2 (for PMU & debug to not trap)

Sure, I’ll reset them like in TF-A.

Thanks fo your review!

Cheers,
Luca
Mark Rutland July 29, 2024, 4:14 p.m. UTC | #3
On Mon, Jul 29, 2024 at 04:27:37PM +0100, Luca Fancellu wrote:
> Hi Mark,
> 
> >> * - EL2 (Non-secure)
> >> - *   Entering at EL2 is partially supported.
> >> + *   Entering at EL2 is partially supported for Armv8-A.
> >> + *   Entering at EL2 is supported for Armv8-R.
> >
> > Nit: IIUC ARMv8-R is Secure-only, so this isn't quite right.
> 
> Ok I’ll drop this change
> 
> >
> >> *   PSCI is not supported when entered in this exception level.
> >> */
> >> ASM_FUNC(_start)
> >> @@ -76,6 +77,39 @@ reset_at_el2:
> >> msr sctlr_el2, x0
> >> isb
> >>
> >> + /* Detect Armv8-R AArch64 */
> >> + mrs x1, id_aa64mmfr0_el1
> >> + /*
> >> + * Check MSA, bits [51:48]:
> >> + * 0xf means Armv8-R AArch64.
> >> + * If not 0xf, proceed in Armv8-A EL2.
> >> + */
> >> + ubfx x0, x1, #48, #4 // MSA
> >> + cmp x0, 0xf
> >> + bne reset_no_el3
> >> +
> >> + /*
> >> + * Armv8-R AArch64 is found, check if Linux can be booted.
> >> + * Check MSA_frac, bits [55:52]:
> >> + * 0x2 means EL1&0 translation regime also supports VMSAv8-64.
> >> + */
> >> + ubfx x0, x1, #52, #4 // MSA_frac
> >> + cmp x0, 0x2
> >> + /*
> >> + * If not 0x2, no VMSA, so cannot boot Linux and dead loop.
> >> + * Also, since the architecture guarantees that those CPUID
> >> + * fields never lose features when the value in a field
> >> + * increases, we use blt to cover it.
> >> + */
> >> + blt err_invalid_arch
> >> +
> >> + /* Start Armv8-R Linux at EL1 */
> >> + mov w0, #SPSR_KERNEL_EL1
> >> + ldr x1, =spsr_to_elx
> >> + str w0, [x1]
> >
> > I'd prefer if we could do this in C code. I'll post a series shortly
> > where we'll have consistent cpu_init_arch() hook that we can do this
> > under.
> 
> Ok are you suggesting to base this serie on the one you’ll push?

Sorry; yes -- I'll send that out shortly, and I'd like to take that as a
base.

> >> +void cpu_init_armv8r_el2(void)
> >> +{
> >> + unsigned long hcr = mrs(hcr_el2);
> >> +
> >> + msr(vpidr_el2, mrs(midr_el1));
> >> + msr(vmpidr_el2, mrs(mpidr_el1));
> >> +
> >> + /* VTCR_MSA: VMSAv8-64 support */
> >> + msr(vtcr_el2, VTCR_EL2_MSA);
> >
> > I suspect we also need to initialize VSTCR_EL2?
> 
> Ok, I’ve booted Linux and it seems to work fine, is this considered at all when HCR_EL2.VM is off?
> Anyway I’ll initialise it, I noticed it’s not done in TF-A.

I don't know; the ARMv8-R manual (ARM DDI 0600B.a) says in E1.2.3 DSB:

| The ordering requirements of Data Synchronization Barrier instruction is as
| follows:
| * EL1 and EL0 memory accesses are ordered only with respect to memory accesses
|   using the same VMID.
| * EL2 memory accesses are ordered only with respect to other EL2 memory
|   accesses.

... which seems to apply regardless of HCR_EL2.VM?

It's probably worth clarifying with the relevant architects.

> > ... and don't we also need to initialize VSCTLR_EL2 to give all CPUs the
> > same VMID? Otherwise barriers won't work at EL1 and below...
> 
> I can see TF-A is initialising it so I’ll do the same

Great; thanks!


> 
> >
> >> +
> >> + /*
> >> + * HCR_EL2.ENSCXT is written unconditionally even if in some cases it's
> >> + * RES0 (when FEAT_CSV2_2 or FEAT_CSV2_1p2 are not implemented) in order
> >> + * to simplify the code, but it's safe in this case as the write would be
> >> + * ignored when not implemented and would remove the trap otherwise.
> >> + */
> >> + hcr |= HCR_EL2_ENSCXT_NOTRAP;
> >
> > I'd prefer if we can do the necessary checks. IIUC we can do this with a
> > helper, e.g.
> >
> > static bool cpu_has_scxt(void)
> > {
> > unsigned long csv2 = mrs_field(ID_AA64PFR0_EL1, CSV2);
> > if (csv2 >= 2)
> > return true;
> > if (csv2 < 1)
> > return false;
> > return mrs_field(ID_AA64PFR1_EL1, CSV2_frac) >= 2;
> > }
> >
> > ... then here we can have:
> >
> > if (cpu_has_scxt())
> > hcr |= HCR_EL2_ENSCXT_NOTRAP;
> 
> Ok I’ll do
> 
> >
> >> +
> >> + if (mrs_field(ID_AA64PFR0_EL1, RAS) >= 2)
> >> + hcr |= HCR_EL2_FIEN_NOTRAP;
> >> +
> >> + if (cpu_has_pauth())
> >> + hcr |= HCR_EL2_APK_NOTRAP | HCR_EL2_API_NOTRAP;
> >> +
> >> + msr(hcr_el2, hcr);
> >> + isb();
> >> + msr(CNTFRQ_EL0, COUNTER_FREQ);
> >> +}
> >
> > I believe we also need to initialize:
> >
> > * CNTVOFF_EL2 (for timers to work correctly)
> > * CNTHCTL_EL2 (for timers to not trap)
> > * CPTR_EL2 (for FP to not trap)
> > * MDCR_EL2 (for PMU & debug to not trap)
> 
> Sure, I’ll reset them like in TF-A.

Perfect!

Mark.
diff mbox series

Patch

diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S
index 211077af17c8..2a8234f7a17d 100644
--- a/arch/aarch64/boot.S
+++ b/arch/aarch64/boot.S
@@ -22,7 +22,8 @@ 
 	 *   EL2 must be implemented.
 	 *
 	 * - EL2 (Non-secure)
-	 *   Entering at EL2 is partially supported.
+	 *   Entering at EL2 is partially supported for Armv8-A.
+	 *   Entering at EL2 is supported for Armv8-R.
 	 *   PSCI is not supported when entered in this exception level.
 	 */
 ASM_FUNC(_start)
@@ -76,6 +77,39 @@  reset_at_el2:
 	msr	sctlr_el2, x0
 	isb
 
+	/* Detect Armv8-R AArch64 */
+	mrs	x1, id_aa64mmfr0_el1
+	/*
+	 * Check MSA, bits [51:48]:
+	 * 0xf means Armv8-R AArch64.
+	 * If not 0xf, proceed in Armv8-A EL2.
+	 */
+	ubfx	x0, x1, #48, #4			// MSA
+	cmp	x0, 0xf
+	bne	reset_no_el3
+
+	/*
+	 * Armv8-R AArch64 is found, check if Linux can be booted.
+	 * Check MSA_frac, bits [55:52]:
+	 * 0x2 means EL1&0 translation regime also supports VMSAv8-64.
+	 */
+	ubfx	x0, x1, #52, #4			// MSA_frac
+	cmp	x0, 0x2
+	/*
+	 * If not 0x2, no VMSA, so cannot boot Linux and dead loop.
+	 * Also, since the architecture guarantees that those CPUID
+	 * fields never lose features when the value in a field
+	 * increases, we use blt to cover it.
+	 */
+	blt	err_invalid_arch
+
+	/* Start Armv8-R Linux at EL1 */
+	mov	w0, #SPSR_KERNEL_EL1
+	ldr	x1, =spsr_to_elx
+	str	w0, [x1]
+
+	bl	cpu_init_armv8r_el2
+
 	b	reset_no_el3
 
 	/*
@@ -95,15 +129,22 @@  reset_no_el3:
 	b.eq	err_invalid_id
 	bl	setup_stack
 
+	ldr	w1, spsr_to_elx
+	and w0, w1, 0xf
+	cmp	w0, #SPSR_EL1H
+	b.eq drop_el
+
 	mov	w0, #1
 	ldr	x1, =flag_keep_el
 	str	w0, [x1]
 
+drop_el:
 	bl	cpu_init_bootwrapper
 
 	b	start_bootmethod
 
 err_invalid_id:
+err_invalid_arch:
 	b	.
 
 	/*
@@ -121,10 +162,14 @@  ASM_FUNC(jump_kernel)
 	ldr	x0, =SCTLR_EL1_KERNEL
 	msr	sctlr_el1, x0
 
+	mrs	x5, CurrentEL
+	cmp	x5, #CURRENTEL_EL2
+	b.eq	1f
+
 	ldr	x0, =SCTLR_EL2_KERNEL
 	msr	sctlr_el2, x0
 
-	cpuid	x0, x1
+1:	cpuid	x0, x1
 	bl	find_logical_id
 	bl	setup_stack		// Reset stack pointer
 
@@ -147,10 +192,18 @@  ASM_FUNC(jump_kernel)
 	 */
 	bfi	x4, x19, #5, #1
 
+	mrs	x5, CurrentEL
+	cmp	x5, #CURRENTEL_EL2
+	b.eq	1f
+
 	msr	elr_el3, x19
 	msr	spsr_el3, x4
 	eret
 
+1:	msr	elr_el2, x19
+	msr	spsr_el2, x4
+	eret
+
 	.ltorg
 
 	.data
diff --git a/arch/aarch64/include/asm/cpu.h b/arch/aarch64/include/asm/cpu.h
index 846b89f8405d..280f488f267d 100644
--- a/arch/aarch64/include/asm/cpu.h
+++ b/arch/aarch64/include/asm/cpu.h
@@ -58,7 +58,13 @@ 
 #define SCR_EL3_TCR2EN			BIT(43)
 #define SCR_EL3_PIEN			BIT(45)
 
+#define VTCR_EL2_MSA			BIT(31)
+
 #define HCR_EL2_RES1			BIT(1)
+#define HCR_EL2_APK_NOTRAP		BIT(40)
+#define HCR_EL2_API_NOTRAP		BIT(41)
+#define HCR_EL2_FIEN_NOTRAP		BIT(47)
+#define HCR_EL2_ENSCXT_NOTRAP		BIT(53)
 
 #define ID_AA64DFR0_EL1_PMSVER		BITS(35, 32)
 #define ID_AA64DFR0_EL1_TRACEBUFFER	BITS(47, 44)
@@ -88,7 +94,10 @@ 
 
 #define ID_AA64PFR1_EL1_MTE		BITS(11, 8)
 #define ID_AA64PFR1_EL1_SME		BITS(27, 24)
+#define ID_AA64PFR1_EL1_CSV2_frac	BITS(35, 32)
+#define ID_AA64PFR0_EL1_RAS		BITS(31, 28)
 #define ID_AA64PFR0_EL1_SVE		BITS(35, 32)
+#define ID_AA64PFR0_EL1_CSV2		BITS(59, 56)
 
 #define ID_AA64SMFR0_EL1		s3_0_c0_c4_5
 #define ID_AA64SMFR0_EL1_FA64		BIT(63)
@@ -114,6 +123,7 @@ 
 #define SPSR_I			(1 << 7)	/* IRQ masked */
 #define SPSR_F			(1 << 6)	/* FIQ masked */
 #define SPSR_T			(1 << 5)	/* Thumb */
+#define SPSR_EL1H		(5 << 0)	/* EL1 Handler mode */
 #define SPSR_EL2H		(9 << 0)	/* EL2 Handler mode */
 #define SPSR_HYP		(0x1a << 0)	/* M[3:0] = hyp, M[4] = AArch32 */
 
@@ -153,6 +163,7 @@ 
 #else
 #define SCTLR_EL1_KERNEL	SCTLR_EL1_RES1
 #define SPSR_KERNEL		(SPSR_A | SPSR_D | SPSR_I | SPSR_F | SPSR_EL2H)
+#define SPSR_KERNEL_EL1		(SPSR_A | SPSR_D | SPSR_I | SPSR_F | SPSR_EL1H)
 #endif
 
 #ifndef __ASSEMBLY__
diff --git a/arch/aarch64/init.c b/arch/aarch64/init.c
index 37cb45fde446..9402a01b9dca 100644
--- a/arch/aarch64/init.c
+++ b/arch/aarch64/init.c
@@ -145,6 +145,35 @@  void cpu_init_el3(void)
 	msr(CNTFRQ_EL0, COUNTER_FREQ);
 }
 
+void cpu_init_armv8r_el2(void)
+{
+	unsigned long hcr = mrs(hcr_el2);
+
+	msr(vpidr_el2, mrs(midr_el1));
+	msr(vmpidr_el2, mrs(mpidr_el1));
+
+	/* VTCR_MSA: VMSAv8-64 support */
+	msr(vtcr_el2, VTCR_EL2_MSA);
+
+	/*
+	 * HCR_EL2.ENSCXT is written unconditionally even if in some cases it's
+	 * RES0 (when FEAT_CSV2_2 or FEAT_CSV2_1p2 are not implemented) in order
+	 * to simplify the code, but it's safe in this case as the write would be
+	 * ignored when not implemented and would remove the trap otherwise.
+	 */
+	hcr |= HCR_EL2_ENSCXT_NOTRAP;
+
+	if (mrs_field(ID_AA64PFR0_EL1, RAS) >= 2)
+		hcr |= HCR_EL2_FIEN_NOTRAP;
+
+	if (cpu_has_pauth())
+		hcr |= HCR_EL2_APK_NOTRAP | HCR_EL2_API_NOTRAP;
+
+	msr(hcr_el2, hcr);
+	isb();
+	msr(CNTFRQ_EL0, COUNTER_FREQ);
+}
+
 #ifdef PSCI
 extern char psci_vectors[];