Skip to content

Commit b89d82e

Browse files
committed
arm64: kpti: Avoid rewriting early page tables when KASLR is enabled
A side effect of commit c55191e ("arm64: mm: apply r/o permissions of VM areas to its linear alias as well") is that the linear map is created with page granularity, which means that transitioning the early page table from global to non-global mappings when enabling kpti can take a significant amount of time during boot. Given that most CPU implementations do not require kpti, this mainly impacts KASLR builds where kpti is forcefully enabled. However, in these situations we know early on that non-global mappings are required and can avoid the use of global mappings from the beginning. The only gotcha is Cavium erratum #27456, which we must detect based on the MIDR value of the boot CPU. Reviewed-by: Ard Biesheuvel <[email protected]> Reported-by: John Garry <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent d9ed419 commit b89d82e

File tree

5 files changed

+52
-5
lines changed

5 files changed

+52
-5
lines changed

arch/arm64/include/asm/mmu.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#ifndef __ASM_MMU_H
1717
#define __ASM_MMU_H
1818

19+
#include <asm/cputype.h>
20+
1921
#define MMCF_AARCH32 0x1 /* mm context flag for AArch32 executables */
2022
#define USER_ASID_BIT 48
2123
#define USER_ASID_FLAG (UL(1) << USER_ASID_BIT)
@@ -44,6 +46,45 @@ static inline bool arm64_kernel_unmapped_at_el0(void)
4446
cpus_have_const_cap(ARM64_UNMAP_KERNEL_AT_EL0);
4547
}
4648

49+
static inline bool arm64_kernel_use_ng_mappings(void)
50+
{
51+
bool tx1_bug;
52+
53+
/* What's a kpti? Use global mappings if we don't know. */
54+
if (!IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0))
55+
return false;
56+
57+
/*
58+
* Note: this function is called before the CPU capabilities have
59+
* been configured, so our early mappings will be global. If we
60+
* later determine that kpti is required, then
61+
* kpti_install_ng_mappings() will make them non-global.
62+
*/
63+
if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE))
64+
return arm64_kernel_unmapped_at_el0();
65+
66+
/*
67+
* KASLR is enabled so we're going to be enabling kpti on non-broken
68+
* CPUs regardless of their susceptibility to Meltdown. Rather
69+
* than force everybody to go through the G -> nG dance later on,
70+
* just put down non-global mappings from the beginning.
71+
*/
72+
if (!IS_ENABLED(CONFIG_CAVIUM_ERRATUM_27456)) {
73+
tx1_bug = false;
74+
#ifndef MODULE
75+
} else if (!static_branch_likely(&arm64_const_caps_ready)) {
76+
extern const struct midr_range cavium_erratum_27456_cpus[];
77+
78+
tx1_bug = is_midr_in_range_list(read_cpuid_id(),
79+
cavium_erratum_27456_cpus);
80+
#endif
81+
} else {
82+
tx1_bug = __cpus_have_const_cap(ARM64_WORKAROUND_CAVIUM_27456);
83+
}
84+
85+
return !tx1_bug && kaslr_offset() > 0;
86+
}
87+
4788
typedef void (*bp_hardening_cb_t)(void);
4889

4990
struct bp_hardening_data {

arch/arm64/include/asm/pgtable-prot.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
3838
#define _PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
3939

40-
#define PTE_MAYBE_NG (arm64_kernel_unmapped_at_el0() ? PTE_NG : 0)
41-
#define PMD_MAYBE_NG (arm64_kernel_unmapped_at_el0() ? PMD_SECT_NG : 0)
40+
#define PTE_MAYBE_NG (arm64_kernel_use_ng_mappings() ? PTE_NG : 0)
41+
#define PMD_MAYBE_NG (arm64_kernel_use_ng_mappings() ? PMD_SECT_NG : 0)
4242

4343
#define PROT_DEFAULT (_PROT_DEFAULT | PTE_MAYBE_NG)
4444
#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_MAYBE_NG)

arch/arm64/kernel/cpu_errata.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ static const struct midr_range arm64_repeat_tlbi_cpus[] = {
553553
#endif
554554

555555
#ifdef CONFIG_CAVIUM_ERRATUM_27456
556-
static const struct midr_range cavium_erratum_27456_cpus[] = {
556+
const struct midr_range cavium_erratum_27456_cpus[] = {
557557
/* Cavium ThunderX, T88 pass 1.x - 2.1 */
558558
MIDR_RANGE(MIDR_THUNDERX, 0, 0, 1, 1),
559559
/* Cavium ThunderX, T81 pass 1.0 */

arch/arm64/kernel/cpufeature.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
983983

984984
/* Useful for KASLR robustness */
985985
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE))
986-
return true;
986+
return kaslr_offset() > 0;
987987

988988
/* Don't force KPTI for CPUs that are not vulnerable */
989989
if (is_midr_in_range_list(read_cpuid_id(), kpti_safe_list))
@@ -1003,7 +1003,12 @@ kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
10031003
static bool kpti_applied = false;
10041004
int cpu = smp_processor_id();
10051005

1006-
if (kpti_applied)
1006+
/*
1007+
* We don't need to rewrite the page-tables if either we've done
1008+
* it already or we have KASLR enabled and therefore have not
1009+
* created any global mappings at all.
1010+
*/
1011+
if (kpti_applied || kaslr_offset() > 0)
10071012
return;
10081013

10091014
remap_fn = (void *)__pa_symbol(idmap_kpti_install_ng_mappings);

arch/arm64/kernel/head.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ ENDPROC(__primary_switched)
475475

476476
ENTRY(kimage_vaddr)
477477
.quad _text - TEXT_OFFSET
478+
EXPORT_SYMBOL(kimage_vaddr)
478479

479480
/*
480481
* If we're fortunate enough to boot at EL2, ensure that the world is

0 commit comments

Comments
 (0)