]>
Commit | Line | Data |
---|---|---|
45d80ddf SL |
1 | From 919e979557d238b97468b72de9ca82d10f279a93 Mon Sep 17 00:00:00 2001 |
2 | From: Michal Suchanek <msuchanek@suse.de> | |
3 | Date: Thu, 11 Apr 2019 21:46:01 +1000 | |
4 | Subject: powerpc/64s: Enable barrier_nospec based on firmware settings | |
5 | ||
6 | commit cb3d6759a93c6d0aea1c10deb6d00e111c29c19c upstream. | |
7 | ||
8 | Check what firmware told us and enable/disable the barrier_nospec as | |
9 | appropriate. | |
10 | ||
11 | We err on the side of enabling the barrier, as it's no-op on older | |
12 | systems, see the comment for more detail. | |
13 | ||
14 | Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> | |
15 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
16 | --- | |
17 | arch/powerpc/include/asm/setup.h | 1 + | |
18 | arch/powerpc/kernel/security.c | 60 ++++++++++++++++++++++++++ | |
19 | arch/powerpc/platforms/powernv/setup.c | 1 + | |
20 | arch/powerpc/platforms/pseries/setup.c | 1 + | |
21 | 4 files changed, 63 insertions(+) | |
22 | ||
23 | diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h | |
24 | index a225b5c42e76..84ae150ce6a6 100644 | |
25 | --- a/arch/powerpc/include/asm/setup.h | |
26 | +++ b/arch/powerpc/include/asm/setup.h | |
27 | @@ -51,6 +51,7 @@ enum l1d_flush_type { | |
28 | ||
29 | void setup_rfi_flush(enum l1d_flush_type, bool enable); | |
30 | void do_rfi_flush_fixups(enum l1d_flush_type types); | |
31 | +void setup_barrier_nospec(void); | |
32 | void do_barrier_nospec_fixups(bool enable); | |
33 | extern bool barrier_nospec_enabled; | |
34 | ||
35 | diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c | |
36 | index 34d436fe2498..d0e974da4918 100644 | |
37 | --- a/arch/powerpc/kernel/security.c | |
38 | +++ b/arch/powerpc/kernel/security.c | |
39 | @@ -9,6 +9,7 @@ | |
40 | #include <linux/device.h> | |
41 | #include <linux/seq_buf.h> | |
42 | ||
43 | +#include <asm/debug.h> | |
44 | #include <asm/security_features.h> | |
45 | #include <asm/setup.h> | |
46 | ||
47 | @@ -23,6 +24,65 @@ static void enable_barrier_nospec(bool enable) | |
48 | do_barrier_nospec_fixups(enable); | |
49 | } | |
50 | ||
51 | +void setup_barrier_nospec(void) | |
52 | +{ | |
53 | + bool enable; | |
54 | + | |
55 | + /* | |
56 | + * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well. | |
57 | + * But there's a good reason not to. The two flags we check below are | |
58 | + * both are enabled by default in the kernel, so if the hcall is not | |
59 | + * functional they will be enabled. | |
60 | + * On a system where the host firmware has been updated (so the ori | |
61 | + * functions as a barrier), but on which the hypervisor (KVM/Qemu) has | |
62 | + * not been updated, we would like to enable the barrier. Dropping the | |
63 | + * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is | |
64 | + * we potentially enable the barrier on systems where the host firmware | |
65 | + * is not updated, but that's harmless as it's a no-op. | |
66 | + */ | |
67 | + enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && | |
68 | + security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR); | |
69 | + | |
70 | + enable_barrier_nospec(enable); | |
71 | +} | |
72 | + | |
73 | +#ifdef CONFIG_DEBUG_FS | |
74 | +static int barrier_nospec_set(void *data, u64 val) | |
75 | +{ | |
76 | + switch (val) { | |
77 | + case 0: | |
78 | + case 1: | |
79 | + break; | |
80 | + default: | |
81 | + return -EINVAL; | |
82 | + } | |
83 | + | |
84 | + if (!!val == !!barrier_nospec_enabled) | |
85 | + return 0; | |
86 | + | |
87 | + enable_barrier_nospec(!!val); | |
88 | + | |
89 | + return 0; | |
90 | +} | |
91 | + | |
92 | +static int barrier_nospec_get(void *data, u64 *val) | |
93 | +{ | |
94 | + *val = barrier_nospec_enabled ? 1 : 0; | |
95 | + return 0; | |
96 | +} | |
97 | + | |
98 | +DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec, | |
99 | + barrier_nospec_get, barrier_nospec_set, "%llu\n"); | |
100 | + | |
101 | +static __init int barrier_nospec_debugfs_init(void) | |
102 | +{ | |
103 | + debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL, | |
104 | + &fops_barrier_nospec); | |
105 | + return 0; | |
106 | +} | |
107 | +device_initcall(barrier_nospec_debugfs_init); | |
108 | +#endif /* CONFIG_DEBUG_FS */ | |
109 | + | |
110 | ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) | |
111 | { | |
112 | bool thread_priv; | |
113 | diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c | |
114 | index 17203abf38e8..eb5464648810 100644 | |
115 | --- a/arch/powerpc/platforms/powernv/setup.c | |
116 | +++ b/arch/powerpc/platforms/powernv/setup.c | |
117 | @@ -123,6 +123,7 @@ static void pnv_setup_rfi_flush(void) | |
118 | security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV)); | |
119 | ||
120 | setup_rfi_flush(type, enable); | |
121 | + setup_barrier_nospec(); | |
122 | } | |
123 | ||
124 | static void __init pnv_setup_arch(void) | |
125 | diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c | |
126 | index 91ade7755823..2b2759c98c7e 100644 | |
127 | --- a/arch/powerpc/platforms/pseries/setup.c | |
128 | +++ b/arch/powerpc/platforms/pseries/setup.c | |
129 | @@ -525,6 +525,7 @@ void pseries_setup_rfi_flush(void) | |
130 | security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR); | |
131 | ||
132 | setup_rfi_flush(types, enable); | |
133 | + setup_barrier_nospec(); | |
134 | } | |
135 | ||
136 | static void __init pSeries_setup_arch(void) | |
137 | -- | |
138 | 2.19.1 | |
139 |