diff mbox series

[2/6] powerpc/64s: Add support for ori barrier_nospec patching

Message ID 20180424041559.32410-2-mpe@ellerman.id.au (mailing list archive)
State Accepted
Commit 2eea7f067f495e33b8b116b35b5988ab2b8aec55
Headers show
Series [1/6] powerpc/64s: Add barrier_nospec | expand

Commit Message

Michael Ellerman April 24, 2018, 4:15 a.m. UTC
From: Michal Suchanek <msuchanek@suse.de>

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>
---
 arch/powerpc/include/asm/barrier.h        |  2 +-
 arch/powerpc/include/asm/feature-fixups.h |  9 +++++++++
 arch/powerpc/include/asm/setup.h          |  1 +
 arch/powerpc/kernel/security.c            |  9 +++++++++
 arch/powerpc/kernel/vmlinux.lds.S         |  7 +++++++
 arch/powerpc/lib/feature-fixups.c         | 27 +++++++++++++++++++++++++++
 6 files changed, 54 insertions(+), 1 deletion(-)

Comments

Michal Suchánek April 26, 2018, 4:10 p.m. UTC | #1
Hello,

On Tue, 24 Apr 2018 14:15:55 +1000
Michael Ellerman <mpe@ellerman.id.au> wrote:

> From: Michal Suchanek <msuchanek@suse.de>
> 
> Based on the RFI patching. This is required to be able to disable the
> speculation barrier.

why do you not patch the nospec barrier which is included as part of
the RFI flush code?

I think when debugging the code it would make more sense if RFI is
patched by RFI patcher and nospec by nospec patcher.

A separate question is if the RFI flush would break without the nospec
barrier.

Thanks

Michal


> 
> 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>
> ---
>  arch/powerpc/include/asm/barrier.h        |  2 +-
>  arch/powerpc/include/asm/feature-fixups.h |  9 +++++++++
>  arch/powerpc/include/asm/setup.h          |  1 +
>  arch/powerpc/kernel/security.c            |  9 +++++++++
>  arch/powerpc/kernel/vmlinux.lds.S         |  7 +++++++
>  arch/powerpc/lib/feature-fixups.c         | 27
> +++++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 1
> deletion(-)
> 
> diff --git a/arch/powerpc/include/asm/barrier.h
> b/arch/powerpc/include/asm/barrier.h index e582d2c88092..f67b3f6e36be
> 100644 --- a/arch/powerpc/include/asm/barrier.h
> +++ b/arch/powerpc/include/asm/barrier.h
> @@ -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") diff --git a/arch/powerpc/include/asm/feature-fixups.h
> b/arch/powerpc/include/asm/feature-fixups.h index
> 1e82eb3caabd..86ac59e75f36 100644 ---
> a/arch/powerpc/include/asm/feature-fixups.h +++
> b/arch/powerpc/include/asm/feature-fixups.h @@ -195,11 +195,20 @@
> 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>
>  
>  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);
> diff --git a/arch/powerpc/include/asm/setup.h
> b/arch/powerpc/include/asm/setup.h index 27fa52ed6d00..afc7280cce3b
> 100644 --- a/arch/powerpc/include/asm/setup.h
> +++ b/arch/powerpc/include/asm/setup.h
> @@ -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__ */
>  
> diff --git a/arch/powerpc/kernel/security.c
> b/arch/powerpc/kernel/security.c index bab5a27ea805..b963eae0b0a0
> 100644 --- a/arch/powerpc/kernel/security.c
> +++ b/arch/powerpc/kernel/security.c
> @@ -9,10 +9,19 @@
>  #include <linux/seq_buf.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;
> diff --git a/arch/powerpc/kernel/vmlinux.lds.S
> b/arch/powerpc/kernel/vmlinux.lds.S index c8af90ff49f0..ff73f498568c
> 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S
> +++ b/arch/powerpc/kernel/vmlinux.lds.S
> @@ -139,6 +139,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)
> diff --git a/arch/powerpc/lib/feature-fixups.c
> b/arch/powerpc/lib/feature-fixups.c index 288fe4f0db4e..093c1d2ea5fd
> 100644 --- a/arch/powerpc/lib/feature-fixups.c
> +++ b/arch/powerpc/lib/feature-fixups.c
> @@ -162,6 +162,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)
Michael Ellerman May 1, 2018, 12:25 p.m. UTC | #2
Michal Suchánek <msuchanek@suse.de> writes:
> On Tue, 24 Apr 2018 14:15:55 +1000
> Michael Ellerman <mpe@ellerman.id.au> wrote:
>
>> From: Michal Suchanek <msuchanek@suse.de>
>> 
>> Based on the RFI patching. This is required to be able to disable the
>> speculation barrier.
>
> why do you not patch the nospec barrier which is included as part of
> the RFI flush code?

We didn't want to put it in the asm like you had, because not all RFI
flush types need the spec barrier.

So it's more complicated than just patching in the spec barrier, we'd
need to only patch it in if the configured RFI flush also needed it.

> I think when debugging the code it would make more sense if RFI is
> patched by RFI patcher and nospec by nospec patcher.

True. I think what I'm saying is that the spec barrier in the RFI
flush is not a nospec barrier, it's part of that RFI flush type.

> A separate question is if the RFI flush would break without the nospec
> barrier.

The ORI flush requires it, but the others don't.

cheers
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h
index e582d2c88092..f67b3f6e36be 100644
--- a/arch/powerpc/include/asm/barrier.h
+++ b/arch/powerpc/include/asm/barrier.h
@@ -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")
diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h
index 1e82eb3caabd..86ac59e75f36 100644
--- a/arch/powerpc/include/asm/feature-fixups.h
+++ b/arch/powerpc/include/asm/feature-fixups.h
@@ -195,11 +195,20 @@  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>
 
 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);
diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h
index 27fa52ed6d00..afc7280cce3b 100644
--- a/arch/powerpc/include/asm/setup.h
+++ b/arch/powerpc/include/asm/setup.h
@@ -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__ */
 
diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c
index bab5a27ea805..b963eae0b0a0 100644
--- a/arch/powerpc/kernel/security.c
+++ b/arch/powerpc/kernel/security.c
@@ -9,10 +9,19 @@ 
 #include <linux/seq_buf.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;
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index c8af90ff49f0..ff73f498568c 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -139,6 +139,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)
diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c
index 288fe4f0db4e..093c1d2ea5fd 100644
--- a/arch/powerpc/lib/feature-fixups.c
+++ b/arch/powerpc/lib/feature-fixups.c
@@ -162,6 +162,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)