summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Murzin <vladimir.murzin@arm.com>2018-07-31 09:08:56 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2018-09-18 07:02:27 -0400
commit5ffdfaedfa0aba3f5db0fbb8ed4f3192be2b39b8 (patch)
tree4bb4a2ef29171186d317ed6396ce89572ce03d11
parent74e248286e1d04b0d9bfdd002450ef0211f6f29f (diff)
arm64: mm: Support Common Not Private translations
Common Not Private (CNP) is a feature of ARMv8.2 extension which allows translation table entries to be shared between different PEs in the same inner shareable domain, so the hardware can use this fact to optimise the caching of such entries in the TLB. CNP occupies one bit in TTBRx_ELy and VTTBR_EL2, which advertises to the hardware that the translation table entries pointed to by this TTBR are the same as every PE in the same inner shareable domain for which the equivalent TTBR also has CNP bit set. In case CNP bit is set but TTBR does not point at the same translation table entries for a given ASID and VMID, then the system is mis-configured, so the results of translations are UNPREDICTABLE. For kernel we postpone setting CNP till all cpus are up and rely on cpufeature framework to 1) patch the code which is sensitive to CNP and 2) update TTBR1_EL1 with CNP bit set. TTBR1_EL1 can be reprogrammed as result of hibernation or cpuidle (via __enable_mmu). For these two cases we restore CnP bit via __cpu_suspend_exit(). There are a few cases we need to care of changes in TTBR0_EL1: - a switch to idmap - software emulated PAN we rule out latter via Kconfig options and for the former we make sure that CNP is set for non-zero ASIDs only. Reviewed-by: James Morse <james.morse@arm.com> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com> [catalin.marinas@arm.com: default y for CONFIG_ARM64_CNP] Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--arch/arm64/Kconfig14
-rw-r--r--arch/arm64/include/asm/cpucaps.h3
-rw-r--r--arch/arm64/include/asm/cpufeature.h6
-rw-r--r--arch/arm64/include/asm/mmu_context.h17
-rw-r--r--arch/arm64/include/asm/pgtable-hwdef.h2
-rw-r--r--arch/arm64/kernel/cpufeature.c34
-rw-r--r--arch/arm64/kernel/suspend.c4
-rw-r--r--arch/arm64/mm/context.c3
-rw-r--r--arch/arm64/mm/proc.S11
9 files changed, 88 insertions, 6 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 58eb02796b16..fabac617d605 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1134,6 +1134,20 @@ config ARM64_RAS_EXTN
1134 and access the new registers if the system supports the extension. 1134 and access the new registers if the system supports the extension.
1135 Platform RAS features may additionally depend on firmware support. 1135 Platform RAS features may additionally depend on firmware support.
1136 1136
1137config ARM64_CNP
1138 bool "Enable support for Common Not Private (CNP) translations"
1139 default y
1140 depends on ARM64_PAN || !ARM64_SW_TTBR0_PAN
1141 help
1142 Common Not Private (CNP) allows translation table entries to
1143 be shared between different PEs in the same inner shareable
1144 domain, so the hardware can use this fact to optimise the
1145 caching of such entries in the TLB.
1146
1147 Selecting this option allows the CNP feature to be detected
1148 at runtime, and does not affect PEs that do not implement
1149 this feature.
1150
1137endmenu 1151endmenu
1138 1152
1139config ARM64_SVE 1153config ARM64_SVE
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 38eec9cf30f2..c51d7e868f3f 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -53,7 +53,8 @@
53#define ARM64_HAS_STAGE2_FWB 32 53#define ARM64_HAS_STAGE2_FWB 32
54#define ARM64_HAS_CRC32 33 54#define ARM64_HAS_CRC32 33
55#define ARM64_SSBS 34 55#define ARM64_SSBS 34
56#define ARM64_HAS_CNP 35
56 57
57#define ARM64_NCAPS 35 58#define ARM64_NCAPS 36
58 59
59#endif /* __ASM_CPUCAPS_H */ 60#endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 9079715794af..bb9fbf6f910a 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -508,6 +508,12 @@ static inline bool system_supports_sve(void)
508 cpus_have_const_cap(ARM64_SVE); 508 cpus_have_const_cap(ARM64_SVE);
509} 509}
510 510
511static inline bool system_supports_cnp(void)
512{
513 return IS_ENABLED(CONFIG_ARM64_CNP) &&
514 cpus_have_const_cap(ARM64_HAS_CNP);
515}
516
511#define ARM64_SSBD_UNKNOWN -1 517#define ARM64_SSBD_UNKNOWN -1
512#define ARM64_SSBD_FORCE_DISABLE 0 518#define ARM64_SSBD_FORCE_DISABLE 0
513#define ARM64_SSBD_KERNEL 1 519#define ARM64_SSBD_KERNEL 1
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 39ec0b8a689e..1e58bf58c22b 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -147,12 +147,25 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp)
147 extern ttbr_replace_func idmap_cpu_replace_ttbr1; 147 extern ttbr_replace_func idmap_cpu_replace_ttbr1;
148 ttbr_replace_func *replace_phys; 148 ttbr_replace_func *replace_phys;
149 149
150 phys_addr_t pgd_phys = virt_to_phys(pgdp); 150 /* phys_to_ttbr() zeros lower 2 bits of ttbr with 52-bit PA */
151 phys_addr_t ttbr1 = phys_to_ttbr(virt_to_phys(pgdp));
152
153 if (system_supports_cnp() && !WARN_ON(pgdp != lm_alias(swapper_pg_dir))) {
154 /*
155 * cpu_replace_ttbr1() is used when there's a boot CPU
156 * up (i.e. cpufeature framework is not up yet) and
157 * latter only when we enable CNP via cpufeature's
158 * enable() callback.
159 * Also we rely on the cpu_hwcap bit being set before
160 * calling the enable() function.
161 */
162 ttbr1 |= TTBR_CNP_BIT;
163 }
151 164
152 replace_phys = (void *)__pa_symbol(idmap_cpu_replace_ttbr1); 165 replace_phys = (void *)__pa_symbol(idmap_cpu_replace_ttbr1);
153 166
154 cpu_install_idmap(); 167 cpu_install_idmap();
155 replace_phys(pgd_phys); 168 replace_phys(ttbr1);
156 cpu_uninstall_idmap(); 169 cpu_uninstall_idmap();
157} 170}
158 171
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index fd208eac9f2a..1d7d8da2ef9b 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -211,6 +211,8 @@
211#define PHYS_MASK_SHIFT (CONFIG_ARM64_PA_BITS) 211#define PHYS_MASK_SHIFT (CONFIG_ARM64_PA_BITS)
212#define PHYS_MASK ((UL(1) << PHYS_MASK_SHIFT) - 1) 212#define PHYS_MASK ((UL(1) << PHYS_MASK_SHIFT) - 1)
213 213
214#define TTBR_CNP_BIT (UL(1) << 0)
215
214/* 216/*
215 * TCR flags. 217 * TCR flags.
216 */ 218 */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index f15e2fb97011..237f8822a391 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -20,6 +20,7 @@
20 20
21#include <linux/bsearch.h> 21#include <linux/bsearch.h>
22#include <linux/cpumask.h> 22#include <linux/cpumask.h>
23#include <linux/crash_dump.h>
23#include <linux/sort.h> 24#include <linux/sort.h>
24#include <linux/stop_machine.h> 25#include <linux/stop_machine.h>
25#include <linux/types.h> 26#include <linux/types.h>
@@ -117,6 +118,7 @@ EXPORT_SYMBOL(cpu_hwcap_keys);
117static bool __maybe_unused 118static bool __maybe_unused
118cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused); 119cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused);
119 120
121static void cpu_enable_cnp(struct arm64_cpu_capabilities const *cap);
120 122
121/* 123/*
122 * NOTE: Any changes to the visibility of features should be kept in 124 * NOTE: Any changes to the visibility of features should be kept in
@@ -863,6 +865,20 @@ static bool has_cache_dic(const struct arm64_cpu_capabilities *entry,
863 return read_sanitised_ftr_reg(SYS_CTR_EL0) & BIT(CTR_DIC_SHIFT); 865 return read_sanitised_ftr_reg(SYS_CTR_EL0) & BIT(CTR_DIC_SHIFT);
864} 866}
865 867
868static bool __maybe_unused
869has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
870{
871 /*
872 * Kdump isn't guaranteed to power-off all secondary CPUs, CNP
873 * may share TLB entries with a CPU stuck in the crashed
874 * kernel.
875 */
876 if (is_kdump_kernel())
877 return false;
878
879 return has_cpuid_feature(entry, scope);
880}
881
866#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 882#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
867static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */ 883static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */
868 884
@@ -1312,6 +1328,19 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
1312 .cpu_enable = cpu_enable_ssbs, 1328 .cpu_enable = cpu_enable_ssbs,
1313 }, 1329 },
1314#endif 1330#endif
1331#ifdef CONFIG_ARM64_CNP
1332 {
1333 .desc = "Common not Private translations",
1334 .capability = ARM64_HAS_CNP,
1335 .type = ARM64_CPUCAP_SYSTEM_FEATURE,
1336 .matches = has_useable_cnp,
1337 .sys_reg = SYS_ID_AA64MMFR2_EL1,
1338 .sign = FTR_UNSIGNED,
1339 .field_pos = ID_AA64MMFR2_CNP_SHIFT,
1340 .min_field_value = 1,
1341 .cpu_enable = cpu_enable_cnp,
1342 },
1343#endif
1315 {}, 1344 {},
1316}; 1345};
1317 1346
@@ -1749,6 +1778,11 @@ cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
1749 return (cpus_have_const_cap(ARM64_HAS_PAN) && !cpus_have_const_cap(ARM64_HAS_UAO)); 1778 return (cpus_have_const_cap(ARM64_HAS_PAN) && !cpus_have_const_cap(ARM64_HAS_UAO));
1750} 1779}
1751 1780
1781static void __maybe_unused cpu_enable_cnp(struct arm64_cpu_capabilities const *cap)
1782{
1783 cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
1784}
1785
1752/* 1786/*
1753 * We emulate only the following system register space. 1787 * We emulate only the following system register space.
1754 * Op0 = 0x3, CRn = 0x0, Op1 = 0x0, CRm = [0, 4 - 7] 1788 * Op0 = 0x3, CRn = 0x0, Op1 = 0x0, CRm = [0, 4 - 7]
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index 70c283368b64..9405d1b7f4b0 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -48,6 +48,10 @@ void notrace __cpu_suspend_exit(void)
48 */ 48 */
49 cpu_uninstall_idmap(); 49 cpu_uninstall_idmap();
50 50
51 /* Restore CnP bit in TTBR1_EL1 */
52 if (system_supports_cnp())
53 cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
54
51 /* 55 /*
52 * PSTATE was not saved over suspend/resume, re-enable any detected 56 * PSTATE was not saved over suspend/resume, re-enable any detected
53 * features that might not have been set correctly. 57 * features that might not have been set correctly.
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index c127f94da8e2..a65af49e12e7 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -196,6 +196,9 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
196 unsigned long flags; 196 unsigned long flags;
197 u64 asid, old_active_asid; 197 u64 asid, old_active_asid;
198 198
199 if (system_supports_cnp())
200 cpu_set_reserved_ttbr0();
201
199 asid = atomic64_read(&mm->context.id); 202 asid = atomic64_read(&mm->context.id);
200 203
201 /* 204 /*
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 03646e6a2ef4..2c75b0b903ae 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -160,6 +160,12 @@ ENTRY(cpu_do_switch_mm)
160 mrs x2, ttbr1_el1 160 mrs x2, ttbr1_el1
161 mmid x1, x1 // get mm->context.id 161 mmid x1, x1 // get mm->context.id
162 phys_to_ttbr x3, x0 162 phys_to_ttbr x3, x0
163
164alternative_if ARM64_HAS_CNP
165 cbz x1, 1f // skip CNP for reserved ASID
166 orr x3, x3, #TTBR_CNP_BIT
1671:
168alternative_else_nop_endif
163#ifdef CONFIG_ARM64_SW_TTBR0_PAN 169#ifdef CONFIG_ARM64_SW_TTBR0_PAN
164 bfi x3, x1, #48, #16 // set the ASID field in TTBR0 170 bfi x3, x1, #48, #16 // set the ASID field in TTBR0
165#endif 171#endif
@@ -184,7 +190,7 @@ ENDPROC(cpu_do_switch_mm)
184.endm 190.endm
185 191
186/* 192/*
187 * void idmap_cpu_replace_ttbr1(phys_addr_t new_pgd) 193 * void idmap_cpu_replace_ttbr1(phys_addr_t ttbr1)
188 * 194 *
189 * This is the low-level counterpart to cpu_replace_ttbr1, and should not be 195 * This is the low-level counterpart to cpu_replace_ttbr1, and should not be
190 * called by anything else. It can only be executed from a TTBR0 mapping. 196 * called by anything else. It can only be executed from a TTBR0 mapping.
@@ -194,8 +200,7 @@ ENTRY(idmap_cpu_replace_ttbr1)
194 200
195 __idmap_cpu_set_reserved_ttbr1 x1, x3 201 __idmap_cpu_set_reserved_ttbr1 x1, x3
196 202
197 phys_to_ttbr x3, x0 203 msr ttbr1_el1, x0
198 msr ttbr1_el1, x3
199 isb 204 isb
200 205
201 restore_daif x2 206 restore_daif x2