diff options
Diffstat (limited to 'arch/powerpc/lib/feature-fixups.c')
-rw-r--r-- | arch/powerpc/lib/feature-fixups.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c index 174c1eba9ac8..48e1ed89052d 100644 --- a/arch/powerpc/lib/feature-fixups.c +++ b/arch/powerpc/lib/feature-fixups.c | |||
@@ -13,6 +13,8 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/string.h> | ||
17 | #include <linux/init.h> | ||
16 | #include <asm/cputable.h> | 18 | #include <asm/cputable.h> |
17 | #include <asm/code-patching.h> | 19 | #include <asm/code-patching.h> |
18 | 20 | ||
@@ -107,3 +109,207 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) | |||
107 | } | 109 | } |
108 | } | 110 | } |
109 | } | 111 | } |
112 | |||
113 | #ifdef CONFIG_FTR_FIXUP_SELFTEST | ||
114 | |||
115 | #define check(x) \ | ||
116 | if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__); | ||
117 | |||
118 | /* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */ | ||
119 | static struct fixup_entry fixup; | ||
120 | |||
121 | static long calc_offset(struct fixup_entry *entry, unsigned int *p) | ||
122 | { | ||
123 | return (unsigned long)p - (unsigned long)entry; | ||
124 | } | ||
125 | |||
126 | void test_basic_patching(void) | ||
127 | { | ||
128 | extern unsigned int ftr_fixup_test1; | ||
129 | extern unsigned int end_ftr_fixup_test1; | ||
130 | extern unsigned int ftr_fixup_test1_orig; | ||
131 | extern unsigned int ftr_fixup_test1_expected; | ||
132 | int size = &end_ftr_fixup_test1 - &ftr_fixup_test1; | ||
133 | |||
134 | fixup.value = fixup.mask = 8; | ||
135 | fixup.start_off = calc_offset(&fixup, &ftr_fixup_test1 + 1); | ||
136 | fixup.end_off = calc_offset(&fixup, &ftr_fixup_test1 + 2); | ||
137 | fixup.alt_start_off = fixup.alt_end_off = 0; | ||
138 | |||
139 | /* Sanity check */ | ||
140 | check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0); | ||
141 | |||
142 | /* Check we don't patch if the value matches */ | ||
143 | patch_feature_section(8, &fixup); | ||
144 | check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0); | ||
145 | |||
146 | /* Check we do patch if the value doesn't match */ | ||
147 | patch_feature_section(0, &fixup); | ||
148 | check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0); | ||
149 | |||
150 | /* Check we do patch if the mask doesn't match */ | ||
151 | memcpy(&ftr_fixup_test1, &ftr_fixup_test1_orig, size); | ||
152 | check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0); | ||
153 | patch_feature_section(~8, &fixup); | ||
154 | check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0); | ||
155 | } | ||
156 | |||
157 | static void test_alternative_patching(void) | ||
158 | { | ||
159 | extern unsigned int ftr_fixup_test2; | ||
160 | extern unsigned int end_ftr_fixup_test2; | ||
161 | extern unsigned int ftr_fixup_test2_orig; | ||
162 | extern unsigned int ftr_fixup_test2_alt; | ||
163 | extern unsigned int ftr_fixup_test2_expected; | ||
164 | int size = &end_ftr_fixup_test2 - &ftr_fixup_test2; | ||
165 | |||
166 | fixup.value = fixup.mask = 0xF; | ||
167 | fixup.start_off = calc_offset(&fixup, &ftr_fixup_test2 + 1); | ||
168 | fixup.end_off = calc_offset(&fixup, &ftr_fixup_test2 + 2); | ||
169 | fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test2_alt); | ||
170 | fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test2_alt + 1); | ||
171 | |||
172 | /* Sanity check */ | ||
173 | check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0); | ||
174 | |||
175 | /* Check we don't patch if the value matches */ | ||
176 | patch_feature_section(0xF, &fixup); | ||
177 | check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0); | ||
178 | |||
179 | /* Check we do patch if the value doesn't match */ | ||
180 | patch_feature_section(0, &fixup); | ||
181 | check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0); | ||
182 | |||
183 | /* Check we do patch if the mask doesn't match */ | ||
184 | memcpy(&ftr_fixup_test2, &ftr_fixup_test2_orig, size); | ||
185 | check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0); | ||
186 | patch_feature_section(~0xF, &fixup); | ||
187 | check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0); | ||
188 | } | ||
189 | |||
190 | static void test_alternative_case_too_big(void) | ||
191 | { | ||
192 | extern unsigned int ftr_fixup_test3; | ||
193 | extern unsigned int end_ftr_fixup_test3; | ||
194 | extern unsigned int ftr_fixup_test3_orig; | ||
195 | extern unsigned int ftr_fixup_test3_alt; | ||
196 | int size = &end_ftr_fixup_test3 - &ftr_fixup_test3; | ||
197 | |||
198 | fixup.value = fixup.mask = 0xC; | ||
199 | fixup.start_off = calc_offset(&fixup, &ftr_fixup_test3 + 1); | ||
200 | fixup.end_off = calc_offset(&fixup, &ftr_fixup_test3 + 2); | ||
201 | fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test3_alt); | ||
202 | fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test3_alt + 2); | ||
203 | |||
204 | /* Sanity check */ | ||
205 | check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0); | ||
206 | |||
207 | /* Expect nothing to be patched, and the error returned to us */ | ||
208 | check(patch_feature_section(0xF, &fixup) == 1); | ||
209 | check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0); | ||
210 | check(patch_feature_section(0, &fixup) == 1); | ||
211 | check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0); | ||
212 | check(patch_feature_section(~0xF, &fixup) == 1); | ||
213 | check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0); | ||
214 | } | ||
215 | |||
216 | static void test_alternative_case_too_small(void) | ||
217 | { | ||
218 | extern unsigned int ftr_fixup_test4; | ||
219 | extern unsigned int end_ftr_fixup_test4; | ||
220 | extern unsigned int ftr_fixup_test4_orig; | ||
221 | extern unsigned int ftr_fixup_test4_alt; | ||
222 | extern unsigned int ftr_fixup_test4_expected; | ||
223 | int size = &end_ftr_fixup_test4 - &ftr_fixup_test4; | ||
224 | unsigned long flag; | ||
225 | |||
226 | /* Check a high-bit flag */ | ||
227 | flag = 1UL << ((sizeof(unsigned long) - 1) * 8); | ||
228 | fixup.value = fixup.mask = flag; | ||
229 | fixup.start_off = calc_offset(&fixup, &ftr_fixup_test4 + 1); | ||
230 | fixup.end_off = calc_offset(&fixup, &ftr_fixup_test4 + 5); | ||
231 | fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test4_alt); | ||
232 | fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test4_alt + 2); | ||
233 | |||
234 | /* Sanity check */ | ||
235 | check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0); | ||
236 | |||
237 | /* Check we don't patch if the value matches */ | ||
238 | patch_feature_section(flag, &fixup); | ||
239 | check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0); | ||
240 | |||
241 | /* Check we do patch if the value doesn't match */ | ||
242 | patch_feature_section(0, &fixup); | ||
243 | check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0); | ||
244 | |||
245 | /* Check we do patch if the mask doesn't match */ | ||
246 | memcpy(&ftr_fixup_test4, &ftr_fixup_test4_orig, size); | ||
247 | check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0); | ||
248 | patch_feature_section(~flag, &fixup); | ||
249 | check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0); | ||
250 | } | ||
251 | |||
252 | static void test_alternative_case_with_branch(void) | ||
253 | { | ||
254 | extern unsigned int ftr_fixup_test5; | ||
255 | extern unsigned int end_ftr_fixup_test5; | ||
256 | extern unsigned int ftr_fixup_test5_expected; | ||
257 | int size = &end_ftr_fixup_test5 - &ftr_fixup_test5; | ||
258 | |||
259 | check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0); | ||
260 | } | ||
261 | |||
262 | static void test_alternative_case_with_external_branch(void) | ||
263 | { | ||
264 | extern unsigned int ftr_fixup_test6; | ||
265 | extern unsigned int end_ftr_fixup_test6; | ||
266 | extern unsigned int ftr_fixup_test6_expected; | ||
267 | int size = &end_ftr_fixup_test6 - &ftr_fixup_test6; | ||
268 | |||
269 | check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0); | ||
270 | } | ||
271 | |||
272 | static void test_cpu_macros(void) | ||
273 | { | ||
274 | extern void ftr_fixup_test_FTR_macros; | ||
275 | extern void ftr_fixup_test_FTR_macros_expected; | ||
276 | unsigned long size = &ftr_fixup_test_FTR_macros_expected - | ||
277 | &ftr_fixup_test_FTR_macros; | ||
278 | |||
279 | /* The fixups have already been done for us during boot */ | ||
280 | check(memcmp(&ftr_fixup_test_FTR_macros, | ||
281 | &ftr_fixup_test_FTR_macros_expected, size) == 0); | ||
282 | } | ||
283 | |||
284 | static void test_fw_macros(void) | ||
285 | { | ||
286 | #ifdef CONFIG_PPC64 | ||
287 | extern void ftr_fixup_test_FW_FTR_macros; | ||
288 | extern void ftr_fixup_test_FW_FTR_macros_expected; | ||
289 | unsigned long size = &ftr_fixup_test_FW_FTR_macros_expected - | ||
290 | &ftr_fixup_test_FW_FTR_macros; | ||
291 | |||
292 | /* The fixups have already been done for us during boot */ | ||
293 | check(memcmp(&ftr_fixup_test_FW_FTR_macros, | ||
294 | &ftr_fixup_test_FW_FTR_macros_expected, size) == 0); | ||
295 | #endif | ||
296 | } | ||
297 | |||
298 | static int __init test_feature_fixups(void) | ||
299 | { | ||
300 | printk(KERN_DEBUG "Running feature fixup self-tests ...\n"); | ||
301 | |||
302 | test_basic_patching(); | ||
303 | test_alternative_patching(); | ||
304 | test_alternative_case_too_big(); | ||
305 | test_alternative_case_too_small(); | ||
306 | test_alternative_case_with_branch(); | ||
307 | test_alternative_case_with_external_branch(); | ||
308 | test_cpu_macros(); | ||
309 | test_fw_macros(); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | late_initcall(test_feature_fixups); | ||
314 | |||
315 | #endif /* CONFIG_FTR_FIXUP_SELFTEST */ | ||