GNU Linux-libre 5.19-rc6-gnu
[releases.git] / arch / riscv / kernel / alternative.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * alternative runtime patching
4  * inspired by the ARM64 and x86 version
5  *
6  * Copyright (C) 2021 Sifive.
7  */
8
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/cpu.h>
12 #include <linux/uaccess.h>
13 #include <asm/alternative.h>
14 #include <asm/sections.h>
15 #include <asm/vendorid_list.h>
16 #include <asm/sbi.h>
17 #include <asm/csr.h>
18
19 struct cpu_manufacturer_info_t {
20         unsigned long vendor_id;
21         unsigned long arch_id;
22         unsigned long imp_id;
23         void (*vendor_patch_func)(struct alt_entry *begin, struct alt_entry *end,
24                                   unsigned long archid, unsigned long impid,
25                                   unsigned int stage);
26 };
27
28 static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info)
29 {
30 #ifdef CONFIG_RISCV_M_MODE
31         cpu_mfr_info->vendor_id = csr_read(CSR_MVENDORID);
32         cpu_mfr_info->arch_id = csr_read(CSR_MARCHID);
33         cpu_mfr_info->imp_id = csr_read(CSR_MIMPID);
34 #else
35         cpu_mfr_info->vendor_id = sbi_get_mvendorid();
36         cpu_mfr_info->arch_id = sbi_get_marchid();
37         cpu_mfr_info->imp_id = sbi_get_mimpid();
38 #endif
39
40         switch (cpu_mfr_info->vendor_id) {
41 #ifdef CONFIG_ERRATA_SIFIVE
42         case SIFIVE_VENDOR_ID:
43                 cpu_mfr_info->vendor_patch_func = sifive_errata_patch_func;
44                 break;
45 #endif
46 #ifdef CONFIG_ERRATA_THEAD
47         case THEAD_VENDOR_ID:
48                 cpu_mfr_info->vendor_patch_func = thead_errata_patch_func;
49                 break;
50 #endif
51         default:
52                 cpu_mfr_info->vendor_patch_func = NULL;
53         }
54 }
55
56 /*
57  * This is called very early in the boot process (directly after we run
58  * a feature detect on the boot CPU). No need to worry about other CPUs
59  * here.
60  */
61 static void __init_or_module _apply_alternatives(struct alt_entry *begin,
62                                                  struct alt_entry *end,
63                                                  unsigned int stage)
64 {
65         struct cpu_manufacturer_info_t cpu_mfr_info;
66
67         riscv_fill_cpu_mfr_info(&cpu_mfr_info);
68
69         riscv_cpufeature_patch_func(begin, end, stage);
70
71         if (!cpu_mfr_info.vendor_patch_func)
72                 return;
73
74         cpu_mfr_info.vendor_patch_func(begin, end,
75                                    cpu_mfr_info.arch_id,
76                                    cpu_mfr_info.imp_id,
77                                    stage);
78 }
79
80 void __init apply_boot_alternatives(void)
81 {
82         /* If called on non-boot cpu things could go wrong */
83         WARN_ON(smp_processor_id() != 0);
84
85         _apply_alternatives((struct alt_entry *)__alt_start,
86                             (struct alt_entry *)__alt_end,
87                             RISCV_ALTERNATIVES_BOOT);
88 }
89
90 /*
91  * apply_early_boot_alternatives() is called from setup_vm() with MMU-off.
92  *
93  * Following requirements should be honoured for it to work correctly:
94  * 1) It should use PC-relative addressing for accessing kernel symbols.
95  *    To achieve this we always use GCC cmodel=medany.
96  * 2) The compiler instrumentation for FTRACE will not work for setup_vm()
97  *    so disable compiler instrumentation when FTRACE is enabled.
98  *
99  * Currently, the above requirements are honoured by using custom CFLAGS
100  * for alternative.o in kernel/Makefile.
101  */
102 void __init apply_early_boot_alternatives(void)
103 {
104 #ifdef CONFIG_RISCV_ALTERNATIVE_EARLY
105         _apply_alternatives((struct alt_entry *)__alt_start,
106                             (struct alt_entry *)__alt_end,
107                             RISCV_ALTERNATIVES_EARLY_BOOT);
108 #endif
109 }
110
111 #ifdef CONFIG_MODULES
112 void apply_module_alternatives(void *start, size_t length)
113 {
114         _apply_alternatives((struct alt_entry *)start,
115                             (struct alt_entry *)(start + length),
116                             RISCV_ALTERNATIVES_MODULE);
117 }
118 #endif