aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel/cpuinfo.c
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2014-07-16 11:32:46 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2014-07-18 10:24:11 -0400
commit127161aaf0fcd37680984f79e1451b870b3e310a (patch)
tree66f405041d7673f64cf9c7c1139ff8d2e8f6d043 /arch/arm64/kernel/cpuinfo.c
parent59ccc0d41b7a60c82c636cd056021f522c30557e (diff)
arm64: add runtime system sanity checks
Unexpected variation in certain system register values across CPUs is an indicator of potential problems with a system. The kernel expects CPUs to be mostly identical in terms of supported features, even in systems with heterogeneous CPUs, with uniform instruction set support being critical for the correct operation of userspace. To help detect issues early where hardware violates the expectations of the kernel, this patch adds simple runtime sanity checks on important ID registers in the bring up path of each CPU. Where CPUs are fundamentally mismatched, set TAINT_CPU_OUT_OF_SPEC. Given that the kernel assumes CPUs are identical feature wise, let's not pretend that we expect such configurations to work. Supporting such configurations would require massive rework, and hopefully they will never exist. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Reviewed-by: Will Deacon <will.deacon@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm64/kernel/cpuinfo.c')
-rw-r--r--arch/arm64/kernel/cpuinfo.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 3ce99fc1fde1..f82f7d1c468e 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -21,6 +21,7 @@
21 21
22#include <linux/bitops.h> 22#include <linux/bitops.h>
23#include <linux/init.h> 23#include <linux/init.h>
24#include <linux/kernel.h>
24#include <linux/printk.h> 25#include <linux/printk.h>
25#include <linux/smp.h> 26#include <linux/smp.h>
26 27
@@ -54,6 +55,97 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
54 pr_info("Detected %s I-cache on CPU%d", icache_policy_str[l1ip], cpu); 55 pr_info("Detected %s I-cache on CPU%d", icache_policy_str[l1ip], cpu);
55} 56}
56 57
58static int check_reg_mask(char *name, u64 mask, u64 boot, u64 cur, int cpu)
59{
60 if ((boot & mask) == (cur & mask))
61 return 0;
62
63 pr_warn("SANITY CHECK: Unexpected variation in %s. Boot CPU: %#016lx, CPU%d: %#016lx\n",
64 name, (unsigned long)boot, cpu, (unsigned long)cur);
65
66 return 1;
67}
68
69#define CHECK_MASK(field, mask, boot, cur, cpu) \
70 check_reg_mask(#field, mask, (boot)->reg_ ## field, (cur)->reg_ ## field, cpu)
71
72#define CHECK(field, boot, cur, cpu) \
73 CHECK_MASK(field, ~0ULL, boot, cur, cpu)
74
75/*
76 * Verify that CPUs don't have unexpected differences that will cause problems.
77 */
78static void cpuinfo_sanity_check(struct cpuinfo_arm64 *cur)
79{
80 unsigned int cpu = smp_processor_id();
81 struct cpuinfo_arm64 *boot = &boot_cpu_data;
82 unsigned int diff = 0;
83
84 /*
85 * The kernel can handle differing I-cache policies, but otherwise
86 * caches should look identical. Userspace JITs will make use of
87 * *minLine.
88 */
89 diff |= CHECK_MASK(ctr, 0xffff3fff, boot, cur, cpu);
90
91 /*
92 * Userspace may perform DC ZVA instructions. Mismatched block sizes
93 * could result in too much or too little memory being zeroed if a
94 * process is preempted and migrated between CPUs.
95 */
96 diff |= CHECK(dczid, boot, cur, cpu);
97
98 /* If different, timekeeping will be broken (especially with KVM) */
99 diff |= CHECK(cntfrq, boot, cur, cpu);
100
101 /*
102 * Even in big.LITTLE, processors should be identical instruction-set
103 * wise.
104 */
105 diff |= CHECK(id_aa64isar0, boot, cur, cpu);
106 diff |= CHECK(id_aa64isar1, boot, cur, cpu);
107
108 /*
109 * Differing PARange support is fine as long as all peripherals and
110 * memory are mapped within the minimum PARange of all CPUs.
111 * Linux should not care about secure memory.
112 * ID_AA64MMFR1 is currently RES0.
113 */
114 diff |= CHECK_MASK(id_aa64mmfr0, 0xffffffffffff0ff0, boot, cur, cpu);
115 diff |= CHECK(id_aa64mmfr1, boot, cur, cpu);
116
117 /*
118 * EL3 is not our concern.
119 * ID_AA64PFR1 is currently RES0.
120 */
121 diff |= CHECK_MASK(id_aa64pfr0, 0xffffffffffff0fff, boot, cur, cpu);
122 diff |= CHECK(id_aa64pfr1, boot, cur, cpu);
123
124 /*
125 * If we have AArch32, we care about 32-bit features for compat. These
126 * registers should be RES0 otherwise.
127 */
128 diff |= CHECK(id_isar0, boot, cur, cpu);
129 diff |= CHECK(id_isar1, boot, cur, cpu);
130 diff |= CHECK(id_isar2, boot, cur, cpu);
131 diff |= CHECK(id_isar3, boot, cur, cpu);
132 diff |= CHECK(id_isar4, boot, cur, cpu);
133 diff |= CHECK(id_isar5, boot, cur, cpu);
134 diff |= CHECK(id_mmfr0, boot, cur, cpu);
135 diff |= CHECK(id_mmfr1, boot, cur, cpu);
136 diff |= CHECK(id_mmfr2, boot, cur, cpu);
137 diff |= CHECK(id_mmfr3, boot, cur, cpu);
138 diff |= CHECK(id_pfr0, boot, cur, cpu);
139 diff |= CHECK(id_pfr1, boot, cur, cpu);
140
141 /*
142 * Mismatched CPU features are a recipe for disaster. Don't even
143 * pretend to support them.
144 */
145 WARN_TAINT_ONCE(diff, TAINT_CPU_OUT_OF_SPEC,
146 "Unsupported CPU feature variation.");
147}
148
57static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) 149static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
58{ 150{
59 info->reg_cntfrq = arch_timer_get_cntfrq(); 151 info->reg_cntfrq = arch_timer_get_cntfrq();
@@ -88,6 +180,7 @@ void cpuinfo_store_cpu(void)
88{ 180{
89 struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data); 181 struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
90 __cpuinfo_store_cpu(info); 182 __cpuinfo_store_cpu(info);
183 cpuinfo_sanity_check(info);
91} 184}
92 185
93void __init cpuinfo_store_boot_cpu(void) 186void __init cpuinfo_store_boot_cpu(void)