Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: head.S: always initialize PSTATE

As with SCTLR_ELx and other control registers, some PSTATE bits are
UNKNOWN out-of-reset, and we may not be able to rely on hardware or
firmware to initialize them to our liking prior to entry to the kernel,
e.g. in the primary/secondary boot paths and return from idle/suspend.

It would be more robust (and easier to reason about) if we consistently
initialized PSTATE to a default value, as we do with control registers.
This will ensure that the kernel is not adversely affected by bits it is
not aware of, e.g. when support for a feature such as PAN/UAO is
disabled.

This patch ensures that PSTATE is consistently initialized at boot time
via an ERET. This is not intended to relax the existing requirements
(e.g. DAIF bits must still be set prior to entering the kernel). For
features detected dynamically (which may require system-wide support),
it is still necessary to subsequently modify PSTATE.

As ERET is not always a Context Synchronization Event, an ISB is placed
before each exception return to ensure updates to control registers have
taken effect. This handles the kernel being entered with SCTLR_ELx.EOS
clear (or any future control bits being in an UNKNOWN state).

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: James Morse <james.morse@arm.com>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20201113124937.20574-6-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Rutland and committed by
Catalin Marinas
d87a8e65 2ffac9e3

+26 -11
+5
arch/arm64/include/asm/ptrace.h
··· 16 16 #define CurrentEL_EL1 (1 << 2) 17 17 #define CurrentEL_EL2 (2 << 2) 18 18 19 + #define INIT_PSTATE_EL1 \ 20 + (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h) 21 + #define INIT_PSTATE_EL2 \ 22 + (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL2h) 23 + 19 24 /* 20 25 * PMR values used to mask/unmask interrupts. 21 26 *
+21 -11
arch/arm64/kernel/head.S
··· 486 486 * reachable EL supported by the kernel in a chosen default state. If dropping 487 487 * from EL2 to EL1, configure EL2 before configuring EL1. 488 488 * 489 + * Since we cannot always rely on ERET synchronizing writes to sysregs (e.g. if 490 + * SCTLR_ELx.EOS is clear), we place an ISB prior to ERET. 491 + * 489 492 * Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in w0 if 490 493 * booted in EL1 or EL2 respectively. 491 494 */ 492 495 SYM_FUNC_START(init_kernel_el) 493 - msr SPsel, #1 // We want to use SP_EL{1,2} 494 496 mrs x0, CurrentEL 495 497 cmp x0, #CurrentEL_EL2 496 - b.eq 1f 498 + b.eq init_el2 499 + 500 + SYM_INNER_LABEL(init_el1, SYM_L_LOCAL) 497 501 mov_q x0, INIT_SCTLR_EL1_MMU_OFF 498 502 msr sctlr_el1, x0 499 - mov w0, #BOOT_CPU_MODE_EL1 // This cpu booted in EL1 500 503 isb 501 - ret 504 + mov_q x0, INIT_PSTATE_EL1 505 + msr spsr_el1, x0 506 + msr elr_el1, lr 507 + mov w0, #BOOT_CPU_MODE_EL1 508 + eret 502 509 503 - 1: mov_q x0, INIT_SCTLR_EL2_MMU_OFF 510 + SYM_INNER_LABEL(init_el2, SYM_L_LOCAL) 511 + mov_q x0, INIT_SCTLR_EL2_MMU_OFF 504 512 msr sctlr_el2, x0 505 513 506 514 #ifdef CONFIG_ARM64_VHE ··· 617 609 618 610 cbz x2, install_el2_stub 619 611 620 - mov w0, #BOOT_CPU_MODE_EL2 // This CPU booted in EL2 621 612 isb 622 - ret 613 + mov_q x0, INIT_PSTATE_EL2 614 + msr spsr_el2, x0 615 + msr elr_el2, lr 616 + mov w0, #BOOT_CPU_MODE_EL2 617 + eret 623 618 624 619 SYM_INNER_LABEL(install_el2_stub, SYM_L_LOCAL) 625 620 /* ··· 654 643 7: adr_l x0, __hyp_stub_vectors 655 644 msr vbar_el2, x0 656 645 657 - /* spsr */ 658 - mov x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\ 659 - PSR_MODE_EL1h) 646 + isb 647 + mov x0, #INIT_PSTATE_EL1 660 648 msr spsr_el2, x0 661 649 msr elr_el2, lr 662 - mov w0, #BOOT_CPU_MODE_EL2 // This CPU booted in EL2 650 + mov w0, #BOOT_CPU_MODE_EL2 663 651 eret 664 652 SYM_FUNC_END(init_kernel_el) 665 653