mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 15:41:36 +00:00
arm64: sdei: explicitly simulate PAN/UAO entry
In preparation for removing addr_limit and set_fs() we must decouple the SDEI PAN/UAO manipulation from the uaccess code, and explicitly reinitialize these as required. SDEI enters the kernel with a non-architectural exception, and prior to the most recent revision of the specification (ARM DEN 0054B), PSTATE bits (e.g. PAN, UAO) are not manipulated in the same way as for architectural exceptions. Notably, older versions of the spec can be read ambiguously as to whether PSTATE bits are inherited unchanged from the interrupted context or whether they are generated from scratch, with TF-A doing the latter. We have three cases to consider: 1) The existing TF-A implementation of SDEI will clear PAN and clear UAO (along with other bits in PSTATE) when delivering an SDEI exception. 2) In theory, implementations of SDEI prior to revision B could inherit PAN and UAO (along with other bits in PSTATE) unchanged from the interrupted context. However, in practice such implementations do not exist. 3) Going forward, new implementations of SDEI must clear UAO, and depending on SCTLR_ELx.SPAN must either inherit or set PAN. As we can ignore (2) we can assume that upon SDEI entry, UAO is always clear, though PAN may be clear, inherited, or set per SCTLR_ELx.SPAN. Therefore, we must explicitly initialize PAN, but do not need to do anything for UAO. Considering what we need to do: * When set_fs() is removed, force_uaccess_begin() will have no HW side-effects. As this only clears UAO, which we can assume has already been cleared upon entry, this is not a problem. We do not need to add code to manipulate UAO explicitly. * PAN may be cleared upon entry (in case 1 above), so where a kernel is built to use PAN and this is supported by all CPUs, the kernel must set PAN upon entry to ensure expected behaviour. * PAN may be inherited from the interrupted context (in case 3 above), and so where a kernel is not built to use PAN or where PAN support is not uniform across CPUs, the kernel must clear PAN to ensure expected behaviour. This patch reworks the SDEI code accordingly, explicitly setting PAN to the expected state in all cases. To cater for the cases where the kernel does not use PAN or this is not uniformly supported by hardware we add a new cpu_has_pan() helper which can be used regardless of whether the kernel is built to use PAN. The existing system_uses_ttbr0_pan() is redefined in terms of system_uses_hw_pan() both for clarity and as a minor optimization when HW PAN is not selected. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Reviewed-by: James Morse <james.morse@arm.com> Cc: James Morse <james.morse@arm.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20201202131558.39270-3-mark.rutland@arm.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
parent
a0ccf2ba68
commit
2376e75cc7
@ -667,10 +667,16 @@ static __always_inline bool system_supports_fpsimd(void)
|
||||
return !cpus_have_const_cap(ARM64_HAS_NO_FPSIMD);
|
||||
}
|
||||
|
||||
static inline bool system_uses_hw_pan(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_ARM64_PAN) &&
|
||||
cpus_have_const_cap(ARM64_HAS_PAN);
|
||||
}
|
||||
|
||||
static inline bool system_uses_ttbr0_pan(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_ARM64_SW_TTBR0_PAN) &&
|
||||
!cpus_have_const_cap(ARM64_HAS_PAN);
|
||||
!system_uses_hw_pan();
|
||||
}
|
||||
|
||||
static __always_inline bool system_supports_sve(void)
|
||||
@ -762,6 +768,13 @@ static inline bool cpu_has_hw_af(void)
|
||||
ID_AA64MMFR1_HADBS_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool cpu_has_pan(void)
|
||||
{
|
||||
u64 mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
|
||||
return cpuid_feature_extract_unsigned_field(mmfr1,
|
||||
ID_AA64MMFR1_PAN_SHIFT);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64_AMU_EXTN
|
||||
/* Check whether the cpu supports the Activity Monitors Unit (AMU) */
|
||||
extern bool cpu_has_amu_feat(int cpu);
|
||||
|
@ -216,6 +216,27 @@ static __kprobes unsigned long _sdei_handler(struct pt_regs *regs,
|
||||
return vbar + 0x480;
|
||||
}
|
||||
|
||||
static void __kprobes notrace __sdei_pstate_entry(void)
|
||||
{
|
||||
/*
|
||||
* The original SDEI spec (ARM DEN 0054A) can be read ambiguously as to
|
||||
* whether PSTATE bits are inherited unchanged or generated from
|
||||
* scratch, and the TF-A implementation always clears PAN and always
|
||||
* clears UAO. There are no other known implementations.
|
||||
*
|
||||
* Subsequent revisions (ARM DEN 0054B) follow the usual rules for how
|
||||
* PSTATE is modified upon architectural exceptions, and so PAN is
|
||||
* either inherited or set per SCTLR_ELx.SPAN, and UAO is always
|
||||
* cleared.
|
||||
*
|
||||
* We must explicitly reset PAN to the expected state, including
|
||||
* clearing it when the host isn't using it, in case a VM had it set.
|
||||
*/
|
||||
if (system_uses_hw_pan())
|
||||
set_pstate_pan(1);
|
||||
else if (cpu_has_pan())
|
||||
set_pstate_pan(0);
|
||||
}
|
||||
|
||||
asmlinkage __kprobes notrace unsigned long
|
||||
__sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
|
||||
@ -224,12 +245,11 @@ __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
|
||||
mm_segment_t orig_addr_limit;
|
||||
|
||||
/*
|
||||
* We didn't take an exception to get here, so the HW hasn't set PAN or
|
||||
* cleared UAO, and the exception entry code hasn't reset addr_limit.
|
||||
* Set PAN, then use force_uaccess_begin() to clear UAO and reset
|
||||
* addr_limit.
|
||||
* We didn't take an exception to get here, so the HW hasn't
|
||||
* set/cleared bits in PSTATE that we may rely on. Initialize PAN, then
|
||||
* use force_uaccess_begin() to reset addr_limit.
|
||||
*/
|
||||
__uaccess_enable_hw_pan();
|
||||
__sdei_pstate_entry();
|
||||
orig_addr_limit = force_uaccess_begin();
|
||||
|
||||
nmi_enter();
|
||||
|
Loading…
Reference in New Issue
Block a user