~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/lib/feature-fixups.c

Version: ~ [ linux-5.17-rc1 ] ~ [ linux-5.16.2 ] ~ [ linux-5.15.16 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.93 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.173 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.225 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.262 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.297 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.299 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
  3  *
  4  *  Modifications for ppc64:
  5  *      Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com>
  6  *
  7  *  Copyright 2008 Michael Ellerman, IBM Corporation.
  8  *
  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.
 13  */
 14 
 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>
 21 #include <asm/page.h>
 22 #include <asm/sections.h>
 23 
 24 
 25 struct fixup_entry {
 26         unsigned long   mask;
 27         unsigned long   value;
 28         long            start_off;
 29         long            end_off;
 30         long            alt_start_off;
 31         long            alt_end_off;
 32 };
 33 
 34 static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
 35 {
 36         /*
 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.
 40          */
 41         return (unsigned int *)((unsigned long)fcur + offset);
 42 }
 43 
 44 static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
 45                                  unsigned int *alt_start, unsigned int *alt_end)
 46 {
 47         unsigned int instr;
 48 
 49         instr = *src;
 50 
 51         if (instr_is_relative_branch(*src)) {
 52                 unsigned int *target = (unsigned int *)branch_target(src);
 53 
 54                 /* Branch within the section doesn't need translating */
 55                 if (target < alt_start || target >= alt_end) {
 56                         instr = translate_branch(dest, src);
 57                         if (!instr)
 58                                 return 1;
 59                 }
 60         }
 61 
 62         patch_instruction(dest, instr);
 63 
 64         return 0;
 65 }
 66 
 67 static int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
 68 {
 69         unsigned int *start, *end, *alt_start, *alt_end, *src, *dest;
 70 
 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);
 75 
 76         if ((alt_end - alt_start) > (end - start))
 77                 return 1;
 78 
 79         if ((value & fcur->mask) == fcur->value)
 80                 return 0;
 81 
 82         src = alt_start;
 83         dest = start;
 84 
 85         for (; src < alt_end; src++, dest++) {
 86                 if (patch_alt_instruction(src, dest, alt_start, alt_end))
 87                         return 1;
 88         }
 89 
 90         for (; dest < end; dest++)
 91                 patch_instruction(dest, PPC_INST_NOP);
 92 
 93         return 0;
 94 }
 95 
 96 void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
 97 {
 98         struct fixup_entry *fcur, *fend;
 99 
100         fcur = fixup_start;
101         fend = fixup_end;
102 
103         for (; fcur < fend; fcur++) {
104                 if (patch_feature_section(value, fcur)) {
105                         WARN_ON(1);
106                         printk("Unable to patch feature section at %p - %p" \
107                                 " with %p - %p\n",
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));
112                 }
113         }
114 }
115 
116 void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
117 {
118         long *start, *end;
119         unsigned int *dest;
120 
121         if (!(value & CPU_FTR_LWSYNC))
122                 return ;
123 
124         start = fixup_start;
125         end = fixup_end;
126 
127         for (; start < end; start++) {
128                 dest = (void *)start + *start;
129                 patch_instruction(dest, PPC_INST_LWSYNC);
130         }
131 }
132 
133 void do_final_fixups(void)
134 {
135 #if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE)
136         int *src, *dest;
137         unsigned long length;
138 
139         if (PHYSICAL_START == 0)
140                 return;
141 
142         src = (int *)(KERNELBASE + PHYSICAL_START);
143         dest = (int *)KERNELBASE;
144         length = (__end_interrupts - _stext) / sizeof(int);
145 
146         while (length--) {
147                 patch_instruction(dest, *src);
148                 src++;
149                 dest++;
150         }
151 #endif
152 }
153 
154 #ifdef CONFIG_FTR_FIXUP_SELFTEST
155 
156 #define check(x)        \
157         if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
158 
159 /* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
160 static struct fixup_entry fixup;
161 
162 static long calc_offset(struct fixup_entry *entry, unsigned int *p)
163 {
164         return (unsigned long)p - (unsigned long)entry;
165 }
166 
167 static void test_basic_patching(void)
168 {
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;
174 
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;
179 
180         /* Sanity check */
181         check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
182 
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);
186 
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);
190 
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);
196 }
197 
198 static void test_alternative_patching(void)
199 {
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;
206 
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);
212 
213         /* Sanity check */
214         check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
215 
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);
219 
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);
223 
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);
229 }
230 
231 static void test_alternative_case_too_big(void)
232 {
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;
238 
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);
244 
245         /* Sanity check */
246         check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
247 
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);
255 }
256 
257 static void test_alternative_case_too_small(void)
258 {
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;
265         unsigned long flag;
266 
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);
274 
275         /* Sanity check */
276         check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
277 
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);
281 
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);
285 
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);
291 }
292 
293 static void test_alternative_case_with_branch(void)
294 {
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;
299 
300         check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0);
301 }
302 
303 static void test_alternative_case_with_external_branch(void)
304 {
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;
309 
310         check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0);
311 }
312 
313 static void test_cpu_macros(void)
314 {
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;
319 
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);
323 }
324 
325 static void test_fw_macros(void)
326 {
327 #ifdef CONFIG_PPC64
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;
332 
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);
336 #endif
337 }
338 
339 static void test_lwsync_macros(void)
340 {
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 -
346                              &lwsync_fixup_test;
347 
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);
352         } else {
353                 check(memcmp(&lwsync_fixup_test,
354                              &lwsync_fixup_test_expected_SYNC, size) == 0);
355         }
356 }
357 
358 static int __init test_feature_fixups(void)
359 {
360         printk(KERN_DEBUG "Running feature fixup self-tests ...\n");
361 
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();
368         test_cpu_macros();
369         test_fw_macros();
370         test_lwsync_macros();
371 
372         return 0;
373 }
374 late_initcall(test_feature_fixups);
375 
376 #endif /* CONFIG_FTR_FIXUP_SELFTEST */
377 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp