Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/lib/feature-fixups.c
10817 views
1
/*
2
* Copyright (C) 2001 Ben. Herrenschmidt ([email protected])
3
*
4
* Modifications for ppc64:
5
* Copyright (C) 2003 Dave Engebretsen <[email protected]>
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
22
23
struct fixup_entry {
24
unsigned long mask;
25
unsigned long value;
26
long start_off;
27
long end_off;
28
long alt_start_off;
29
long alt_end_off;
30
};
31
32
static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
33
{
34
/*
35
* We store the offset to the code as a negative offset from
36
* the start of the alt_entry, to support the VDSO. This
37
* routine converts that back into an actual address.
38
*/
39
return (unsigned int *)((unsigned long)fcur + offset);
40
}
41
42
static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
43
unsigned int *alt_start, unsigned int *alt_end)
44
{
45
unsigned int instr;
46
47
instr = *src;
48
49
if (instr_is_relative_branch(*src)) {
50
unsigned int *target = (unsigned int *)branch_target(src);
51
52
/* Branch within the section doesn't need translating */
53
if (target < alt_start || target >= alt_end) {
54
instr = translate_branch(dest, src);
55
if (!instr)
56
return 1;
57
}
58
}
59
60
patch_instruction(dest, instr);
61
62
return 0;
63
}
64
65
static int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
66
{
67
unsigned int *start, *end, *alt_start, *alt_end, *src, *dest;
68
69
start = calc_addr(fcur, fcur->start_off);
70
end = calc_addr(fcur, fcur->end_off);
71
alt_start = calc_addr(fcur, fcur->alt_start_off);
72
alt_end = calc_addr(fcur, fcur->alt_end_off);
73
74
if ((alt_end - alt_start) > (end - start))
75
return 1;
76
77
if ((value & fcur->mask) == fcur->value)
78
return 0;
79
80
src = alt_start;
81
dest = start;
82
83
for (; src < alt_end; src++, dest++) {
84
if (patch_alt_instruction(src, dest, alt_start, alt_end))
85
return 1;
86
}
87
88
for (; dest < end; dest++)
89
patch_instruction(dest, PPC_INST_NOP);
90
91
return 0;
92
}
93
94
void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
95
{
96
struct fixup_entry *fcur, *fend;
97
98
fcur = fixup_start;
99
fend = fixup_end;
100
101
for (; fcur < fend; fcur++) {
102
if (patch_feature_section(value, fcur)) {
103
WARN_ON(1);
104
printk("Unable to patch feature section at %p - %p" \
105
" with %p - %p\n",
106
calc_addr(fcur, fcur->start_off),
107
calc_addr(fcur, fcur->end_off),
108
calc_addr(fcur, fcur->alt_start_off),
109
calc_addr(fcur, fcur->alt_end_off));
110
}
111
}
112
}
113
114
void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
115
{
116
long *start, *end;
117
unsigned int *dest;
118
119
if (!(value & CPU_FTR_LWSYNC))
120
return ;
121
122
start = fixup_start;
123
end = fixup_end;
124
125
for (; start < end; start++) {
126
dest = (void *)start + *start;
127
patch_instruction(dest, PPC_INST_LWSYNC);
128
}
129
}
130
131
#ifdef CONFIG_FTR_FIXUP_SELFTEST
132
133
#define check(x) \
134
if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
135
136
/* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
137
static struct fixup_entry fixup;
138
139
static long calc_offset(struct fixup_entry *entry, unsigned int *p)
140
{
141
return (unsigned long)p - (unsigned long)entry;
142
}
143
144
void test_basic_patching(void)
145
{
146
extern unsigned int ftr_fixup_test1;
147
extern unsigned int end_ftr_fixup_test1;
148
extern unsigned int ftr_fixup_test1_orig;
149
extern unsigned int ftr_fixup_test1_expected;
150
int size = &end_ftr_fixup_test1 - &ftr_fixup_test1;
151
152
fixup.value = fixup.mask = 8;
153
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test1 + 1);
154
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test1 + 2);
155
fixup.alt_start_off = fixup.alt_end_off = 0;
156
157
/* Sanity check */
158
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
159
160
/* Check we don't patch if the value matches */
161
patch_feature_section(8, &fixup);
162
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
163
164
/* Check we do patch if the value doesn't match */
165
patch_feature_section(0, &fixup);
166
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
167
168
/* Check we do patch if the mask doesn't match */
169
memcpy(&ftr_fixup_test1, &ftr_fixup_test1_orig, size);
170
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
171
patch_feature_section(~8, &fixup);
172
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
173
}
174
175
static void test_alternative_patching(void)
176
{
177
extern unsigned int ftr_fixup_test2;
178
extern unsigned int end_ftr_fixup_test2;
179
extern unsigned int ftr_fixup_test2_orig;
180
extern unsigned int ftr_fixup_test2_alt;
181
extern unsigned int ftr_fixup_test2_expected;
182
int size = &end_ftr_fixup_test2 - &ftr_fixup_test2;
183
184
fixup.value = fixup.mask = 0xF;
185
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test2 + 1);
186
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test2 + 2);
187
fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test2_alt);
188
fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test2_alt + 1);
189
190
/* Sanity check */
191
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
192
193
/* Check we don't patch if the value matches */
194
patch_feature_section(0xF, &fixup);
195
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
196
197
/* Check we do patch if the value doesn't match */
198
patch_feature_section(0, &fixup);
199
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
200
201
/* Check we do patch if the mask doesn't match */
202
memcpy(&ftr_fixup_test2, &ftr_fixup_test2_orig, size);
203
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
204
patch_feature_section(~0xF, &fixup);
205
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
206
}
207
208
static void test_alternative_case_too_big(void)
209
{
210
extern unsigned int ftr_fixup_test3;
211
extern unsigned int end_ftr_fixup_test3;
212
extern unsigned int ftr_fixup_test3_orig;
213
extern unsigned int ftr_fixup_test3_alt;
214
int size = &end_ftr_fixup_test3 - &ftr_fixup_test3;
215
216
fixup.value = fixup.mask = 0xC;
217
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test3 + 1);
218
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test3 + 2);
219
fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test3_alt);
220
fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test3_alt + 2);
221
222
/* Sanity check */
223
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
224
225
/* Expect nothing to be patched, and the error returned to us */
226
check(patch_feature_section(0xF, &fixup) == 1);
227
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
228
check(patch_feature_section(0, &fixup) == 1);
229
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
230
check(patch_feature_section(~0xF, &fixup) == 1);
231
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
232
}
233
234
static void test_alternative_case_too_small(void)
235
{
236
extern unsigned int ftr_fixup_test4;
237
extern unsigned int end_ftr_fixup_test4;
238
extern unsigned int ftr_fixup_test4_orig;
239
extern unsigned int ftr_fixup_test4_alt;
240
extern unsigned int ftr_fixup_test4_expected;
241
int size = &end_ftr_fixup_test4 - &ftr_fixup_test4;
242
unsigned long flag;
243
244
/* Check a high-bit flag */
245
flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
246
fixup.value = fixup.mask = flag;
247
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test4 + 1);
248
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test4 + 5);
249
fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test4_alt);
250
fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test4_alt + 2);
251
252
/* Sanity check */
253
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
254
255
/* Check we don't patch if the value matches */
256
patch_feature_section(flag, &fixup);
257
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
258
259
/* Check we do patch if the value doesn't match */
260
patch_feature_section(0, &fixup);
261
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
262
263
/* Check we do patch if the mask doesn't match */
264
memcpy(&ftr_fixup_test4, &ftr_fixup_test4_orig, size);
265
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
266
patch_feature_section(~flag, &fixup);
267
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
268
}
269
270
static void test_alternative_case_with_branch(void)
271
{
272
extern unsigned int ftr_fixup_test5;
273
extern unsigned int end_ftr_fixup_test5;
274
extern unsigned int ftr_fixup_test5_expected;
275
int size = &end_ftr_fixup_test5 - &ftr_fixup_test5;
276
277
check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0);
278
}
279
280
static void test_alternative_case_with_external_branch(void)
281
{
282
extern unsigned int ftr_fixup_test6;
283
extern unsigned int end_ftr_fixup_test6;
284
extern unsigned int ftr_fixup_test6_expected;
285
int size = &end_ftr_fixup_test6 - &ftr_fixup_test6;
286
287
check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0);
288
}
289
290
static void test_cpu_macros(void)
291
{
292
extern u8 ftr_fixup_test_FTR_macros;
293
extern u8 ftr_fixup_test_FTR_macros_expected;
294
unsigned long size = &ftr_fixup_test_FTR_macros_expected -
295
&ftr_fixup_test_FTR_macros;
296
297
/* The fixups have already been done for us during boot */
298
check(memcmp(&ftr_fixup_test_FTR_macros,
299
&ftr_fixup_test_FTR_macros_expected, size) == 0);
300
}
301
302
static void test_fw_macros(void)
303
{
304
#ifdef CONFIG_PPC64
305
extern u8 ftr_fixup_test_FW_FTR_macros;
306
extern u8 ftr_fixup_test_FW_FTR_macros_expected;
307
unsigned long size = &ftr_fixup_test_FW_FTR_macros_expected -
308
&ftr_fixup_test_FW_FTR_macros;
309
310
/* The fixups have already been done for us during boot */
311
check(memcmp(&ftr_fixup_test_FW_FTR_macros,
312
&ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
313
#endif
314
}
315
316
static void test_lwsync_macros(void)
317
{
318
extern u8 lwsync_fixup_test;
319
extern u8 end_lwsync_fixup_test;
320
extern u8 lwsync_fixup_test_expected_LWSYNC;
321
extern u8 lwsync_fixup_test_expected_SYNC;
322
unsigned long size = &end_lwsync_fixup_test -
323
&lwsync_fixup_test;
324
325
/* The fixups have already been done for us during boot */
326
if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
327
check(memcmp(&lwsync_fixup_test,
328
&lwsync_fixup_test_expected_LWSYNC, size) == 0);
329
} else {
330
check(memcmp(&lwsync_fixup_test,
331
&lwsync_fixup_test_expected_SYNC, size) == 0);
332
}
333
}
334
335
static int __init test_feature_fixups(void)
336
{
337
printk(KERN_DEBUG "Running feature fixup self-tests ...\n");
338
339
test_basic_patching();
340
test_alternative_patching();
341
test_alternative_case_too_big();
342
test_alternative_case_too_small();
343
test_alternative_case_with_branch();
344
test_alternative_case_with_external_branch();
345
test_cpu_macros();
346
test_fw_macros();
347
test_lwsync_macros();
348
349
return 0;
350
}
351
late_initcall(test_feature_fixups);
352
353
#endif /* CONFIG_FTR_FIXUP_SELFTEST */
354
355