diff options
-rw-r--r-- | arch/arm64/kernel/cpuinfo.c | 93 |
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 | ||
58 | static 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 | */ | ||
78 | static 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 | |||
57 | static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) | 149 | static 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 | ||
93 | void __init cpuinfo_store_boot_cpu(void) | 186 | void __init cpuinfo_store_boot_cpu(void) |