arm64: cpufeature: Allow early filtering of feature override
[linux-2.6-microblaze.git] / arch / arm64 / kernel / idreg-override.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Early cpufeature override framework
4  *
5  * Copyright (C) 2020 Google LLC
6  * Author: Marc Zyngier <maz@kernel.org>
7  */
8
9 #include <linux/ctype.h>
10 #include <linux/kernel.h>
11 #include <linux/libfdt.h>
12
13 #include <asm/cacheflush.h>
14 #include <asm/cpufeature.h>
15 #include <asm/setup.h>
16
17 #define FTR_DESC_NAME_LEN       20
18 #define FTR_DESC_FIELD_LEN      10
19 #define FTR_ALIAS_NAME_LEN      30
20 #define FTR_ALIAS_OPTION_LEN    80
21
22 struct ftr_set_desc {
23         char                            name[FTR_DESC_NAME_LEN];
24         struct arm64_ftr_override       *override;
25         struct {
26                 char                    name[FTR_DESC_FIELD_LEN];
27                 u8                      shift;
28                 bool                    (*filter)(u64 val);
29         }                               fields[];
30 };
31
32 static const struct ftr_set_desc mmfr1 __initconst = {
33         .name           = "id_aa64mmfr1",
34         .override       = &id_aa64mmfr1_override,
35         .fields         = {
36                 { "vh", ID_AA64MMFR1_VHE_SHIFT },
37                 {}
38         },
39 };
40
41 static const struct ftr_set_desc pfr1 __initconst = {
42         .name           = "id_aa64pfr1",
43         .override       = &id_aa64pfr1_override,
44         .fields         = {
45                 { "bt", ID_AA64PFR1_BT_SHIFT },
46                 {}
47         },
48 };
49
50 static const struct ftr_set_desc isar1 __initconst = {
51         .name           = "id_aa64isar1",
52         .override       = &id_aa64isar1_override,
53         .fields         = {
54                 { "gpi", ID_AA64ISAR1_GPI_SHIFT },
55                 { "gpa", ID_AA64ISAR1_GPA_SHIFT },
56                 { "api", ID_AA64ISAR1_API_SHIFT },
57                 { "apa", ID_AA64ISAR1_APA_SHIFT },
58                 {}
59         },
60 };
61
62 extern struct arm64_ftr_override kaslr_feature_override;
63
64 static const struct ftr_set_desc kaslr __initconst = {
65         .name           = "kaslr",
66 #ifdef CONFIG_RANDOMIZE_BASE
67         .override       = &kaslr_feature_override,
68 #endif
69         .fields         = {
70                 { "disabled", 0 },
71                 {}
72         },
73 };
74
75 static const struct ftr_set_desc * const regs[] __initconst = {
76         &mmfr1,
77         &pfr1,
78         &isar1,
79         &kaslr,
80 };
81
82 static const struct {
83         char    alias[FTR_ALIAS_NAME_LEN];
84         char    feature[FTR_ALIAS_OPTION_LEN];
85 } aliases[] __initconst = {
86         { "kvm-arm.mode=nvhe",          "id_aa64mmfr1.vh=0" },
87         { "kvm-arm.mode=protected",     "id_aa64mmfr1.vh=0" },
88         { "arm64.nobti",                "id_aa64pfr1.bt=0" },
89         { "arm64.nopauth",
90           "id_aa64isar1.gpi=0 id_aa64isar1.gpa=0 "
91           "id_aa64isar1.api=0 id_aa64isar1.apa=0"          },
92         { "nokaslr",                    "kaslr.disabled=1" },
93 };
94
95 static int __init find_field(const char *cmdline,
96                              const struct ftr_set_desc *reg, int f, u64 *v)
97 {
98         char opt[FTR_DESC_NAME_LEN + FTR_DESC_FIELD_LEN + 2];
99         int len;
100
101         len = snprintf(opt, ARRAY_SIZE(opt), "%s.%s=",
102                        reg->name, reg->fields[f].name);
103
104         if (!parameqn(cmdline, opt, len))
105                 return -1;
106
107         return kstrtou64(cmdline + len, 0, v);
108 }
109
110 static void __init match_options(const char *cmdline)
111 {
112         int i;
113
114         for (i = 0; i < ARRAY_SIZE(regs); i++) {
115                 int f;
116
117                 if (!regs[i]->override)
118                         continue;
119
120                 for (f = 0; strlen(regs[i]->fields[f].name); f++) {
121                         u64 shift = regs[i]->fields[f].shift;
122                         u64 mask = 0xfUL << shift;
123                         u64 v;
124
125                         if (find_field(cmdline, regs[i], f, &v))
126                                 continue;
127
128                         /*
129                          * If an override gets filtered out, advertise
130                          * it by setting the value to 0xf, but
131                          * clearing the mask... Yes, this is fragile.
132                          */
133                         if (regs[i]->fields[f].filter &&
134                             !regs[i]->fields[f].filter(v)) {
135                                 regs[i]->override->val  |= mask;
136                                 regs[i]->override->mask &= ~mask;
137                                 continue;
138                         }
139
140                         regs[i]->override->val  &= ~mask;
141                         regs[i]->override->val  |= (v << shift) & mask;
142                         regs[i]->override->mask |= mask;
143
144                         return;
145                 }
146         }
147 }
148
149 static __init void __parse_cmdline(const char *cmdline, bool parse_aliases)
150 {
151         do {
152                 char buf[256];
153                 size_t len;
154                 int i;
155
156                 cmdline = skip_spaces(cmdline);
157
158                 for (len = 0; cmdline[len] && !isspace(cmdline[len]); len++);
159                 if (!len)
160                         return;
161
162                 len = min(len, ARRAY_SIZE(buf) - 1);
163                 strncpy(buf, cmdline, len);
164                 buf[len] = 0;
165
166                 if (strcmp(buf, "--") == 0)
167                         return;
168
169                 cmdline += len;
170
171                 match_options(buf);
172
173                 for (i = 0; parse_aliases && i < ARRAY_SIZE(aliases); i++)
174                         if (parameq(buf, aliases[i].alias))
175                                 __parse_cmdline(aliases[i].feature, false);
176         } while (1);
177 }
178
179 static __init const u8 *get_bootargs_cmdline(void)
180 {
181         const u8 *prop;
182         void *fdt;
183         int node;
184
185         fdt = get_early_fdt_ptr();
186         if (!fdt)
187                 return NULL;
188
189         node = fdt_path_offset(fdt, "/chosen");
190         if (node < 0)
191                 return NULL;
192
193         prop = fdt_getprop(fdt, node, "bootargs", NULL);
194         if (!prop)
195                 return NULL;
196
197         return strlen(prop) ? prop : NULL;
198 }
199
200 static __init void parse_cmdline(void)
201 {
202         const u8 *prop = get_bootargs_cmdline();
203
204         if (IS_ENABLED(CONFIG_CMDLINE_FORCE) || !prop)
205                 __parse_cmdline(CONFIG_CMDLINE, true);
206
207         if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && prop)
208                 __parse_cmdline(prop, true);
209 }
210
211 /* Keep checkers quiet */
212 void init_feature_override(void);
213
214 asmlinkage void __init init_feature_override(void)
215 {
216         int i;
217
218         for (i = 0; i < ARRAY_SIZE(regs); i++) {
219                 if (regs[i]->override) {
220                         regs[i]->override->val  = 0;
221                         regs[i]->override->mask = 0;
222                 }
223         }
224
225         parse_cmdline();
226
227         for (i = 0; i < ARRAY_SIZE(regs); i++) {
228                 if (regs[i]->override)
229                         __flush_dcache_area(regs[i]->override,
230                                             sizeof(*regs[i]->override));
231         }
232 }