diff options
author | Michael Ellerman <mpe@ellerman.id.au> | 2016-03-24 07:04:05 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2016-04-14 01:48:06 -0400 |
commit | 85baa095497f3e590df9f6c8932121f123efca5c (patch) | |
tree | e05e3f82a00772249694c361a77efb593ac5781c | |
parent | 5d31a96e6c0187f2c5d7004e005fd094a1277e9e (diff) |
powerpc/livepatch: Add live patching support on ppc64le
Add the kconfig logic & assembly support for handling live patched
functions. This depends on DYNAMIC_FTRACE_WITH_REGS, which in turn
depends on the new -mprofile-kernel ftrace ABI, which is only supported
currently on ppc64le.
Live patching is handled by a special ftrace handler. This means it runs
from ftrace_caller(). The live patch handler modifies the NIP so as to
redirect the return from ftrace_caller() to the new patched function.
However there is one particularly tricky case we need to handle.
If a function A calls another function B, and it is known at link time
that they share the same TOC, then A will not save or restore its TOC,
and will call the local entry point of B.
When we live patch B, we replace it with a new function C, which may
not have the same TOC as A. At live patch time it's too late to modify A
to do the TOC save/restore, so the live patching code must interpose
itself between A and C, and do the TOC save/restore that A omitted.
An additionaly complication is that the livepatch code can not create a
stack frame in order to save the TOC. That is because if C takes > 8
arguments, or is varargs, A will have written the arguments for C in
A's stack frame.
To solve this, we introduce a "livepatch stack" which grows upward from
the base of the regular stack, and is used to store the TOC & LR when
calling a live patched function.
When the patched function returns, we retrieve the real LR & TOC from
the livepatch stack, restore them, and pop the livepatch "stack frame".
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Reviewed-by: Torsten Duwe <duwe@suse.de>
Reviewed-by: Balbir Singh <bsingharora@gmail.com>
-rw-r--r-- | arch/powerpc/Kconfig | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/asm-offsets.c | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/entry_64.S | 97 |
3 files changed, 104 insertions, 0 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 91da283cd658..926c0eafbee3 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -159,6 +159,7 @@ config PPC | |||
159 | select ARCH_HAS_DEVMEM_IS_ALLOWED | 159 | select ARCH_HAS_DEVMEM_IS_ALLOWED |
160 | select HAVE_ARCH_SECCOMP_FILTER | 160 | select HAVE_ARCH_SECCOMP_FILTER |
161 | select ARCH_HAS_UBSAN_SANITIZE_ALL | 161 | select ARCH_HAS_UBSAN_SANITIZE_ALL |
162 | select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS | ||
162 | 163 | ||
163 | config GENERIC_CSUM | 164 | config GENERIC_CSUM |
164 | def_bool CPU_LITTLE_ENDIAN | 165 | def_bool CPU_LITTLE_ENDIAN |
@@ -1110,3 +1111,5 @@ config PPC_LIB_RHEAP | |||
1110 | bool | 1111 | bool |
1111 | 1112 | ||
1112 | source "arch/powerpc/kvm/Kconfig" | 1113 | source "arch/powerpc/kvm/Kconfig" |
1114 | |||
1115 | source "kernel/livepatch/Kconfig" | ||
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 07cebc3514f3..723efac2d917 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c | |||
@@ -86,6 +86,10 @@ int main(void) | |||
86 | DEFINE(KSP_LIMIT, offsetof(struct thread_struct, ksp_limit)); | 86 | DEFINE(KSP_LIMIT, offsetof(struct thread_struct, ksp_limit)); |
87 | #endif /* CONFIG_PPC64 */ | 87 | #endif /* CONFIG_PPC64 */ |
88 | 88 | ||
89 | #ifdef CONFIG_LIVEPATCH | ||
90 | DEFINE(TI_livepatch_sp, offsetof(struct thread_info, livepatch_sp)); | ||
91 | #endif | ||
92 | |||
89 | DEFINE(KSP, offsetof(struct thread_struct, ksp)); | 93 | DEFINE(KSP, offsetof(struct thread_struct, ksp)); |
90 | DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); | 94 | DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); |
91 | #ifdef CONFIG_BOOKE | 95 | #ifdef CONFIG_BOOKE |
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index ec7f8aada697..47dbede3bddd 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S | |||
@@ -20,6 +20,7 @@ | |||
20 | 20 | ||
21 | #include <linux/errno.h> | 21 | #include <linux/errno.h> |
22 | #include <linux/err.h> | 22 | #include <linux/err.h> |
23 | #include <linux/magic.h> | ||
23 | #include <asm/unistd.h> | 24 | #include <asm/unistd.h> |
24 | #include <asm/processor.h> | 25 | #include <asm/processor.h> |
25 | #include <asm/page.h> | 26 | #include <asm/page.h> |
@@ -1224,6 +1225,9 @@ _GLOBAL(ftrace_caller) | |||
1224 | addi r3,r3,function_trace_op@toc@l | 1225 | addi r3,r3,function_trace_op@toc@l |
1225 | ld r5,0(r3) | 1226 | ld r5,0(r3) |
1226 | 1227 | ||
1228 | #ifdef CONFIG_LIVEPATCH | ||
1229 | mr r14,r7 /* remember old NIP */ | ||
1230 | #endif | ||
1227 | /* Calculate ip from nip-4 into r3 for call below */ | 1231 | /* Calculate ip from nip-4 into r3 for call below */ |
1228 | subi r3, r7, MCOUNT_INSN_SIZE | 1232 | subi r3, r7, MCOUNT_INSN_SIZE |
1229 | 1233 | ||
@@ -1248,6 +1252,9 @@ ftrace_call: | |||
1248 | /* Load ctr with the possibly modified NIP */ | 1252 | /* Load ctr with the possibly modified NIP */ |
1249 | ld r3, _NIP(r1) | 1253 | ld r3, _NIP(r1) |
1250 | mtctr r3 | 1254 | mtctr r3 |
1255 | #ifdef CONFIG_LIVEPATCH | ||
1256 | cmpd r14,r3 /* has NIP been altered? */ | ||
1257 | #endif | ||
1251 | 1258 | ||
1252 | /* Restore gprs */ | 1259 | /* Restore gprs */ |
1253 | REST_8GPRS(0,r1) | 1260 | REST_8GPRS(0,r1) |
@@ -1265,6 +1272,11 @@ ftrace_call: | |||
1265 | ld r0, LRSAVE(r1) | 1272 | ld r0, LRSAVE(r1) |
1266 | mtlr r0 | 1273 | mtlr r0 |
1267 | 1274 | ||
1275 | #ifdef CONFIG_LIVEPATCH | ||
1276 | /* Based on the cmpd above, if the NIP was altered handle livepatch */ | ||
1277 | bne- livepatch_handler | ||
1278 | #endif | ||
1279 | |||
1268 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 1280 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
1269 | stdu r1, -112(r1) | 1281 | stdu r1, -112(r1) |
1270 | .globl ftrace_graph_call | 1282 | .globl ftrace_graph_call |
@@ -1281,6 +1293,91 @@ _GLOBAL(ftrace_graph_stub) | |||
1281 | 1293 | ||
1282 | _GLOBAL(ftrace_stub) | 1294 | _GLOBAL(ftrace_stub) |
1283 | blr | 1295 | blr |
1296 | |||
1297 | #ifdef CONFIG_LIVEPATCH | ||
1298 | /* | ||
1299 | * This function runs in the mcount context, between two functions. As | ||
1300 | * such it can only clobber registers which are volatile and used in | ||
1301 | * function linkage. | ||
1302 | * | ||
1303 | * We get here when a function A, calls another function B, but B has | ||
1304 | * been live patched with a new function C. | ||
1305 | * | ||
1306 | * On entry: | ||
1307 | * - we have no stack frame and can not allocate one | ||
1308 | * - LR points back to the original caller (in A) | ||
1309 | * - CTR holds the new NIP in C | ||
1310 | * - r0 & r12 are free | ||
1311 | * | ||
1312 | * r0 can't be used as the base register for a DS-form load or store, so | ||
1313 | * we temporarily shuffle r1 (stack pointer) into r0 and then put it back. | ||
1314 | */ | ||
1315 | livepatch_handler: | ||
1316 | CURRENT_THREAD_INFO(r12, r1) | ||
1317 | |||
1318 | /* Save stack pointer into r0 */ | ||
1319 | mr r0, r1 | ||
1320 | |||
1321 | /* Allocate 3 x 8 bytes */ | ||
1322 | ld r1, TI_livepatch_sp(r12) | ||
1323 | addi r1, r1, 24 | ||
1324 | std r1, TI_livepatch_sp(r12) | ||
1325 | |||
1326 | /* Save toc & real LR on livepatch stack */ | ||
1327 | std r2, -24(r1) | ||
1328 | mflr r12 | ||
1329 | std r12, -16(r1) | ||
1330 | |||
1331 | /* Store stack end marker */ | ||
1332 | lis r12, STACK_END_MAGIC@h | ||
1333 | ori r12, r12, STACK_END_MAGIC@l | ||
1334 | std r12, -8(r1) | ||
1335 | |||
1336 | /* Restore real stack pointer */ | ||
1337 | mr r1, r0 | ||
1338 | |||
1339 | /* Put ctr in r12 for global entry and branch there */ | ||
1340 | mfctr r12 | ||
1341 | bctrl | ||
1342 | |||
1343 | /* | ||
1344 | * Now we are returning from the patched function to the original | ||
1345 | * caller A. We are free to use r0 and r12, and we can use r2 until we | ||
1346 | * restore it. | ||
1347 | */ | ||
1348 | |||
1349 | CURRENT_THREAD_INFO(r12, r1) | ||
1350 | |||
1351 | /* Save stack pointer into r0 */ | ||
1352 | mr r0, r1 | ||
1353 | |||
1354 | ld r1, TI_livepatch_sp(r12) | ||
1355 | |||
1356 | /* Check stack marker hasn't been trashed */ | ||
1357 | lis r2, STACK_END_MAGIC@h | ||
1358 | ori r2, r2, STACK_END_MAGIC@l | ||
1359 | ld r12, -8(r1) | ||
1360 | 1: tdne r12, r2 | ||
1361 | EMIT_BUG_ENTRY 1b, __FILE__, __LINE__ - 1, 0 | ||
1362 | |||
1363 | /* Restore LR & toc from livepatch stack */ | ||
1364 | ld r12, -16(r1) | ||
1365 | mtlr r12 | ||
1366 | ld r2, -24(r1) | ||
1367 | |||
1368 | /* Pop livepatch stack frame */ | ||
1369 | CURRENT_THREAD_INFO(r12, r0) | ||
1370 | subi r1, r1, 24 | ||
1371 | std r1, TI_livepatch_sp(r12) | ||
1372 | |||
1373 | /* Restore real stack pointer */ | ||
1374 | mr r1, r0 | ||
1375 | |||
1376 | /* Return to original caller of live patched function */ | ||
1377 | blr | ||
1378 | #endif | ||
1379 | |||
1380 | |||
1284 | #else | 1381 | #else |
1285 | _GLOBAL_TOC(_mcount) | 1382 | _GLOBAL_TOC(_mcount) |
1286 | /* Taken from output of objdump from lib64/glibc */ | 1383 | /* Taken from output of objdump from lib64/glibc */ |