b47bf3559756bc400c68faa9aa13a6820a315d60
[openwrt/staging/wigyori.git] / target / linux / hifiveu / patches-5.10 / 0020-riscv-Introduce-alternative-mechanism-to-apply-errat.patch
1 From a3e6ca97499c444fee45c7efafcdef311142952c Mon Sep 17 00:00:00 2001
2 From: Vincent Chen <vincent.chen@sifive.com>
3 Date: Tue, 19 Jan 2021 03:30:22 -0800
4 Subject: [PATCH 20/29] riscv: Introduce alternative mechanism to apply errata
5 solution
6
7 Introduce the "alternative" mechanism from ARM64 and x86 to apply the CPU
8 vendors' errata solution at runtime. The main purpose of this patch is
9 to provide a framework. Therefore, the implementation is quite basic for
10 now so that some scenarios could not use this scheme such as patching code
11 to a module, relocating the patching code and heterogeneous CPU topology.
12
13 Users could use the two macros ALTINSN and ALTDATA to modify the existing
14 instruction and data respectively. By specifying the parameters vendorid,
15 archid and impid, the kernel can probably apply the patch codes based on
16 the same information of the running CPU. To keep the flexibility, the user
17 can pass the specific kernel configure to the alternative macro to enable
18 or disable the errata solution at compile time.
19
20 Rebased for v5.10.8 by David Abdurachmanov.
21
22 Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
23 ---
24 arch/riscv/Kconfig | 7 ++
25 arch/riscv/Makefile | 1 +
26 arch/riscv/errata/Makefile | 1 +
27 arch/riscv/errata/alternative.c | 74 ++++++++++++++++
28 arch/riscv/include/asm/alternative-macros.h | 133 ++++++++++++++++++++++++++++
29 arch/riscv/include/asm/alternative.h | 43 +++++++++
30 arch/riscv/include/asm/asm.h | 1 +
31 arch/riscv/include/asm/sections.h | 3 +
32 arch/riscv/kernel/smpboot.c | 4 +
33 arch/riscv/kernel/vmlinux.lds.S | 21 +++++
34 10 files changed, 288 insertions(+)
35 create mode 100644 arch/riscv/errata/Makefile
36 create mode 100644 arch/riscv/errata/alternative.c
37 create mode 100644 arch/riscv/include/asm/alternative-macros.h
38 create mode 100644 arch/riscv/include/asm/alternative.h
39
40 diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
41 index 3474286..2717aa0 100644
42 --- a/arch/riscv/Kconfig
43 +++ b/arch/riscv/Kconfig
44 @@ -84,6 +84,7 @@ config RISCV
45 select PCI_MSI if PCI
46 select RISCV_INTC
47 select RISCV_TIMER if RISCV_SBI
48 + select RISCV_ERRATA
49 select SPARSE_IRQ
50 select SYSCTL_EXCEPTION_TRACE
51 select THREAD_INFO_IN_TASK
52 @@ -423,6 +424,12 @@ config BUILTIN_DTB
53 depends on RISCV_M_MODE
54 depends on OF
55
56 +config RISCV_ERRATA
57 + bool "Runtime apply errata patch"
58 + help
59 + This option provides the support for applying the errata patch
60 + at runtime.
61 +
62 menu "Power management options"
63
64 source "kernel/power/Kconfig"
65 diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
66 index 0289a97..cd23fb0 100644
67 --- a/arch/riscv/Makefile
68 +++ b/arch/riscv/Makefile
69 @@ -75,6 +75,7 @@ KBUILD_IMAGE := $(boot)/Image.gz
70 head-y := arch/riscv/kernel/head.o
71
72 core-y += arch/riscv/
73 +core-$(CONFIG_RISCV_ERRATA) += arch/riscv/errata/
74
75 libs-y += arch/riscv/lib/
76 libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
77 diff --git a/arch/riscv/errata/Makefile b/arch/riscv/errata/Makefile
78 new file mode 100644
79 index 00000000..43e6d54
80 --- /dev/null
81 +++ b/arch/riscv/errata/Makefile
82 @@ -0,0 +1 @@
83 +obj-y += alternative.o
84 diff --git a/arch/riscv/errata/alternative.c b/arch/riscv/errata/alternative.c
85 new file mode 100644
86 index 00000000..0827c05
87 --- /dev/null
88 +++ b/arch/riscv/errata/alternative.c
89 @@ -0,0 +1,74 @@
90 +// SPDX-License-Identifier: GPL-2.0-only
91 +/*
92 + * alternative runtime patching
93 + * inspired by the ARM64 and x86 version
94 + *
95 + * Copyright (C) 2021 Sifive.
96 + */
97 +
98 +#include <linux/init.h>
99 +#include <linux/cpu.h>
100 +#include <linux/uaccess.h>
101 +#include <asm/patch.h>
102 +#include <asm/alternative.h>
103 +#include <asm/sections.h>
104 +
105 +struct alt_region {
106 + struct alt_entry *begin;
107 + struct alt_entry *end;
108 +};
109 +
110 +static bool __init default_checkfunc(struct alt_entry *alt)
111 +{
112 + return false;
113 +}
114 +
115 +static bool (*errata_checkfunc)(struct alt_entry *alt) = default_checkfunc;
116 +typedef int (*patch_func_t)(void *addr, const void *insn, size_t size);
117 +
118 +static void __apply_alternatives(void *alt_region, void *alt_patch_func)
119 +{
120 + struct alt_entry *alt;
121 + struct alt_region *region = alt_region;
122 +
123 + for (alt = region->begin; alt < region->end; alt++) {
124 + if (!errata_checkfunc(alt))
125 + continue;
126 + ((patch_func_t)alt_patch_func)(alt->old_ptr, alt->alt_ptr, alt->old_len);
127 + }
128 +}
129 +
130 +static void __init init_alternatvie(void)
131 +{
132 + struct errata_checkfunc_id *ptr;
133 +
134 + for (ptr = (struct errata_checkfunc_id *)__alt_checkfunc_table;
135 + ptr < (struct errata_checkfunc_id *)__alt_checkfunc_table_end;
136 + ptr++) {
137 + if (cpu_manufactor_info.vendorid == ptr->vendorid)
138 + errata_checkfunc = ptr->func;
139 + }
140 +}
141 +
142 +/*
143 + * This is called very early in the boot process (directly after we run
144 + * a feature detect on the boot CPU). No need to worry about other CPUs
145 + * here.
146 + */
147 +void __init apply_boot_alternatives(void)
148 +{
149 + struct alt_region region;
150 +
151 + init_alternatvie();
152 + /* If called on non-boot cpu things could go wrong */
153 + WARN_ON(smp_processor_id() != 0);
154 +
155 + region.begin = (struct alt_entry *)__alt_insn;
156 + region.end = (struct alt_entry *)__alt_insn_end;
157 + __apply_alternatives(&region, patch_text_nosync);
158 +
159 + region.begin = (struct alt_entry *)__alt_data;
160 + region.end = (struct alt_entry *)__alt_data_end;
161 + __apply_alternatives(&region, copy_to_kernel_nofault);
162 +}
163 +
164 diff --git a/arch/riscv/include/asm/alternative-macros.h b/arch/riscv/include/asm/alternative-macros.h
165 new file mode 100644
166 index 00000000..dd06ded
167 --- /dev/null
168 +++ b/arch/riscv/include/asm/alternative-macros.h
169 @@ -0,0 +1,133 @@
170 +/* SPDX-License-Identifier: GPL-2.0 */
171 +#ifndef __ASM_ALTERNATIVE_MACROS_H
172 +#define __ASM_ALTERNATIVE_MACROS_H
173 +
174 +#ifndef __ASSEMBLY__
175 +
176 +#include <asm/asm.h>
177 +#include <linux/stringify.h>
178 +
179 +#define ALT_ENTRY(oldptr, altptr, vendorid, archid, impid, oldlen, altlen) \
180 + RISCV_PTR " " oldptr "\n" \
181 + RISCV_PTR " " altptr "\n" \
182 + REG_ASM " " vendorid "\n" \
183 + REG_ASM " " archid "\ n" \
184 + REG_ASM " " impid "\n" \
185 + ".word " oldlen "\n" \
186 + ".word " altlen "\n" \
187 +
188 +#define __ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, enable) \
189 + ".if " __stringify(enable) " == 1\n" \
190 + "886 :\n\t" \
191 + oldinsn "\n" \
192 + "887 :\n" \
193 + ".pushsection .altinsn, \"a\"\n" \
194 + ALT_ENTRY("886b", "888f", vendorid, archid, impid, "887b - 886b", "889f - 888f") \
195 + ".popsection\n" \
196 + ".subsection 1\n" \
197 + "888 :\n\t" \
198 + altinsn "\n" \
199 + "889 :\n\t" \
200 + ".previous\n" \
201 + ".org . - (887b - 886b) + (889b - 888b)\n\t" \
202 + ".org . - (889b - 888b) + (887b - 886b)\n\t" \
203 + ".endif\n"
204 +
205 +#define _ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, CONFIG_k, ...) \
206 + __ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, IS_ENABLED(CONFIG_k))
207 +
208 +#define __ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, enable) \
209 + ".if " __stringify(enable) " == 1\n" \
210 + ".pushsection .altdata, \"a\"\n" \
211 + ALT_ENTRY(oldptr, altptr, vendorid, archid, impid, oldlen, altlen) \
212 + ".popsection\n" \
213 + ".previous\n" \
214 + ".endif\n"
215 +
216 +#define _ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, CONFIG_k, ...) \
217 + __ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, IS_ENABLED(CONFIG_k))
218 +
219 +#else
220 +.macro ALT_ENTRY oldptr altptr vendorid archid impid oldlen alt_len
221 + RISCV_PTR \oldptr
222 + RISCV_PTR \altptr
223 + REG_ASM \vendorid
224 + REG_ASM \archid
225 + REG_ASM \impid
226 + .word \oldlen
227 + .word \alt_len
228 +.endm
229 +
230 +.macro __ALTINSN_CFG insn1 insn2 vendorid archid impid enable = 1
231 + .if \enable
232 +886 :
233 + \insn1
234 +887 :
235 + .pushsection .altinsn, "a"
236 + ALT_ENTRY 886b, 888f, \vendorid, \archid, \impid, 887b - 886b, 889f - 888f
237 + .popsection
238 + .subsection 1
239 +888 :
240 + \insn2
241 +889 :
242 + .previous
243 + .org . - (889b - 888b) + (887b - 886b)
244 + .org . - (887b - 886b) + (889b - 888b)
245 + .endif
246 +.endm
247 +
248 +#define _ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, CONFIG_k, ...) \
249 + __ALTINSN_CFG oldinsn, altinsn, vendorid, archid, impid, IS_ENABLED(CONFIG_k)
250 +
251 +.macro __ALTDATA_CFG oldptr altptr vendorid archid impid oldlen altlen enable = 1
252 + .if \enable
253 + .pushsection .altdata, "a"
254 + ALT_ENTRY \oldptr \altptr \vendorid \archid \impid \oldlen \altlen
255 + .popsection
256 + .org . - \oldlen + \altlen
257 + .org . - \altlen + \oldlen
258 + .endif
259 +.endm
260 +
261 +#define _ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, CONFIG_k, ...) \
262 + __ALTDATA_CFG oldptr, altptr, vendorid, archid, impid, oldlen, altlen, IS_ENABLED(CONFIG_k)
263 +
264 +#endif
265 +
266 +/*
267 + * Usage: asm(ALTINSN(oldinsn, altinsn, vendorid, archid, impid));
268 + *
269 + * Usage: asm(ALTERNATIVE(oldinsn, altinsn, vendorid, archid, impid, CONFIG_FOO));
270 + *
271 + * oldinsn: The old instruction which will be replaced.
272 + * altinsn: The replacement instruction.
273 + * vendorid: The CPU vendor ID.
274 + * archid: The CPU architecture ID.
275 + * impid: The CPU implement ID.
276 + *
277 + * N.B. If CONFIG_FOO is specified, but not selected, the whole block
278 + * will be omitted, including oldinstr.
279 + */
280 +#define ALTINSN(oldinsn, altinsn, ...) \
281 + _ALTINSN_CFG(oldinsn, altinsn, __VA_ARGS__, 1)
282 +
283 +/*
284 + * Usage: asm(ALTDATA(oldptr, altptr, vendorid, archid, impid, oldlen, altlen));
285 + *
286 + * Usage: asm(ALTERNATIVE(oldptr, altptr, feature, CONFIG_FOO));
287 + *
288 + * oldptr: The address of old data.
289 + * altinsn: The address of replacement data.
290 + * vendorid: The CPU vendor ID.
291 + * archid: The CPU architecture ID.
292 + * impid: The CPU implement ID.
293 + * oldlen: The data length of old data.
294 + * newlen: The data length of new data.
295 + *
296 + * N.B. If CONFIG_FOO is specified, but not selected, the whole block
297 + * will be omitted.
298 + */
299 +
300 +#define ALTDATA(oldptr, altptr, ...) \
301 + _ALTDATA_CFG(oldptr, altptr, __VA_ARGS__, 1)
302 +#endif
303 diff --git a/arch/riscv/include/asm/alternative.h b/arch/riscv/include/asm/alternative.h
304 new file mode 100644
305 index 00000000..fc1d929
306 --- /dev/null
307 +++ b/arch/riscv/include/asm/alternative.h
308 @@ -0,0 +1,43 @@
309 +/* SPDX-License-Identifier: GPL-2.0-only */
310 +/*
311 + * Copyright (C) 2021 Sifive.
312 + */
313 +
314 +#ifndef __ASM_ALTERNATIVE_H
315 +#define __ASM_ALTERNATIVE_H
316 +
317 +#include <asm/alternative-macros.h>
318 +#ifndef __ASSEMBLY__
319 +
320 +#include <linux/init.h>
321 +#include <linux/types.h>
322 +#include <linux/stddef.h>
323 +#include <asm/hwcap.h>
324 +
325 +void __init apply_boot_alternatives(void);
326 +
327 +struct alt_entry {
328 + void *old_ptr; /* address of original instruciton or data */
329 + void *alt_ptr; /* address of replacement instruction or data */
330 + unsigned long vendorid; /* cpu vendor id */
331 + unsigned long archid; /* cpu architecture id */
332 + unsigned long impid; /* cpu implement id */
333 + unsigned int old_len; /* size of original instruction(s) or data(s) */
334 + unsigned int alt_len; /* size of new instruction(s) or data(s) */
335 +};
336 +
337 +struct errata_checkfunc_id {
338 + unsigned long vendorid;
339 + bool (*func)(struct alt_entry *alt);
340 +};
341 +
342 +extern struct cpu_manufactor_info_t cpu_manufactor_info;
343 +
344 +#define REGISTER_ERRATA_CHECKFUNC(checkfunc, vendor_id) \
345 + static const struct errata_checkfunc_id _errata_check_##vendor_id \
346 + __used __section(".alt_checkfunc_table") \
347 + __aligned(__alignof__(struct errata_checkfunc_id)) = \
348 + { .vendorid = vendor_id, \
349 + .func = checkfunc }
350 +#endif
351 +#endif
352 diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h
353 index 9c992a8..618d7c5 100644
354 --- a/arch/riscv/include/asm/asm.h
355 +++ b/arch/riscv/include/asm/asm.h
356 @@ -23,6 +23,7 @@
357 #define REG_L __REG_SEL(ld, lw)
358 #define REG_S __REG_SEL(sd, sw)
359 #define REG_SC __REG_SEL(sc.d, sc.w)
360 +#define REG_ASM __REG_SEL(.dword, .word)
361 #define SZREG __REG_SEL(8, 4)
362 #define LGREG __REG_SEL(3, 2)
363
364 diff --git a/arch/riscv/include/asm/sections.h b/arch/riscv/include/asm/sections.h
365 index 3a9971b..2ee8e12 100644
366 --- a/arch/riscv/include/asm/sections.h
367 +++ b/arch/riscv/include/asm/sections.h
368 @@ -9,5 +9,8 @@
369
370 extern char _start[];
371 extern char _start_kernel[];
372 +extern char __alt_checkfunc_table[], __alt_checkfunc_table_end[];
373 +extern char __alt_data[], __alt_data_end[];
374 +extern char __alt_insn[], __alt_insn_end[];
375
376 #endif /* __ASM_SECTIONS_H */
377 diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
378 index 96167d5..7177ee2 100644
379 --- a/arch/riscv/kernel/smpboot.c
380 +++ b/arch/riscv/kernel/smpboot.c
381 @@ -31,6 +31,7 @@
382 #include <asm/sections.h>
383 #include <asm/sbi.h>
384 #include <asm/smp.h>
385 +#include <asm/alternative.h>
386
387 #include "head.h"
388
389 @@ -39,6 +40,9 @@ static DECLARE_COMPLETION(cpu_running);
390 void __init smp_prepare_boot_cpu(void)
391 {
392 init_cpu_topology();
393 +#ifdef CONFIG_RISCV_ERRATA
394 + apply_boot_alternatives();
395 +#endif
396 }
397
398 void __init smp_prepare_cpus(unsigned int max_cpus)
399 diff --git a/arch/riscv/kernel/vmlinux.lds.S b/arch/riscv/kernel/vmlinux.lds.S
400 index 3ffbd6c..9df933c 100644
401 --- a/arch/riscv/kernel/vmlinux.lds.S
402 +++ b/arch/riscv/kernel/vmlinux.lds.S
403 @@ -77,6 +77,27 @@ SECTIONS
404
405 INIT_DATA_SECTION(16)
406
407 + . = ALIGN(8);
408 + .alt_checkfunc_table : {
409 + __alt_checkfunc_table = .;
410 + *(.alt_checkfunc_table)
411 + __alt_checkfunc_table_end = .;
412 + }
413 +
414 + . = ALIGN(8);
415 + .altinsn : {
416 + __alt_insn = .;
417 + *(.altinsn)
418 + __alt_insn_end = .;
419 + }
420 +
421 + . = ALIGN(8);
422 + .altdata : {
423 + __alt_data = .;
424 + *(.altdata)
425 + __alt_data_end = .;
426 + }
427 +
428 /* Start of data section */
429 _sdata = .;
430 RO_DATA(SECTION_ALIGN)
431 --
432 2.7.4
433