aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)