2 * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
4 * Modifications for ppc64:
5 * Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com>
7 * Copyright 2008 Michael Ellerman, IBM Corporation.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/init.h>
19 #include <asm/cputable.h>
20 #include <asm/code-patching.h>
22 #include <asm/sections.h>
34 static unsigned int *calc_addr(struct fixup_entry
*fcur
, long offset
)
37 * We store the offset to the code as a negative offset from
38 * the start of the alt_entry, to support the VDSO. This
39 * routine converts that back into an actual address.
41 return (unsigned int *)((unsigned long)fcur
+ offset
);
44 static int patch_alt_instruction(unsigned int *src
, unsigned int *dest
,
45 unsigned int *alt_start
, unsigned int *alt_end
)
51 if (instr_is_relative_branch(*src
)) {
52 unsigned int *target
= (unsigned int *)branch_target(src
);
54 /* Branch within the section doesn't need translating */
55 if (target
< alt_start
|| target
>= alt_end
) {
56 instr
= translate_branch(dest
, src
);
62 patch_instruction(dest
, instr
);
67 static int patch_feature_section(unsigned long value
, struct fixup_entry
*fcur
)
69 unsigned int *start
, *end
, *alt_start
, *alt_end
, *src
, *dest
;
71 start
= calc_addr(fcur
, fcur
->start_off
);
72 end
= calc_addr(fcur
, fcur
->end_off
);
73 alt_start
= calc_addr(fcur
, fcur
->alt_start_off
);
74 alt_end
= calc_addr(fcur
, fcur
->alt_end_off
);
76 if ((alt_end
- alt_start
) > (end
- start
))
79 if ((value
& fcur
->mask
) == fcur
->value
)
85 for (; src
< alt_end
; src
++, dest
++) {
86 if (patch_alt_instruction(src
, dest
, alt_start
, alt_end
))
90 for (; dest
< end
; dest
++)
91 patch_instruction(dest
, PPC_INST_NOP
);
96 void do_feature_fixups(unsigned long value
, void *fixup_start
, void *fixup_end
)
98 struct fixup_entry
*fcur
, *fend
;
103 for (; fcur
< fend
; fcur
++) {
104 if (patch_feature_section(value
, fcur
)) {
106 printk("Unable to patch feature section at %p - %p" \
108 calc_addr(fcur
, fcur
->start_off
),
109 calc_addr(fcur
, fcur
->end_off
),
110 calc_addr(fcur
, fcur
->alt_start_off
),
111 calc_addr(fcur
, fcur
->alt_end_off
));
116 void do_lwsync_fixups(unsigned long value
, void *fixup_start
, void *fixup_end
)
121 if (!(value
& CPU_FTR_LWSYNC
))
127 for (; start
< end
; start
++) {
128 dest
= (void *)start
+ *start
;
129 patch_instruction(dest
, PPC_INST_LWSYNC
);
133 void do_final_fixups(void)
135 #if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE)
137 unsigned long length
;
139 if (PHYSICAL_START
== 0)
142 src
= (int *)(KERNELBASE
+ PHYSICAL_START
);
143 dest
= (int *)KERNELBASE
;
144 length
= (__end_interrupts
- _stext
) / sizeof(int);
147 patch_instruction(dest
, *src
);
154 #ifdef CONFIG_FTR_FIXUP_SELFTEST
157 if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
159 /* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
160 static struct fixup_entry fixup
;
162 static long calc_offset(struct fixup_entry
*entry
, unsigned int *p
)
164 return (unsigned long)p
- (unsigned long)entry
;
167 static void test_basic_patching(void)
169 extern unsigned int ftr_fixup_test1
;
170 extern unsigned int end_ftr_fixup_test1
;
171 extern unsigned int ftr_fixup_test1_orig
;
172 extern unsigned int ftr_fixup_test1_expected
;
173 int size
= &end_ftr_fixup_test1
- &ftr_fixup_test1
;
175 fixup
.value
= fixup
.mask
= 8;
176 fixup
.start_off
= calc_offset(&fixup
, &ftr_fixup_test1
+ 1);
177 fixup
.end_off
= calc_offset(&fixup
, &ftr_fixup_test1
+ 2);
178 fixup
.alt_start_off
= fixup
.alt_end_off
= 0;
181 check(memcmp(&ftr_fixup_test1
, &ftr_fixup_test1_orig
, size
) == 0);
183 /* Check we don't patch if the value matches */
184 patch_feature_section(8, &fixup
);
185 check(memcmp(&ftr_fixup_test1
, &ftr_fixup_test1_orig
, size
) == 0);
187 /* Check we do patch if the value doesn't match */
188 patch_feature_section(0, &fixup
);
189 check(memcmp(&ftr_fixup_test1
, &ftr_fixup_test1_expected
, size
) == 0);
191 /* Check we do patch if the mask doesn't match */
192 memcpy(&ftr_fixup_test1
, &ftr_fixup_test1_orig
, size
);
193 check(memcmp(&ftr_fixup_test1
, &ftr_fixup_test1_orig
, size
) == 0);
194 patch_feature_section(~8, &fixup
);
195 check(memcmp(&ftr_fixup_test1
, &ftr_fixup_test1_expected
, size
) == 0);
198 static void test_alternative_patching(void)
200 extern unsigned int ftr_fixup_test2
;
201 extern unsigned int end_ftr_fixup_test2
;
202 extern unsigned int ftr_fixup_test2_orig
;
203 extern unsigned int ftr_fixup_test2_alt
;
204 extern unsigned int ftr_fixup_test2_expected
;
205 int size
= &end_ftr_fixup_test2
- &ftr_fixup_test2
;
207 fixup
.value
= fixup
.mask
= 0xF;
208 fixup
.start_off
= calc_offset(&fixup
, &ftr_fixup_test2
+ 1);
209 fixup
.end_off
= calc_offset(&fixup
, &ftr_fixup_test2
+ 2);
210 fixup
.alt_start_off
= calc_offset(&fixup
, &ftr_fixup_test2_alt
);
211 fixup
.alt_end_off
= calc_offset(&fixup
, &ftr_fixup_test2_alt
+ 1);
214 check(memcmp(&ftr_fixup_test2
, &ftr_fixup_test2_orig
, size
) == 0);
216 /* Check we don't patch if the value matches */
217 patch_feature_section(0xF, &fixup
);
218 check(memcmp(&ftr_fixup_test2
, &ftr_fixup_test2_orig
, size
) == 0);
220 /* Check we do patch if the value doesn't match */
221 patch_feature_section(0, &fixup
);
222 check(memcmp(&ftr_fixup_test2
, &ftr_fixup_test2_expected
, size
) == 0);
224 /* Check we do patch if the mask doesn't match */
225 memcpy(&ftr_fixup_test2
, &ftr_fixup_test2_orig
, size
);
226 check(memcmp(&ftr_fixup_test2
, &ftr_fixup_test2_orig
, size
) == 0);
227 patch_feature_section(~0xF, &fixup
);
228 check(memcmp(&ftr_fixup_test2
, &ftr_fixup_test2_expected
, size
) == 0);
231 static void test_alternative_case_too_big(void)
233 extern unsigned int ftr_fixup_test3
;
234 extern unsigned int end_ftr_fixup_test3
;
235 extern unsigned int ftr_fixup_test3_orig
;
236 extern unsigned int ftr_fixup_test3_alt
;
237 int size
= &end_ftr_fixup_test3
- &ftr_fixup_test3
;
239 fixup
.value
= fixup
.mask
= 0xC;
240 fixup
.start_off
= calc_offset(&fixup
, &ftr_fixup_test3
+ 1);
241 fixup
.end_off
= calc_offset(&fixup
, &ftr_fixup_test3
+ 2);
242 fixup
.alt_start_off
= calc_offset(&fixup
, &ftr_fixup_test3_alt
);
243 fixup
.alt_end_off
= calc_offset(&fixup
, &ftr_fixup_test3_alt
+ 2);
246 check(memcmp(&ftr_fixup_test3
, &ftr_fixup_test3_orig
, size
) == 0);
248 /* Expect nothing to be patched, and the error returned to us */
249 check(patch_feature_section(0xF, &fixup
) == 1);
250 check(memcmp(&ftr_fixup_test3
, &ftr_fixup_test3_orig
, size
) == 0);
251 check(patch_feature_section(0, &fixup
) == 1);
252 check(memcmp(&ftr_fixup_test3
, &ftr_fixup_test3_orig
, size
) == 0);
253 check(patch_feature_section(~0xF, &fixup
) == 1);
254 check(memcmp(&ftr_fixup_test3
, &ftr_fixup_test3_orig
, size
) == 0);
257 static void test_alternative_case_too_small(void)
259 extern unsigned int ftr_fixup_test4
;
260 extern unsigned int end_ftr_fixup_test4
;
261 extern unsigned int ftr_fixup_test4_orig
;
262 extern unsigned int ftr_fixup_test4_alt
;
263 extern unsigned int ftr_fixup_test4_expected
;
264 int size
= &end_ftr_fixup_test4
- &ftr_fixup_test4
;
267 /* Check a high-bit flag */
268 flag
= 1UL << ((sizeof(unsigned long) - 1) * 8);
269 fixup
.value
= fixup
.mask
= flag
;
270 fixup
.start_off
= calc_offset(&fixup
, &ftr_fixup_test4
+ 1);
271 fixup
.end_off
= calc_offset(&fixup
, &ftr_fixup_test4
+ 5);
272 fixup
.alt_start_off
= calc_offset(&fixup
, &ftr_fixup_test4_alt
);
273 fixup
.alt_end_off
= calc_offset(&fixup
, &ftr_fixup_test4_alt
+ 2);
276 check(memcmp(&ftr_fixup_test4
, &ftr_fixup_test4_orig
, size
) == 0);
278 /* Check we don't patch if the value matches */
279 patch_feature_section(flag
, &fixup
);
280 check(memcmp(&ftr_fixup_test4
, &ftr_fixup_test4_orig
, size
) == 0);
282 /* Check we do patch if the value doesn't match */
283 patch_feature_section(0, &fixup
);
284 check(memcmp(&ftr_fixup_test4
, &ftr_fixup_test4_expected
, size
) == 0);
286 /* Check we do patch if the mask doesn't match */
287 memcpy(&ftr_fixup_test4
, &ftr_fixup_test4_orig
, size
);
288 check(memcmp(&ftr_fixup_test4
, &ftr_fixup_test4_orig
, size
) == 0);
289 patch_feature_section(~flag
, &fixup
);
290 check(memcmp(&ftr_fixup_test4
, &ftr_fixup_test4_expected
, size
) == 0);
293 static void test_alternative_case_with_branch(void)
295 extern unsigned int ftr_fixup_test5
;
296 extern unsigned int end_ftr_fixup_test5
;
297 extern unsigned int ftr_fixup_test5_expected
;
298 int size
= &end_ftr_fixup_test5
- &ftr_fixup_test5
;
300 check(memcmp(&ftr_fixup_test5
, &ftr_fixup_test5_expected
, size
) == 0);
303 static void test_alternative_case_with_external_branch(void)
305 extern unsigned int ftr_fixup_test6
;
306 extern unsigned int end_ftr_fixup_test6
;
307 extern unsigned int ftr_fixup_test6_expected
;
308 int size
= &end_ftr_fixup_test6
- &ftr_fixup_test6
;
310 check(memcmp(&ftr_fixup_test6
, &ftr_fixup_test6_expected
, size
) == 0);
313 static void test_cpu_macros(void)
315 extern u8 ftr_fixup_test_FTR_macros
;
316 extern u8 ftr_fixup_test_FTR_macros_expected
;
317 unsigned long size
= &ftr_fixup_test_FTR_macros_expected
-
318 &ftr_fixup_test_FTR_macros
;
320 /* The fixups have already been done for us during boot */
321 check(memcmp(&ftr_fixup_test_FTR_macros
,
322 &ftr_fixup_test_FTR_macros_expected
, size
) == 0);
325 static void test_fw_macros(void)
328 extern u8 ftr_fixup_test_FW_FTR_macros
;
329 extern u8 ftr_fixup_test_FW_FTR_macros_expected
;
330 unsigned long size
= &ftr_fixup_test_FW_FTR_macros_expected
-
331 &ftr_fixup_test_FW_FTR_macros
;
333 /* The fixups have already been done for us during boot */
334 check(memcmp(&ftr_fixup_test_FW_FTR_macros
,
335 &ftr_fixup_test_FW_FTR_macros_expected
, size
) == 0);
339 static void test_lwsync_macros(void)
341 extern u8 lwsync_fixup_test
;
342 extern u8 end_lwsync_fixup_test
;
343 extern u8 lwsync_fixup_test_expected_LWSYNC
;
344 extern u8 lwsync_fixup_test_expected_SYNC
;
345 unsigned long size
= &end_lwsync_fixup_test
-
348 /* The fixups have already been done for us during boot */
349 if (cur_cpu_spec
->cpu_features
& CPU_FTR_LWSYNC
) {
350 check(memcmp(&lwsync_fixup_test
,
351 &lwsync_fixup_test_expected_LWSYNC
, size
) == 0);
353 check(memcmp(&lwsync_fixup_test
,
354 &lwsync_fixup_test_expected_SYNC
, size
) == 0);
358 static int __init
test_feature_fixups(void)
360 printk(KERN_DEBUG
"Running feature fixup self-tests ...\n");
362 test_basic_patching();
363 test_alternative_patching();
364 test_alternative_case_too_big();
365 test_alternative_case_too_small();
366 test_alternative_case_with_branch();
367 test_alternative_case_with_external_branch();
370 test_lwsync_macros();
374 late_initcall(test_feature_fixups
);
376 #endif /* CONFIG_FTR_FIXUP_SELFTEST */