powerpc/64s: Add support for ori barrier_nospec patching
authorMichal Suchanek <msuchanek@suse.de>
Fri, 29 Mar 2019 11:25:49 +0000 (22:25 +1100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 3 Apr 2019 04:25:11 +0000 (06:25 +0200)
commit 2eea7f067f495e33b8b116b35b5988ab2b8aec55 upstream.

Based on the RFI patching. This is required to be able to disable the
speculation barrier.

Only one barrier type is supported and it does nothing when the
firmware does not enable it. Also re-patching modules is not supported
So the only meaningful thing that can be done is patching out the
speculation barrier at boot when the user says it is not wanted.

Signed-off-by: Michal Suchanek <msuchanek@suse.de>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/powerpc/include/asm/barrier.h
arch/powerpc/include/asm/feature-fixups.h
arch/powerpc/include/asm/setup.h
arch/powerpc/kernel/security.c
arch/powerpc/kernel/vmlinux.lds.S
arch/powerpc/lib/feature-fixups.c

index e582d2c880922504c6d93b3df72f655953437aae..f67b3f6e36bebf85b69f72a913b9800daebbe3c4 100644 (file)
@@ -81,7 +81,7 @@ do {                                                                  \
  * Prevent execution of subsequent instructions until preceding branches have
  * been fully resolved and are no longer executing speculatively.
  */
-#define barrier_nospec_asm ori 31,31,0
+#define barrier_nospec_asm NOSPEC_BARRIER_FIXUP_SECTION; nop
 
 // This also acts as a compiler barrier due to the memory clobber.
 #define barrier_nospec() asm (stringify_in_c(barrier_nospec_asm) ::: "memory")
index a9b64df34e2a365a6916c89786d3398f4311413b..fcfd05672b1b82b3ac6f94b23e6b14b3ec431918 100644 (file)
@@ -211,6 +211,14 @@ label##3:                                          \
        FTR_ENTRY_OFFSET 951b-952b;                     \
        .popsection;
 
+#define NOSPEC_BARRIER_FIXUP_SECTION                   \
+953:                                                   \
+       .pushsection __barrier_nospec_fixup,"a";        \
+       .align 2;                                       \
+954:                                                   \
+       FTR_ENTRY_OFFSET 953b-954b;                     \
+       .popsection;
+
 
 #ifndef __ASSEMBLY__
 #include <linux/types.h>
@@ -219,6 +227,7 @@ extern long stf_barrier_fallback;
 extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup;
 extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup;
 extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup;
+extern long __start___barrier_nospec_fixup, __stop___barrier_nospec_fixup;
 
 void apply_feature_fixups(void);
 void setup_feature_keys(void);
index a5e919e34c42198e9711c5ee069398ec1ca2cc6d..88018e4423863eb3bc152005d567743a1b997b77 100644 (file)
@@ -52,6 +52,7 @@ enum l1d_flush_type {
 
 void setup_rfi_flush(enum l1d_flush_type, bool enable);
 void do_rfi_flush_fixups(enum l1d_flush_type types);
+void do_barrier_nospec_fixups(bool enable);
 
 #endif /* !__ASSEMBLY__ */
 
index b98a722da9151bd41351de9448b21bfbc417cd6b..4f18a447ab70e3177df460acb3a779d71ca97106 100644 (file)
 
 #include <asm/debugfs.h>
 #include <asm/security_features.h>
+#include <asm/setup.h>
 
 
 unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
 
+static bool barrier_nospec_enabled;
+
+static void enable_barrier_nospec(bool enable)
+{
+       barrier_nospec_enabled = enable;
+       do_barrier_nospec_fixups(enable);
+}
+
 ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
 {
        bool thread_priv;
index c89ffb88fa3ba26ead303b8fffc594bb8ceecb08..43960d69bec9ef91c334a7431168332b009b61eb 100644 (file)
@@ -153,6 +153,13 @@ SECTIONS
                *(__rfi_flush_fixup)
                __stop___rfi_flush_fixup = .;
        }
+
+       . = ALIGN(8);
+       __spec_barrier_fixup : AT(ADDR(__spec_barrier_fixup) - LOAD_OFFSET) {
+               __start___barrier_nospec_fixup = .;
+               *(__barrier_nospec_fixup)
+               __stop___barrier_nospec_fixup = .;
+       }
 #endif
 
        EXCEPTION_TABLE(0)
index e1bcdc32a851cf6439e9cba8aa47d7cb2bf31475..65b4e8276bdd043de97a1e88ff2a74841b5cb806 100644 (file)
@@ -277,6 +277,33 @@ void do_rfi_flush_fixups(enum l1d_flush_type types)
                (types &  L1D_FLUSH_MTTRIG)     ? "mttrig type"
                                                : "unknown");
 }
+
+void do_barrier_nospec_fixups(bool enable)
+{
+       unsigned int instr, *dest;
+       long *start, *end;
+       int i;
+
+       start = PTRRELOC(&__start___barrier_nospec_fixup),
+       end = PTRRELOC(&__stop___barrier_nospec_fixup);
+
+       instr = 0x60000000; /* nop */
+
+       if (enable) {
+               pr_info("barrier-nospec: using ORI speculation barrier\n");
+               instr = 0x63ff0000; /* ori 31,31,0 speculation barrier */
+       }
+
+       for (i = 0; start < end; start++, i++) {
+               dest = (void *)start + *start;
+
+               pr_devel("patching dest %lx\n", (unsigned long)dest);
+               patch_instruction(dest, instr);
+       }
+
+       printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i);
+}
+
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
 void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)