]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - arch/powerpc/kernel/security.c
powerpc/64s: Add support for software count cache flush
[thirdparty/kernel/stable.git] / arch / powerpc / kernel / security.c
index 2f30fc8ed0a852fe87d02c5fb7bfd4e020e80334..fd4703b6ddc0f9f903a0958ee80f610c2d84d025 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/device.h>
 #include <linux/seq_buf.h>
 
+#include <asm/asm-prototypes.h>
+#include <asm/code-patching.h>
 #include <asm/debug.h>
 #include <asm/security_features.h>
 #include <asm/setup.h>
 
 unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
 
+enum count_cache_flush_type {
+       COUNT_CACHE_FLUSH_NONE  = 0x1,
+       COUNT_CACHE_FLUSH_SW    = 0x2,
+       COUNT_CACHE_FLUSH_HW    = 0x4,
+};
+static enum count_cache_flush_type count_cache_flush_type;
+
 bool barrier_nospec_enabled;
 static bool no_nospec;
 
@@ -160,17 +169,29 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c
        bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
        ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
 
-       if (bcs || ccd) {
+       if (bcs || ccd || count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) {
+               bool comma = false;
                seq_buf_printf(&s, "Mitigation: ");
 
-               if (bcs)
+               if (bcs) {
                        seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
+                       comma = true;
+               }
+
+               if (ccd) {
+                       if (comma)
+                               seq_buf_printf(&s, ", ");
+                       seq_buf_printf(&s, "Indirect branch cache disabled");
+                       comma = true;
+               }
 
-               if (bcs && ccd)
+               if (comma)
                        seq_buf_printf(&s, ", ");
 
-               if (ccd)
-                       seq_buf_printf(&s, "Indirect branch cache disabled");
+               seq_buf_printf(&s, "Software count cache flush");
+
+               if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW)
+                       seq_buf_printf(&s, "(hardware accelerated)");
        } else
                seq_buf_printf(&s, "Vulnerable");
 
@@ -327,4 +348,71 @@ static __init int stf_barrier_debugfs_init(void)
 }
 device_initcall(stf_barrier_debugfs_init);
 #endif /* CONFIG_DEBUG_FS */
+
+static void toggle_count_cache_flush(bool enable)
+{
+       if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
+               patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP);
+               count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
+               pr_info("count-cache-flush: software flush disabled.\n");
+               return;
+       }
+
+       patch_branch_site(&patch__call_flush_count_cache,
+                         (u64)&flush_count_cache, BRANCH_SET_LINK);
+
+       if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) {
+               count_cache_flush_type = COUNT_CACHE_FLUSH_SW;
+               pr_info("count-cache-flush: full software flush sequence enabled.\n");
+               return;
+       }
+
+       patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR);
+       count_cache_flush_type = COUNT_CACHE_FLUSH_HW;
+       pr_info("count-cache-flush: hardware assisted flush sequence enabled\n");
+}
+
+void setup_count_cache_flush(void)
+{
+       toggle_count_cache_flush(true);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int count_cache_flush_set(void *data, u64 val)
+{
+       bool enable;
+
+       if (val == 1)
+               enable = true;
+       else if (val == 0)
+               enable = false;
+       else
+               return -EINVAL;
+
+       toggle_count_cache_flush(enable);
+
+       return 0;
+}
+
+static int count_cache_flush_get(void *data, u64 *val)
+{
+       if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE)
+               *val = 0;
+       else
+               *val = 1;
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get,
+                       count_cache_flush_set, "%llu\n");
+
+static __init int count_cache_flush_debugfs_init(void)
+{
+       debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root,
+                           NULL, &fops_count_cache_flush);
+       return 0;
+}
+device_initcall(count_cache_flush_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
 #endif /* CONFIG_PPC_BOOK3S_64 */