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 11*a048a07dSNicholas Piggin #include <asm/debugfs.h> 129a868f63SMichael Ellerman #include <asm/security_features.h> 139a868f63SMichael Ellerman 149a868f63SMichael Ellerman 15e7347a86SMauricio Faria de Oliveira unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; 168ad33041SMichael Ellerman 178ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) 188ad33041SMichael Ellerman { 19ff348355SMichael Ellerman bool thread_priv; 20ff348355SMichael Ellerman 21ff348355SMichael Ellerman thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV); 22ff348355SMichael Ellerman 23ff348355SMichael Ellerman if (rfi_flush || thread_priv) { 24ff348355SMichael Ellerman struct seq_buf s; 25ff348355SMichael Ellerman seq_buf_init(&s, buf, PAGE_SIZE - 1); 26ff348355SMichael Ellerman 27ff348355SMichael Ellerman seq_buf_printf(&s, "Mitigation: "); 28ff348355SMichael Ellerman 298ad33041SMichael Ellerman if (rfi_flush) 30ff348355SMichael Ellerman seq_buf_printf(&s, "RFI Flush"); 31ff348355SMichael Ellerman 32ff348355SMichael Ellerman if (rfi_flush && thread_priv) 33ff348355SMichael Ellerman seq_buf_printf(&s, ", "); 34ff348355SMichael Ellerman 35ff348355SMichael Ellerman if (thread_priv) 36ff348355SMichael Ellerman seq_buf_printf(&s, "L1D private per thread"); 37ff348355SMichael Ellerman 38ff348355SMichael Ellerman seq_buf_printf(&s, "\n"); 39ff348355SMichael Ellerman 40ff348355SMichael Ellerman return s.len; 41ff348355SMichael Ellerman } 42ff348355SMichael Ellerman 43ff348355SMichael Ellerman if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && 44ff348355SMichael Ellerman !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) 45ff348355SMichael Ellerman return sprintf(buf, "Not affected\n"); 468ad33041SMichael Ellerman 478ad33041SMichael Ellerman return sprintf(buf, "Vulnerable\n"); 488ad33041SMichael Ellerman } 4956986016SMichael Ellerman 5056986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf) 5156986016SMichael Ellerman { 5256986016SMichael Ellerman if (!security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) 5356986016SMichael Ellerman return sprintf(buf, "Not affected\n"); 5456986016SMichael Ellerman 5556986016SMichael Ellerman return sprintf(buf, "Vulnerable\n"); 5656986016SMichael Ellerman } 57d6fbe1c5SMichael Ellerman 58d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf) 59d6fbe1c5SMichael Ellerman { 60d6fbe1c5SMichael Ellerman bool bcs, ccd, ori; 61d6fbe1c5SMichael Ellerman struct seq_buf s; 62d6fbe1c5SMichael Ellerman 63d6fbe1c5SMichael Ellerman seq_buf_init(&s, buf, PAGE_SIZE - 1); 64d6fbe1c5SMichael Ellerman 65d6fbe1c5SMichael Ellerman bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED); 66d6fbe1c5SMichael Ellerman ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED); 67d6fbe1c5SMichael Ellerman ori = security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31); 68d6fbe1c5SMichael Ellerman 69d6fbe1c5SMichael Ellerman if (bcs || ccd) { 70d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Mitigation: "); 71d6fbe1c5SMichael Ellerman 72d6fbe1c5SMichael Ellerman if (bcs) 73d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Indirect branch serialisation (kernel only)"); 74d6fbe1c5SMichael Ellerman 75d6fbe1c5SMichael Ellerman if (bcs && ccd) 76d6fbe1c5SMichael Ellerman seq_buf_printf(&s, ", "); 77d6fbe1c5SMichael Ellerman 78d6fbe1c5SMichael Ellerman if (ccd) 79d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Indirect branch cache disabled"); 80d6fbe1c5SMichael Ellerman } else 81d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "Vulnerable"); 82d6fbe1c5SMichael Ellerman 83d6fbe1c5SMichael Ellerman if (ori) 84d6fbe1c5SMichael Ellerman seq_buf_printf(&s, ", ori31 speculation barrier enabled"); 85d6fbe1c5SMichael Ellerman 86d6fbe1c5SMichael Ellerman seq_buf_printf(&s, "\n"); 87d6fbe1c5SMichael Ellerman 88d6fbe1c5SMichael Ellerman return s.len; 89d6fbe1c5SMichael Ellerman } 90*a048a07dSNicholas Piggin 91*a048a07dSNicholas Piggin /* 92*a048a07dSNicholas Piggin * Store-forwarding barrier support. 93*a048a07dSNicholas Piggin */ 94*a048a07dSNicholas Piggin 95*a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types; 96*a048a07dSNicholas Piggin static bool no_stf_barrier; 97*a048a07dSNicholas Piggin bool stf_barrier; 98*a048a07dSNicholas Piggin 99*a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p) 100*a048a07dSNicholas Piggin { 101*a048a07dSNicholas Piggin pr_info("stf-barrier: disabled on command line."); 102*a048a07dSNicholas Piggin no_stf_barrier = true; 103*a048a07dSNicholas Piggin return 0; 104*a048a07dSNicholas Piggin } 105*a048a07dSNicholas Piggin 106*a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier); 107*a048a07dSNicholas Piggin 108*a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */ 109*a048a07dSNicholas Piggin static int __init handle_ssbd(char *p) 110*a048a07dSNicholas Piggin { 111*a048a07dSNicholas Piggin if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) { 112*a048a07dSNicholas Piggin /* Until firmware tells us, we have the barrier with auto */ 113*a048a07dSNicholas Piggin return 0; 114*a048a07dSNicholas Piggin } else if (strncmp(p, "off", 3) == 0) { 115*a048a07dSNicholas Piggin handle_no_stf_barrier(NULL); 116*a048a07dSNicholas Piggin return 0; 117*a048a07dSNicholas Piggin } else 118*a048a07dSNicholas Piggin return 1; 119*a048a07dSNicholas Piggin 120*a048a07dSNicholas Piggin return 0; 121*a048a07dSNicholas Piggin } 122*a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd); 123*a048a07dSNicholas Piggin 124*a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */ 125*a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p) 126*a048a07dSNicholas Piggin { 127*a048a07dSNicholas Piggin handle_no_stf_barrier(NULL); 128*a048a07dSNicholas Piggin return 0; 129*a048a07dSNicholas Piggin } 130*a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd); 131*a048a07dSNicholas Piggin 132*a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable) 133*a048a07dSNicholas Piggin { 134*a048a07dSNicholas Piggin if (enable) 135*a048a07dSNicholas Piggin do_stf_barrier_fixups(stf_enabled_flush_types); 136*a048a07dSNicholas Piggin else 137*a048a07dSNicholas Piggin do_stf_barrier_fixups(STF_BARRIER_NONE); 138*a048a07dSNicholas Piggin 139*a048a07dSNicholas Piggin stf_barrier = enable; 140*a048a07dSNicholas Piggin } 141*a048a07dSNicholas Piggin 142*a048a07dSNicholas Piggin void setup_stf_barrier(void) 143*a048a07dSNicholas Piggin { 144*a048a07dSNicholas Piggin enum stf_barrier_type type; 145*a048a07dSNicholas Piggin bool enable, hv; 146*a048a07dSNicholas Piggin 147*a048a07dSNicholas Piggin hv = cpu_has_feature(CPU_FTR_HVMODE); 148*a048a07dSNicholas Piggin 149*a048a07dSNicholas Piggin /* Default to fallback in case fw-features are not available */ 150*a048a07dSNicholas Piggin if (cpu_has_feature(CPU_FTR_ARCH_300)) 151*a048a07dSNicholas Piggin type = STF_BARRIER_EIEIO; 152*a048a07dSNicholas Piggin else if (cpu_has_feature(CPU_FTR_ARCH_207S)) 153*a048a07dSNicholas Piggin type = STF_BARRIER_SYNC_ORI; 154*a048a07dSNicholas Piggin else if (cpu_has_feature(CPU_FTR_ARCH_206)) 155*a048a07dSNicholas Piggin type = STF_BARRIER_FALLBACK; 156*a048a07dSNicholas Piggin else 157*a048a07dSNicholas Piggin type = STF_BARRIER_NONE; 158*a048a07dSNicholas Piggin 159*a048a07dSNicholas Piggin enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && 160*a048a07dSNicholas Piggin (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || 161*a048a07dSNicholas Piggin (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv)); 162*a048a07dSNicholas Piggin 163*a048a07dSNicholas Piggin if (type == STF_BARRIER_FALLBACK) { 164*a048a07dSNicholas Piggin pr_info("stf-barrier: fallback barrier available\n"); 165*a048a07dSNicholas Piggin } else if (type == STF_BARRIER_SYNC_ORI) { 166*a048a07dSNicholas Piggin pr_info("stf-barrier: hwsync barrier available\n"); 167*a048a07dSNicholas Piggin } else if (type == STF_BARRIER_EIEIO) { 168*a048a07dSNicholas Piggin pr_info("stf-barrier: eieio barrier available\n"); 169*a048a07dSNicholas Piggin } 170*a048a07dSNicholas Piggin 171*a048a07dSNicholas Piggin stf_enabled_flush_types = type; 172*a048a07dSNicholas Piggin 173*a048a07dSNicholas Piggin if (!no_stf_barrier) 174*a048a07dSNicholas Piggin stf_barrier_enable(enable); 175*a048a07dSNicholas Piggin } 176*a048a07dSNicholas Piggin 177*a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf) 178*a048a07dSNicholas Piggin { 179*a048a07dSNicholas Piggin if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) { 180*a048a07dSNicholas Piggin const char *type; 181*a048a07dSNicholas Piggin switch (stf_enabled_flush_types) { 182*a048a07dSNicholas Piggin case STF_BARRIER_EIEIO: 183*a048a07dSNicholas Piggin type = "eieio"; 184*a048a07dSNicholas Piggin break; 185*a048a07dSNicholas Piggin case STF_BARRIER_SYNC_ORI: 186*a048a07dSNicholas Piggin type = "hwsync"; 187*a048a07dSNicholas Piggin break; 188*a048a07dSNicholas Piggin case STF_BARRIER_FALLBACK: 189*a048a07dSNicholas Piggin type = "fallback"; 190*a048a07dSNicholas Piggin break; 191*a048a07dSNicholas Piggin default: 192*a048a07dSNicholas Piggin type = "unknown"; 193*a048a07dSNicholas Piggin } 194*a048a07dSNicholas Piggin return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); 195*a048a07dSNicholas Piggin } 196*a048a07dSNicholas Piggin 197*a048a07dSNicholas Piggin if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && 198*a048a07dSNicholas Piggin !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) 199*a048a07dSNicholas Piggin return sprintf(buf, "Not affected\n"); 200*a048a07dSNicholas Piggin 201*a048a07dSNicholas Piggin return sprintf(buf, "Vulnerable\n"); 202*a048a07dSNicholas Piggin } 203*a048a07dSNicholas Piggin 204*a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS 205*a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val) 206*a048a07dSNicholas Piggin { 207*a048a07dSNicholas Piggin bool enable; 208*a048a07dSNicholas Piggin 209*a048a07dSNicholas Piggin if (val == 1) 210*a048a07dSNicholas Piggin enable = true; 211*a048a07dSNicholas Piggin else if (val == 0) 212*a048a07dSNicholas Piggin enable = false; 213*a048a07dSNicholas Piggin else 214*a048a07dSNicholas Piggin return -EINVAL; 215*a048a07dSNicholas Piggin 216*a048a07dSNicholas Piggin /* Only do anything if we're changing state */ 217*a048a07dSNicholas Piggin if (enable != stf_barrier) 218*a048a07dSNicholas Piggin stf_barrier_enable(enable); 219*a048a07dSNicholas Piggin 220*a048a07dSNicholas Piggin return 0; 221*a048a07dSNicholas Piggin } 222*a048a07dSNicholas Piggin 223*a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val) 224*a048a07dSNicholas Piggin { 225*a048a07dSNicholas Piggin *val = stf_barrier ? 1 : 0; 226*a048a07dSNicholas Piggin return 0; 227*a048a07dSNicholas Piggin } 228*a048a07dSNicholas Piggin 229*a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n"); 230*a048a07dSNicholas Piggin 231*a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void) 232*a048a07dSNicholas Piggin { 233*a048a07dSNicholas Piggin debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier); 234*a048a07dSNicholas Piggin return 0; 235*a048a07dSNicholas Piggin } 236*a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init); 237*a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */ 238