]>
Commit | Line | Data |
---|---|---|
4fa5b95e GKH |
1 | From foo@baz Mon 29 Apr 2019 11:38:37 AM CEST |
2 | From: Michael Ellerman <mpe@ellerman.id.au> | |
3 | Date: Mon, 22 Apr 2019 00:20:11 +1000 | |
4 | Subject: powerpc/64s: Add support for ori barrier_nospec patching | |
5 | To: stable@vger.kernel.org, gregkh@linuxfoundation.org | |
6 | Cc: linuxppc-dev@ozlabs.org, diana.craciun@nxp.com, msuchanek@suse.de, npiggin@gmail.com, christophe.leroy@c-s.fr | |
7 | Message-ID: <20190421142037.21881-27-mpe@ellerman.id.au> | |
8 | ||
9 | From: Michal Suchanek <msuchanek@suse.de> | |
10 | ||
11 | commit 2eea7f067f495e33b8b116b35b5988ab2b8aec55 upstream. | |
12 | ||
13 | Based on the RFI patching. This is required to be able to disable the | |
14 | speculation barrier. | |
15 | ||
16 | Only one barrier type is supported and it does nothing when the | |
17 | firmware does not enable it. Also re-patching modules is not supported | |
18 | So the only meaningful thing that can be done is patching out the | |
19 | speculation barrier at boot when the user says it is not wanted. | |
20 | ||
21 | Signed-off-by: Michal Suchanek <msuchanek@suse.de> | |
22 | Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> | |
23 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
24 | --- | |
25 | arch/powerpc/include/asm/barrier.h | 2 +- | |
26 | arch/powerpc/include/asm/feature-fixups.h | 9 +++++++++ | |
27 | arch/powerpc/include/asm/setup.h | 1 + | |
28 | arch/powerpc/kernel/security.c | 9 +++++++++ | |
29 | arch/powerpc/kernel/vmlinux.lds.S | 7 +++++++ | |
30 | arch/powerpc/lib/feature-fixups.c | 27 +++++++++++++++++++++++++++ | |
31 | 6 files changed, 54 insertions(+), 1 deletion(-) | |
32 | ||
33 | --- a/arch/powerpc/include/asm/barrier.h | |
34 | +++ b/arch/powerpc/include/asm/barrier.h | |
35 | @@ -97,7 +97,7 @@ do { \ | |
36 | * Prevent execution of subsequent instructions until preceding branches have | |
37 | * been fully resolved and are no longer executing speculatively. | |
38 | */ | |
39 | -#define barrier_nospec_asm ori 31,31,0 | |
40 | +#define barrier_nospec_asm NOSPEC_BARRIER_FIXUP_SECTION; nop | |
41 | ||
42 | // This also acts as a compiler barrier due to the memory clobber. | |
43 | #define barrier_nospec() asm (stringify_in_c(barrier_nospec_asm) ::: "memory") | |
44 | --- a/arch/powerpc/include/asm/feature-fixups.h | |
45 | +++ b/arch/powerpc/include/asm/feature-fixups.h | |
46 | @@ -208,6 +208,14 @@ label##3: \ | |
47 | FTR_ENTRY_OFFSET 951b-952b; \ | |
48 | .popsection; | |
49 | ||
50 | +#define NOSPEC_BARRIER_FIXUP_SECTION \ | |
51 | +953: \ | |
52 | + .pushsection __barrier_nospec_fixup,"a"; \ | |
53 | + .align 2; \ | |
54 | +954: \ | |
55 | + FTR_ENTRY_OFFSET 953b-954b; \ | |
56 | + .popsection; | |
57 | + | |
58 | ||
59 | #ifndef __ASSEMBLY__ | |
60 | ||
61 | @@ -215,6 +223,7 @@ extern long stf_barrier_fallback; | |
62 | extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup; | |
63 | extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup; | |
64 | extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup; | |
65 | +extern long __start___barrier_nospec_fixup, __stop___barrier_nospec_fixup; | |
66 | ||
67 | #endif | |
68 | ||
69 | --- a/arch/powerpc/include/asm/setup.h | |
70 | +++ b/arch/powerpc/include/asm/setup.h | |
71 | @@ -38,6 +38,7 @@ enum l1d_flush_type { | |
72 | ||
73 | void setup_rfi_flush(enum l1d_flush_type, bool enable); | |
74 | void do_rfi_flush_fixups(enum l1d_flush_type types); | |
75 | +void do_barrier_nospec_fixups(bool enable); | |
76 | ||
77 | #endif /* !__ASSEMBLY__ */ | |
78 | ||
79 | --- a/arch/powerpc/kernel/security.c | |
80 | +++ b/arch/powerpc/kernel/security.c | |
81 | @@ -11,10 +11,19 @@ | |
82 | ||
83 | #include <asm/debug.h> | |
84 | #include <asm/security_features.h> | |
85 | +#include <asm/setup.h> | |
86 | ||
87 | ||
88 | unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; | |
89 | ||
90 | +static bool barrier_nospec_enabled; | |
91 | + | |
92 | +static void enable_barrier_nospec(bool enable) | |
93 | +{ | |
94 | + barrier_nospec_enabled = enable; | |
95 | + do_barrier_nospec_fixups(enable); | |
96 | +} | |
97 | + | |
98 | ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) | |
99 | { | |
100 | bool thread_priv; | |
101 | --- a/arch/powerpc/kernel/vmlinux.lds.S | |
102 | +++ b/arch/powerpc/kernel/vmlinux.lds.S | |
103 | @@ -93,6 +93,13 @@ SECTIONS | |
104 | *(__rfi_flush_fixup) | |
105 | __stop___rfi_flush_fixup = .; | |
106 | } | |
107 | + | |
108 | + . = ALIGN(8); | |
109 | + __spec_barrier_fixup : AT(ADDR(__spec_barrier_fixup) - LOAD_OFFSET) { | |
110 | + __start___barrier_nospec_fixup = .; | |
111 | + *(__barrier_nospec_fixup) | |
112 | + __stop___barrier_nospec_fixup = .; | |
113 | + } | |
114 | #endif | |
115 | ||
116 | EXCEPTION_TABLE(0) | |
117 | --- a/arch/powerpc/lib/feature-fixups.c | |
118 | +++ b/arch/powerpc/lib/feature-fixups.c | |
119 | @@ -274,6 +274,33 @@ void do_rfi_flush_fixups(enum l1d_flush_ | |
120 | (types & L1D_FLUSH_MTTRIG) ? "mttrig type" | |
121 | : "unknown"); | |
122 | } | |
123 | + | |
124 | +void do_barrier_nospec_fixups(bool enable) | |
125 | +{ | |
126 | + unsigned int instr, *dest; | |
127 | + long *start, *end; | |
128 | + int i; | |
129 | + | |
130 | + start = PTRRELOC(&__start___barrier_nospec_fixup), | |
131 | + end = PTRRELOC(&__stop___barrier_nospec_fixup); | |
132 | + | |
133 | + instr = 0x60000000; /* nop */ | |
134 | + | |
135 | + if (enable) { | |
136 | + pr_info("barrier-nospec: using ORI speculation barrier\n"); | |
137 | + instr = 0x63ff0000; /* ori 31,31,0 speculation barrier */ | |
138 | + } | |
139 | + | |
140 | + for (i = 0; start < end; start++, i++) { | |
141 | + dest = (void *)start + *start; | |
142 | + | |
143 | + pr_devel("patching dest %lx\n", (unsigned long)dest); | |
144 | + patch_instruction(dest, instr); | |
145 | + } | |
146 | + | |
147 | + printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); | |
148 | +} | |
149 | + | |
150 | #endif /* CONFIG_PPC_BOOK3S_64 */ | |
151 | ||
152 | void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end) |