| From aec86b052df6541cc97c5fca44e5934cbea4963b Mon Sep 17 00:00:00 2001 |
| From: Michael Ellerman <mpe@ellerman.id.au> |
| Date: Thu, 6 May 2021 14:49:59 +1000 |
| Subject: powerpc/64s: Fix crashes when toggling entry flush barrier |
| |
| From: Michael Ellerman <mpe@ellerman.id.au> |
| |
| commit aec86b052df6541cc97c5fca44e5934cbea4963b upstream. |
| |
| The entry flush mitigation can be enabled/disabled at runtime via a |
| debugfs file (entry_flush), which causes the kernel to patch itself to |
| enable/disable the relevant mitigations. |
| |
| However depending on which mitigation we're using, it may not be safe to |
| do that patching while other CPUs are active. For example the following |
| crash: |
| |
| sleeper[15639]: segfault (11) at c000000000004c20 nip c000000000004c20 lr c000000000004c20 |
| |
| Shows that we returned to userspace with a corrupted LR that points into |
| the kernel, due to executing the partially patched call to the fallback |
| entry flush (ie. we missed the LR restore). |
| |
| Fix it by doing the patching under stop machine. The CPUs that aren't |
| doing the patching will be spinning in the core of the stop machine |
| logic. That is currently sufficient for our purposes, because none of |
| the patching we do is to that code or anywhere in the vicinity. |
| |
| Fixes: f79643787e0a ("powerpc/64s: flush L1D on kernel entry") |
| Cc: stable@vger.kernel.org # v5.10+ |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Link: https://lore.kernel.org/r/20210506044959.1298123-2-mpe@ellerman.id.au |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/powerpc/lib/feature-fixups.c | 16 +++++++++++++++- |
| 1 file changed, 15 insertions(+), 1 deletion(-) |
| |
| --- a/arch/powerpc/lib/feature-fixups.c |
| +++ b/arch/powerpc/lib/feature-fixups.c |
| @@ -299,8 +299,9 @@ void do_uaccess_flush_fixups(enum l1d_fl |
| : "unknown"); |
| } |
| |
| -void do_entry_flush_fixups(enum l1d_flush_type types) |
| +static int __do_entry_flush_fixups(void *data) |
| { |
| + enum l1d_flush_type types = *(enum l1d_flush_type *)data; |
| unsigned int instrs[3], *dest; |
| long *start, *end; |
| int i; |
| @@ -369,6 +370,19 @@ void do_entry_flush_fixups(enum l1d_flus |
| : "ori type" : |
| (types & L1D_FLUSH_MTTRIG) ? "mttrig type" |
| : "unknown"); |
| + |
| + return 0; |
| +} |
| + |
| +void do_entry_flush_fixups(enum l1d_flush_type types) |
| +{ |
| + /* |
| + * The call to the fallback flush can not be safely patched in/out while |
| + * other CPUs are executing it. So call __do_entry_flush_fixups() on one |
| + * CPU while all other CPUs spin in the stop machine core with interrupts |
| + * hard disabled. |
| + */ |
| + stop_machine(__do_entry_flush_fixups, &types, NULL); |
| } |
| |
| void do_rfi_flush_fixups(enum l1d_flush_type types) |