arm64: Cope with CPUs stuck in VHE mode
It seems that the CPUs part of the SoC known as Apple M1 have the terrible habit of being stuck with HCR_EL2.E2H==1, in violation of the architecture. Try and work around this deplorable state of affairs by detecting the stuck bit early and short-circuit the nVHE dance. Additional filtering code ensures that attempts at switching to nVHE from the command-line are also ignored. It is still unknown whether there are many more such nuggets to be found... Reported-by: Hector Martin <marcan@marcan.st> Acked-by: Will Deacon <will@kernel.org> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210408131010.1109027-3-maz@kernel.org Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Родитель
cac642c12a
Коммит
31a32b49b8
|
@ -477,14 +477,13 @@ EXPORT_SYMBOL(kimage_vaddr)
|
|||
* booted in EL1 or EL2 respectively.
|
||||
*/
|
||||
SYM_FUNC_START(init_kernel_el)
|
||||
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
||||
msr sctlr_el1, x0
|
||||
|
||||
mrs x0, CurrentEL
|
||||
cmp x0, #CurrentEL_EL2
|
||||
b.eq init_el2
|
||||
|
||||
SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)
|
||||
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
||||
msr sctlr_el1, x0
|
||||
isb
|
||||
mov_q x0, INIT_PSTATE_EL1
|
||||
msr spsr_el1, x0
|
||||
|
@ -504,9 +503,43 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
|
|||
msr vbar_el2, x0
|
||||
isb
|
||||
|
||||
/*
|
||||
* Fruity CPUs seem to have HCR_EL2.E2H set to RES1,
|
||||
* making it impossible to start in nVHE mode. Is that
|
||||
* compliant with the architecture? Absolutely not!
|
||||
*/
|
||||
mrs x0, hcr_el2
|
||||
and x0, x0, #HCR_E2H
|
||||
cbz x0, 1f
|
||||
|
||||
/* Switching to VHE requires a sane SCTLR_EL1 as a start */
|
||||
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
||||
msr_s SYS_SCTLR_EL12, x0
|
||||
|
||||
/*
|
||||
* Force an eret into a helper "function", and let it return
|
||||
* to our original caller... This makes sure that we have
|
||||
* initialised the basic PSTATE state.
|
||||
*/
|
||||
mov x0, #INIT_PSTATE_EL2
|
||||
msr spsr_el1, x0
|
||||
adr x0, __cpu_stick_to_vhe
|
||||
msr elr_el1, x0
|
||||
eret
|
||||
|
||||
1:
|
||||
mov_q x0, INIT_SCTLR_EL1_MMU_OFF
|
||||
msr sctlr_el1, x0
|
||||
|
||||
msr elr_el2, lr
|
||||
mov w0, #BOOT_CPU_MODE_EL2
|
||||
eret
|
||||
|
||||
__cpu_stick_to_vhe:
|
||||
mov x0, #HVC_VHE_RESTART
|
||||
hvc #0
|
||||
mov x0, #BOOT_CPU_MODE_EL2
|
||||
ret
|
||||
SYM_FUNC_END(init_kernel_el)
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,12 +27,12 @@ SYM_CODE_START(__hyp_stub_vectors)
|
|||
ventry el2_fiq_invalid // FIQ EL2t
|
||||
ventry el2_error_invalid // Error EL2t
|
||||
|
||||
ventry el2_sync_invalid // Synchronous EL2h
|
||||
ventry elx_sync // Synchronous EL2h
|
||||
ventry el2_irq_invalid // IRQ EL2h
|
||||
ventry el2_fiq_invalid // FIQ EL2h
|
||||
ventry el2_error_invalid // Error EL2h
|
||||
|
||||
ventry el1_sync // Synchronous 64-bit EL1
|
||||
ventry elx_sync // Synchronous 64-bit EL1
|
||||
ventry el1_irq_invalid // IRQ 64-bit EL1
|
||||
ventry el1_fiq_invalid // FIQ 64-bit EL1
|
||||
ventry el1_error_invalid // Error 64-bit EL1
|
||||
|
@ -45,7 +45,7 @@ SYM_CODE_END(__hyp_stub_vectors)
|
|||
|
||||
.align 11
|
||||
|
||||
SYM_CODE_START_LOCAL(el1_sync)
|
||||
SYM_CODE_START_LOCAL(elx_sync)
|
||||
cmp x0, #HVC_SET_VECTORS
|
||||
b.ne 1f
|
||||
msr vbar_el2, x1
|
||||
|
@ -71,7 +71,7 @@ SYM_CODE_START_LOCAL(el1_sync)
|
|||
|
||||
9: mov x0, xzr
|
||||
eret
|
||||
SYM_CODE_END(el1_sync)
|
||||
SYM_CODE_END(elx_sync)
|
||||
|
||||
// nVHE? No way! Give me the real thing!
|
||||
SYM_CODE_START_LOCAL(mutate_to_vhe)
|
||||
|
|
|
@ -29,11 +29,22 @@ struct ftr_set_desc {
|
|||
} fields[];
|
||||
};
|
||||
|
||||
static bool __init mmfr1_vh_filter(u64 val)
|
||||
{
|
||||
/*
|
||||
* If we ever reach this point while running VHE, we're
|
||||
* guaranteed to be on one of these funky, VHE-stuck CPUs. If
|
||||
* the user was trying to force nVHE on us, proceed with
|
||||
* attitude adjustment.
|
||||
*/
|
||||
return !(is_kernel_in_hyp_mode() && val == 0);
|
||||
}
|
||||
|
||||
static const struct ftr_set_desc mmfr1 __initconst = {
|
||||
.name = "id_aa64mmfr1",
|
||||
.override = &id_aa64mmfr1_override,
|
||||
.fields = {
|
||||
{ "vh", ID_AA64MMFR1_VHE_SHIFT },
|
||||
{ "vh", ID_AA64MMFR1_VHE_SHIFT, mmfr1_vh_filter },
|
||||
{}
|
||||
},
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче