Message ID | 20240716142906.1502802-5-luca.fancellu@arm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add Armv8-R AArch64 support | expand |
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 >
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
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 --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[];
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(-)