19a868f63SMichael Ellerman // SPDX-License-Identifier: GPL-2.0+ 29a868f63SMichael Ellerman // 39a868f63SMichael Ellerman // Security related flags and so on. 49a868f63SMichael Ellerman // 59a868f63SMichael Ellerman // Copyright 2018, Michael Ellerman, IBM Corporation. 69a868f63SMichael Ellerman 79a868f63SMichael Ellerman #include <linux/kernel.h> 88ad33041SMichael Ellerman #include <linux/device.h> 9ff348355SMichael Ellerman #include <linux/seq_buf.h> 108ad33041SMichael Ellerman 11a048a07dSNicholas Piggin #include <asm/debugfs.h> 129a868f63SMichael Ellerman #include <asm/security_features.h> 132eea7f06SMichal Suchanek #include <asm/setup.h> 149a868f63SMichael Ellerman 159a868f63SMichael Ellerman 16e7347a86SMauricio Faria de Oliveira unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; 178ad33041SMichael Ellerman 18815069caSMichal Suchanek bool barrier_nospec_enabled; 19*cf175dc3SDiana Craciun static bool no_nospec; 202eea7f06SMichal Suchanek 212eea7f06SMichal Suchanek static void enable_barrier_nospec(bool enable) 222eea7f06SMichal Suchanek { 232eea7f06SMichal Suchanek barrier_nospec_enabled = enable; 242eea7f06SMichal Suchanek do_barrier_nospec_fixups(enable); 252eea7f06SMichal Suchanek } 262eea7f06SMichal Suchanek 27cb3d6759SMichal Suchanek void setup_barrier_nospec(void) 28cb3d6759SMichal Suchanek { 29cb3d6759SMichal Suchanek bool enable; 30cb3d6759SMichal Suchanek 31cb3d6759SMichal Suchanek /* 32cb3d6759SMichal Suchanek * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well. 33cb3d6759SMichal Suchanek * But there's a good reason not to. The two flags we check below are 34cb3d6759SMichal Suchanek * both are enabled by default in the kernel, so if the hcall is not 35cb3d6759SMichal Suchanek * functional they will be enabled. 36cb3d6759SMichal Suchanek * On a system where the host firmware has been updated (so the ori 37cb3d6759SMichal Suchanek * functions as a barrier), but on which the hypervisor (KVM/Qemu) has 38cb3d6759SMichal Suchanek * not been updated, we would like to enable the barrier. Dropping the 39cb3d6759SMichal Suchanek * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is 40cb3d6759SMichal Suchanek * we potentially enable the barrier on systems where the host firmware 41cb3d6759SMichal Suchanek * is not updated, but that's harmless as it's a no-op. 42cb3d6759SMichal Suchanek */ 43cb3d6759SMichal Suchanek enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && 44cb3d6759SMichal Suchanek security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR); 45cb3d6759SMichal Suchanek 46*cf175dc3SDiana Craciun if (!no_nospec) 47cb3d6759SMichal Suchanek enable_barrier_nospec(enable); 48cb3d6759SMichal Suchanek } 49cb3d6759SMichal Suchanek 50*cf175dc3SDiana Craciun static int __init handle_nospectre_v1(char *p) 51*cf175dc3SDiana Craciun { 52*cf175dc3SDiana Craciun no_nospec = true; 53*cf175dc3SDiana Craciun 54*cf175dc3SDiana Craciun return 0; 55*cf175dc3SDiana Craciun } 56*cf175dc3SDiana Craciun early_param("nospectre_v1", handle_nospectre_v1); 57*cf175dc3SDiana Craciun 58cb3d6759SMichal Suchanek #ifdef CONFIG_DEBUG_FS 59cb3d6759SMichal Suchanek static int barrier_nospec_set(void *data, u64 val) 60cb3d6759SMichal Suchanek { 61cb3d6759SMichal Suchanek switch (val) { 62cb3d6759SMichal Suchanek case 0: 63cb3d6759SMichal Suchanek case 1: 64cb3d6759SMichal Suchanek break; 65cb3d6759SMichal Suchanek default: 66cb3d6759SMichal Suchanek return -EINVAL; 67cb3d6759SMichal Suchanek } 68cb3d6759SMichal Suchanek 69cb3d6759SMichal Suchanek if (!!val == !!barrier_nospec_enabled) 70cb3d6759SMichal Suchanek return 0; 71cb3d6759SMichal Suchanek 72cb3d6759SMichal Suchanek enable_barrier_nospec(!!val); 73cb3d6759SMichal Suchanek 74cb3d6759SMichal Suchanek return 0; 75cb3d6759SMichal Suchanek } 76cb3d6759SMichal Suchanek 77cb3d6759SMichal Suchanek static int barrier_nospec_get(void *data, u64 *val) 78cb3d6759SMichal Suchanek { 79cb3d6759SMichal Suchanek *val = barrier_nospec_enabled ? 1 : 0; 80cb3d6759SMichal Suchanek return 0; 81cb3d6759SMichal Suchanek } 82cb3d6759SMichal Suchanek 83cb3d6759SMichal Suchanek DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec, 84cb3d6759SMichal Suchanek barrier_nospec_get, barrier_nospec_set, "%llu\n"); 85cb3d6759SMichal Suchanek 86cb3d6759SMichal Suchanek static __init int barrier_nospec_debugfs_init(void) 87cb3d6759SMichal Suchanek { 88cb3d6759SMichal Suchanek debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL, 89cb3d6759SMichal Suchanek &fops_barrier_nospec); 90cb3d6759SMichal Suchanek return 0; 91cb3d6759SMichal Suchanek } 92cb3d6759SMichal Suchanek device_initcall(barrier_nospec_debugfs_init); 93cb3d6759SMichal Suchanek #endif /* CONFIG_DEBUG_FS */ 94cb3d6759SMichal Suchanek 958ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) 968ad33041SMichael Ellerman { 97ff348355SMichael Ellerman bool thread_priv; 98ff348355SMichael Ellerman 99ff348355SMichael Ellerman thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV); 100ff348355SMichael Ellerman 101ff348355SMichael Ellerman if (rfi_flush || thread_priv) { 102ff348355SMichael Ellerman struct seq_buf s; 103ff348355SMichael Ellerman seq_buf_init(&s, buf, PAGE_SIZE - 1); 104ff348355SMichael Ellerman 105ff348355SMichael Ellerman seq_buf_printf(&s, "Mitigation: "); 106ff348355SMichael Ellerman 1078ad33041SMichael Ellerman if (rfi_flush) 108ff348355SMichael Ellerman seq_buf_printf(&s, "RFI Flush"); 109ff348355SMichael Ellerman 110ff348355SMichael Ellerman if (rfi_flush && thread_priv) 111ff348355SMichael Ellerman seq_buf_printf(&s, ", "); 112ff348355SMichael Ellerman 113ff348355SMichael Ellerman if (thread_priv) 114ff348355SMichael Ellerman seq_buf_printf(&s, "L1D private per thread"); 115ff348355SMichael Ellerman 116ff348355SMichael Ellerman seq_buf_printf(&s, "\n"); 117ff348355SMichael Ellerman 118ff348355SMichael Ellerman return s.len; 119ff348355SMichael Ellerman } 120ff348355SMichael Ellerman 121ff348355SMichael Ellerman if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && 122ff348355SMichael Ellerman !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) 123ff348355SMichael Ellerman return sprintf(buf, "Not affected\n"); 1248ad33041SMichael Ellerman 1258ad33041SMichael Ellerman return sprintf(buf, "Vulnerable\n"); 1268ad33041SMichael Ellerman } 12756986016SMichael Ellerman 12856986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf) 12956986016SMichael Ellerman { 1306d44acaeSMichael Ellerman struct seq_buf s; 13156986016SMichael Ellerman 1326d44acaeSMichael Ellerman seq_buf_init(&s, buf, PAGE_SIZE - 1); 1336d44acaeSMichael Ellerman 1346d44acaeSMichael Ellerman if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) { 135a3775145SMichal Suchanek if (barrier_nospec_enabled) 1366d44acaeSMichael Ellerman seq_buf_printf(&s, "Mitigation: __user pointer sanitization"); 1376d44acaeSMichael Ellerman else 1386d44acaeSMichael Ellerman seq_buf_printf(&s, "Vulnerable"); 139a3775145SMichal Suchanek 1406d44acaeSMichael Ellerman if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31)) 1416d44acaeSMichael Ellerman seq_buf_printf(&s, ", ori31 speculation barrier enabled"); 1426d44acaeSMichael Ellerman 1436d44acaeSMichael Ellerman seq_buf_printf(&s, "\n"); 1446d44acaeSMichael Ellerman } else 1456d44acaeSMichael Ellerman seq_buf_printf(&s, "Not affected\n"); 1466d44acaeSMichael Ellerman 1476d44acaeSMichael Ellerman return s.len; 14856986016SMichael Ellerman } 149d6fbe1c5SMichael Ellerman 150d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf) 151d6fbe1c5SMichael Ellerman { 152d6fbe1c5SMichael Ellerman struct seq_buf s; 1536d44acaeSMichael Ellerman bool bcs, ccd; 154d6fbe1c5SMichael Ellerman 155d6fbe1c5SMichael Ellerman seq_buf_init(&s, buf, PAGE_SIZE - 1); 156d6fbe1c5SMichael Ellerman 157d6fbe1c5SMichael Ellerman bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED); 158d6fbe1c5SMichael Ellerman ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED); 159d6fbe1c5SMichael Ellerman 160d6fbe1c5SMichael Ellerman if (bcs || ccd) { 161d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Mitigation: "); 162d6fbe1c5SMichael Ellerman 163d6fbe1c5SMichael Ellerman if (bcs) 164d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Indirect branch serialisation (kernel only)"); 165d6fbe1c5SMichael Ellerman 166d6fbe1c5SMichael Ellerman if (bcs && ccd) 167d6fbe1c5SMichael Ellerman seq_buf_printf(&s, ", "); 168d6fbe1c5SMichael Ellerman 169d6fbe1c5SMichael Ellerman if (ccd) 170d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Indirect branch cache disabled"); 171d6fbe1c5SMichael Ellerman } else 172d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Vulnerable"); 173d6fbe1c5SMichael Ellerman 174d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "\n"); 175d6fbe1c5SMichael Ellerman 176d6fbe1c5SMichael Ellerman return s.len; 177d6fbe1c5SMichael Ellerman } 178a048a07dSNicholas Piggin 179a048a07dSNicholas Piggin /* 180a048a07dSNicholas Piggin * Store-forwarding barrier support. 181a048a07dSNicholas Piggin */ 182a048a07dSNicholas Piggin 183a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types; 184a048a07dSNicholas Piggin static bool no_stf_barrier; 185a048a07dSNicholas Piggin bool stf_barrier; 186a048a07dSNicholas Piggin 187a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p) 188a048a07dSNicholas Piggin { 189a048a07dSNicholas Piggin pr_info("stf-barrier: disabled on command line."); 190a048a07dSNicholas Piggin no_stf_barrier = true; 191a048a07dSNicholas Piggin return 0; 192a048a07dSNicholas Piggin } 193a048a07dSNicholas Piggin 194a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier); 195a048a07dSNicholas Piggin 196a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */ 197a048a07dSNicholas Piggin static int __init handle_ssbd(char *p) 198a048a07dSNicholas Piggin { 199a048a07dSNicholas Piggin if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) { 200a048a07dSNicholas Piggin /* Until firmware tells us, we have the barrier with auto */ 201a048a07dSNicholas Piggin return 0; 202a048a07dSNicholas Piggin } else if (strncmp(p, "off", 3) == 0) { 203a048a07dSNicholas Piggin handle_no_stf_barrier(NULL); 204a048a07dSNicholas Piggin return 0; 205a048a07dSNicholas Piggin } else 206a048a07dSNicholas Piggin return 1; 207a048a07dSNicholas Piggin 208a048a07dSNicholas Piggin return 0; 209a048a07dSNicholas Piggin } 210a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd); 211a048a07dSNicholas Piggin 212a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */ 213a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p) 214a048a07dSNicholas Piggin { 215a048a07dSNicholas Piggin handle_no_stf_barrier(NULL); 216a048a07dSNicholas Piggin return 0; 217a048a07dSNicholas Piggin } 218a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd); 219a048a07dSNicholas Piggin 220a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable) 221a048a07dSNicholas Piggin { 222a048a07dSNicholas Piggin if (enable) 223a048a07dSNicholas Piggin do_stf_barrier_fixups(stf_enabled_flush_types); 224a048a07dSNicholas Piggin else 225a048a07dSNicholas Piggin do_stf_barrier_fixups(STF_BARRIER_NONE); 226a048a07dSNicholas Piggin 227a048a07dSNicholas Piggin stf_barrier = enable; 228a048a07dSNicholas Piggin } 229a048a07dSNicholas Piggin 230a048a07dSNicholas Piggin void setup_stf_barrier(void) 231a048a07dSNicholas Piggin { 232a048a07dSNicholas Piggin enum stf_barrier_type type; 233a048a07dSNicholas Piggin bool enable, hv; 234a048a07dSNicholas Piggin 235a048a07dSNicholas Piggin hv = cpu_has_feature(CPU_FTR_HVMODE); 236a048a07dSNicholas Piggin 237a048a07dSNicholas Piggin /* Default to fallback in case fw-features are not available */ 238a048a07dSNicholas Piggin if (cpu_has_feature(CPU_FTR_ARCH_300)) 239a048a07dSNicholas Piggin type = STF_BARRIER_EIEIO; 240a048a07dSNicholas Piggin else if (cpu_has_feature(CPU_FTR_ARCH_207S)) 241a048a07dSNicholas Piggin type = STF_BARRIER_SYNC_ORI; 242a048a07dSNicholas Piggin else if (cpu_has_feature(CPU_FTR_ARCH_206)) 243a048a07dSNicholas Piggin type = STF_BARRIER_FALLBACK; 244a048a07dSNicholas Piggin else 245a048a07dSNicholas Piggin type = STF_BARRIER_NONE; 246a048a07dSNicholas Piggin 247a048a07dSNicholas Piggin enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && 248a048a07dSNicholas Piggin (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || 249a048a07dSNicholas Piggin (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv)); 250a048a07dSNicholas Piggin 251a048a07dSNicholas Piggin if (type == STF_BARRIER_FALLBACK) { 252a048a07dSNicholas Piggin pr_info("stf-barrier: fallback barrier available\n"); 253a048a07dSNicholas Piggin } else if (type == STF_BARRIER_SYNC_ORI) { 254a048a07dSNicholas Piggin pr_info("stf-barrier: hwsync barrier available\n"); 255a048a07dSNicholas Piggin } else if (type == STF_BARRIER_EIEIO) { 256a048a07dSNicholas Piggin pr_info("stf-barrier: eieio barrier available\n"); 257a048a07dSNicholas Piggin } 258a048a07dSNicholas Piggin 259a048a07dSNicholas Piggin stf_enabled_flush_types = type; 260a048a07dSNicholas Piggin 261a048a07dSNicholas Piggin if (!no_stf_barrier) 262a048a07dSNicholas Piggin stf_barrier_enable(enable); 263a048a07dSNicholas Piggin } 264a048a07dSNicholas Piggin 265a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf) 266a048a07dSNicholas Piggin { 267a048a07dSNicholas Piggin if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) { 268a048a07dSNicholas Piggin const char *type; 269a048a07dSNicholas Piggin switch (stf_enabled_flush_types) { 270a048a07dSNicholas Piggin case STF_BARRIER_EIEIO: 271a048a07dSNicholas Piggin type = "eieio"; 272a048a07dSNicholas Piggin break; 273a048a07dSNicholas Piggin case STF_BARRIER_SYNC_ORI: 274a048a07dSNicholas Piggin type = "hwsync"; 275a048a07dSNicholas Piggin break; 276a048a07dSNicholas Piggin case STF_BARRIER_FALLBACK: 277a048a07dSNicholas Piggin type = "fallback"; 278a048a07dSNicholas Piggin break; 279a048a07dSNicholas Piggin default: 280a048a07dSNicholas Piggin type = "unknown"; 281a048a07dSNicholas Piggin } 282a048a07dSNicholas Piggin return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); 283a048a07dSNicholas Piggin } 284a048a07dSNicholas Piggin 285a048a07dSNicholas Piggin if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && 286a048a07dSNicholas Piggin !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) 287a048a07dSNicholas Piggin return sprintf(buf, "Not affected\n"); 288a048a07dSNicholas Piggin 289a048a07dSNicholas Piggin return sprintf(buf, "Vulnerable\n"); 290a048a07dSNicholas Piggin } 291a048a07dSNicholas Piggin 292a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS 293a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val) 294a048a07dSNicholas Piggin { 295a048a07dSNicholas Piggin bool enable; 296a048a07dSNicholas Piggin 297a048a07dSNicholas Piggin if (val == 1) 298a048a07dSNicholas Piggin enable = true; 299a048a07dSNicholas Piggin else if (val == 0) 300a048a07dSNicholas Piggin enable = false; 301a048a07dSNicholas Piggin else 302a048a07dSNicholas Piggin return -EINVAL; 303a048a07dSNicholas Piggin 304a048a07dSNicholas Piggin /* Only do anything if we're changing state */ 305a048a07dSNicholas Piggin if (enable != stf_barrier) 306a048a07dSNicholas Piggin stf_barrier_enable(enable); 307a048a07dSNicholas Piggin 308a048a07dSNicholas Piggin return 0; 309a048a07dSNicholas Piggin } 310a048a07dSNicholas Piggin 311a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val) 312a048a07dSNicholas Piggin { 313a048a07dSNicholas Piggin *val = stf_barrier ? 1 : 0; 314a048a07dSNicholas Piggin return 0; 315a048a07dSNicholas Piggin } 316a048a07dSNicholas Piggin 317a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n"); 318a048a07dSNicholas Piggin 319a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void) 320a048a07dSNicholas Piggin { 321a048a07dSNicholas Piggin debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier); 322a048a07dSNicholas Piggin return 0; 323a048a07dSNicholas Piggin } 324a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init); 325a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */ 326