idreg-override.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Early cpufeature override framework
  4. *
  5. * Copyright (C) 2020 Google LLC
  6. * Author: Marc Zyngier <[email protected]>
  7. */
  8. #include <linux/ctype.h>
  9. #include <linux/kernel.h>
  10. #include <linux/libfdt.h>
  11. #include <asm/cacheflush.h>
  12. #include <asm/cpufeature.h>
  13. #include <asm/setup.h>
  14. #define FTR_DESC_NAME_LEN 20
  15. #define FTR_DESC_FIELD_LEN 10
  16. #define FTR_ALIAS_NAME_LEN 30
  17. #define FTR_ALIAS_OPTION_LEN 116
  18. static u64 __boot_status __initdata;
  19. struct ftr_set_desc {
  20. char name[FTR_DESC_NAME_LEN];
  21. struct arm64_ftr_override *override;
  22. struct {
  23. char name[FTR_DESC_FIELD_LEN];
  24. u8 shift;
  25. u8 width;
  26. bool (*filter)(u64 val);
  27. } fields[];
  28. };
  29. #define FIELD(n, s, f) { .name = n, .shift = s, .width = 4, .filter = f }
  30. static bool __init mmfr1_vh_filter(u64 val)
  31. {
  32. /*
  33. * If we ever reach this point while running VHE, we're
  34. * guaranteed to be on one of these funky, VHE-stuck CPUs. If
  35. * the user was trying to force nVHE on us, proceed with
  36. * attitude adjustment.
  37. */
  38. return !(__boot_status == (BOOT_CPU_FLAG_E2H | BOOT_CPU_MODE_EL2) &&
  39. val == 0);
  40. }
  41. static const struct ftr_set_desc mmfr1 __initconst = {
  42. .name = "id_aa64mmfr1",
  43. .override = &id_aa64mmfr1_override,
  44. .fields = {
  45. FIELD("vh", ID_AA64MMFR1_EL1_VH_SHIFT, mmfr1_vh_filter),
  46. {}
  47. },
  48. };
  49. static bool __init pfr0_sve_filter(u64 val)
  50. {
  51. /*
  52. * Disabling SVE also means disabling all the features that
  53. * are associated with it. The easiest way to do it is just to
  54. * override id_aa64zfr0_el1 to be 0.
  55. */
  56. if (!val) {
  57. id_aa64zfr0_override.val = 0;
  58. id_aa64zfr0_override.mask = GENMASK(63, 0);
  59. }
  60. return true;
  61. }
  62. static const struct ftr_set_desc pfr0 __initconst = {
  63. .name = "id_aa64pfr0",
  64. .override = &id_aa64pfr0_override,
  65. .fields = {
  66. FIELD("sve", ID_AA64PFR0_EL1_SVE_SHIFT, pfr0_sve_filter),
  67. {}
  68. },
  69. };
  70. static bool __init pfr1_sme_filter(u64 val)
  71. {
  72. /*
  73. * Similarly to SVE, disabling SME also means disabling all
  74. * the features that are associated with it. Just set
  75. * id_aa64smfr0_el1 to 0 and don't look back.
  76. */
  77. if (!val) {
  78. id_aa64smfr0_override.val = 0;
  79. id_aa64smfr0_override.mask = GENMASK(63, 0);
  80. }
  81. return true;
  82. }
  83. static const struct ftr_set_desc pfr1 __initconst = {
  84. .name = "id_aa64pfr1",
  85. .override = &id_aa64pfr1_override,
  86. .fields = {
  87. FIELD("bt", ID_AA64PFR1_EL1_BT_SHIFT, NULL ),
  88. FIELD("mte", ID_AA64PFR1_EL1_MTE_SHIFT, NULL),
  89. FIELD("sme", ID_AA64PFR1_EL1_SME_SHIFT, pfr1_sme_filter),
  90. {}
  91. },
  92. };
  93. static const struct ftr_set_desc isar1 __initconst = {
  94. .name = "id_aa64isar1",
  95. .override = &id_aa64isar1_override,
  96. .fields = {
  97. FIELD("gpi", ID_AA64ISAR1_EL1_GPI_SHIFT, NULL),
  98. FIELD("gpa", ID_AA64ISAR1_EL1_GPA_SHIFT, NULL),
  99. FIELD("api", ID_AA64ISAR1_EL1_API_SHIFT, NULL),
  100. FIELD("apa", ID_AA64ISAR1_EL1_APA_SHIFT, NULL),
  101. {}
  102. },
  103. };
  104. static const struct ftr_set_desc isar2 __initconst = {
  105. .name = "id_aa64isar2",
  106. .override = &id_aa64isar2_override,
  107. .fields = {
  108. FIELD("gpa3", ID_AA64ISAR2_EL1_GPA3_SHIFT, NULL),
  109. FIELD("apa3", ID_AA64ISAR2_EL1_APA3_SHIFT, NULL),
  110. {}
  111. },
  112. };
  113. static const struct ftr_set_desc smfr0 __initconst = {
  114. .name = "id_aa64smfr0",
  115. .override = &id_aa64smfr0_override,
  116. .fields = {
  117. /* FA64 is a one bit field... :-/ */
  118. { "fa64", ID_AA64SMFR0_EL1_FA64_SHIFT, 1, },
  119. {}
  120. },
  121. };
  122. extern struct arm64_ftr_override kaslr_feature_override;
  123. static const struct ftr_set_desc kaslr __initconst = {
  124. .name = "kaslr",
  125. #ifdef CONFIG_RANDOMIZE_BASE
  126. .override = &kaslr_feature_override,
  127. #endif
  128. .fields = {
  129. FIELD("disabled", 0, NULL),
  130. {}
  131. },
  132. };
  133. static const struct ftr_set_desc * const regs[] __initconst = {
  134. &mmfr1,
  135. &pfr0,
  136. &pfr1,
  137. &isar1,
  138. &isar2,
  139. &smfr0,
  140. &kaslr,
  141. };
  142. static const struct {
  143. char alias[FTR_ALIAS_NAME_LEN];
  144. char feature[FTR_ALIAS_OPTION_LEN];
  145. } aliases[] __initconst = {
  146. { "kvm-arm.mode=nvhe", "id_aa64mmfr1.vh=0" },
  147. { "kvm-arm.mode=protected", "id_aa64mmfr1.vh=0" },
  148. { "arm64.nosve", "id_aa64pfr0.sve=0 id_aa64pfr1.sme=0" },
  149. { "arm64.nosme", "id_aa64pfr1.sme=0" },
  150. { "arm64.nobti", "id_aa64pfr1.bt=0" },
  151. { "arm64.nopauth",
  152. "id_aa64isar1.gpi=0 id_aa64isar1.gpa=0 "
  153. "id_aa64isar1.api=0 id_aa64isar1.apa=0 "
  154. "id_aa64isar2.gpa3=0 id_aa64isar2.apa3=0" },
  155. { "arm64.nomte", "id_aa64pfr1.mte=0" },
  156. { "nokaslr", "kaslr.disabled=1" },
  157. };
  158. static int __init find_field(const char *cmdline,
  159. const struct ftr_set_desc *reg, int f, u64 *v)
  160. {
  161. char opt[FTR_DESC_NAME_LEN + FTR_DESC_FIELD_LEN + 2];
  162. int len;
  163. len = snprintf(opt, ARRAY_SIZE(opt), "%s.%s=",
  164. reg->name, reg->fields[f].name);
  165. if (!parameqn(cmdline, opt, len))
  166. return -1;
  167. return kstrtou64(cmdline + len, 0, v);
  168. }
  169. static void __init match_options(const char *cmdline)
  170. {
  171. int i;
  172. for (i = 0; i < ARRAY_SIZE(regs); i++) {
  173. int f;
  174. if (!regs[i]->override)
  175. continue;
  176. for (f = 0; strlen(regs[i]->fields[f].name); f++) {
  177. u64 shift = regs[i]->fields[f].shift;
  178. u64 width = regs[i]->fields[f].width ?: 4;
  179. u64 mask = GENMASK_ULL(shift + width - 1, shift);
  180. u64 v;
  181. if (find_field(cmdline, regs[i], f, &v))
  182. continue;
  183. /*
  184. * If an override gets filtered out, advertise
  185. * it by setting the value to the all-ones while
  186. * clearing the mask... Yes, this is fragile.
  187. */
  188. if (regs[i]->fields[f].filter &&
  189. !regs[i]->fields[f].filter(v)) {
  190. regs[i]->override->val |= mask;
  191. regs[i]->override->mask &= ~mask;
  192. continue;
  193. }
  194. regs[i]->override->val &= ~mask;
  195. regs[i]->override->val |= (v << shift) & mask;
  196. regs[i]->override->mask |= mask;
  197. return;
  198. }
  199. }
  200. }
  201. static __init void __parse_cmdline(const char *cmdline, bool parse_aliases)
  202. {
  203. do {
  204. char buf[256];
  205. size_t len;
  206. int i;
  207. cmdline = skip_spaces(cmdline);
  208. for (len = 0; cmdline[len] && !isspace(cmdline[len]); len++);
  209. if (!len)
  210. return;
  211. len = min(len, ARRAY_SIZE(buf) - 1);
  212. strncpy(buf, cmdline, len);
  213. buf[len] = 0;
  214. if (strcmp(buf, "--") == 0)
  215. return;
  216. cmdline += len;
  217. match_options(buf);
  218. for (i = 0; parse_aliases && i < ARRAY_SIZE(aliases); i++)
  219. if (parameq(buf, aliases[i].alias))
  220. __parse_cmdline(aliases[i].feature, false);
  221. } while (1);
  222. }
  223. static __init const u8 *get_bootargs_cmdline(void)
  224. {
  225. const u8 *prop;
  226. void *fdt;
  227. int node;
  228. fdt = get_early_fdt_ptr();
  229. if (!fdt)
  230. return NULL;
  231. node = fdt_path_offset(fdt, "/chosen");
  232. if (node < 0)
  233. return NULL;
  234. prop = fdt_getprop(fdt, node, "bootargs", NULL);
  235. if (!prop)
  236. return NULL;
  237. return strlen(prop) ? prop : NULL;
  238. }
  239. static __init void parse_cmdline(void)
  240. {
  241. const u8 *prop = get_bootargs_cmdline();
  242. if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
  243. IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
  244. !prop) {
  245. __parse_cmdline(CONFIG_CMDLINE, true);
  246. }
  247. if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && prop)
  248. __parse_cmdline(prop, true);
  249. }
  250. /* Keep checkers quiet */
  251. void init_feature_override(u64 boot_status);
  252. asmlinkage void __init init_feature_override(u64 boot_status)
  253. {
  254. int i;
  255. for (i = 0; i < ARRAY_SIZE(regs); i++) {
  256. if (regs[i]->override) {
  257. regs[i]->override->val = 0;
  258. regs[i]->override->mask = 0;
  259. }
  260. }
  261. __boot_status = boot_status;
  262. parse_cmdline();
  263. for (i = 0; i < ARRAY_SIZE(regs); i++) {
  264. if (regs[i]->override)
  265. dcache_clean_inval_poc((unsigned long)regs[i]->override,
  266. (unsigned long)regs[i]->override +
  267. sizeof(*regs[i]->override));
  268. }
  269. }