aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch
diff options
context:
space:
mode:
authorMichael Ellerman <mpe@ellerman.id.au>2016-03-24 07:04:02 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2016-04-14 01:47:05 -0400
commit28e7cbd3e0f5fefec892842d1391ebd508fdb5ce (patch)
tree98c58a0587978e5e81fb32baa13f64ce3f386158 /kernel/livepatch
parent04cf31a759ef575f750a63777cee95500e410994 (diff)
livepatch: Allow architectures to specify an alternate ftrace location
When livepatch tries to patch a function it takes the function address and asks ftrace to install the livepatch handler at that location. ftrace will look for an mcount call site at that exact address. On powerpc the mcount location is not the first instruction of the function, and in fact it's not at a constant offset from the start of the function. To accommodate this add a hook which arch code can override to customise the behaviour. Signed-off-by: Torsten Duwe <duwe@suse.de> Signed-off-by: Balbir Singh <bsingharora@gmail.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'kernel/livepatch')
-rw-r--r--kernel/livepatch/core.c34
1 files changed, 31 insertions, 3 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index bc2c85c064c1..35ee2c5b7176 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -298,6 +298,19 @@ unlock:
298 rcu_read_unlock(); 298 rcu_read_unlock();
299} 299}
300 300
301/*
302 * Convert a function address into the appropriate ftrace location.
303 *
304 * Usually this is just the address of the function, but on some architectures
305 * it's more complicated so allow them to provide a custom behaviour.
306 */
307#ifndef klp_get_ftrace_location
308static unsigned long klp_get_ftrace_location(unsigned long faddr)
309{
310 return faddr;
311}
312#endif
313
301static void klp_disable_func(struct klp_func *func) 314static void klp_disable_func(struct klp_func *func)
302{ 315{
303 struct klp_ops *ops; 316 struct klp_ops *ops;
@@ -312,8 +325,14 @@ static void klp_disable_func(struct klp_func *func)
312 return; 325 return;
313 326
314 if (list_is_singular(&ops->func_stack)) { 327 if (list_is_singular(&ops->func_stack)) {
328 unsigned long ftrace_loc;
329
330 ftrace_loc = klp_get_ftrace_location(func->old_addr);
331 if (WARN_ON(!ftrace_loc))
332 return;
333
315 WARN_ON(unregister_ftrace_function(&ops->fops)); 334 WARN_ON(unregister_ftrace_function(&ops->fops));
316 WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0)); 335 WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
317 336
318 list_del_rcu(&func->stack_node); 337 list_del_rcu(&func->stack_node);
319 list_del(&ops->node); 338 list_del(&ops->node);
@@ -338,6 +357,15 @@ static int klp_enable_func(struct klp_func *func)
338 357
339 ops = klp_find_ops(func->old_addr); 358 ops = klp_find_ops(func->old_addr);
340 if (!ops) { 359 if (!ops) {
360 unsigned long ftrace_loc;
361
362 ftrace_loc = klp_get_ftrace_location(func->old_addr);
363 if (!ftrace_loc) {
364 pr_err("failed to find location for function '%s'\n",
365 func->old_name);
366 return -EINVAL;
367 }
368
341 ops = kzalloc(sizeof(*ops), GFP_KERNEL); 369 ops = kzalloc(sizeof(*ops), GFP_KERNEL);
342 if (!ops) 370 if (!ops)
343 return -ENOMEM; 371 return -ENOMEM;
@@ -352,7 +380,7 @@ static int klp_enable_func(struct klp_func *func)
352 INIT_LIST_HEAD(&ops->func_stack); 380 INIT_LIST_HEAD(&ops->func_stack);
353 list_add_rcu(&func->stack_node, &ops->func_stack); 381 list_add_rcu(&func->stack_node, &ops->func_stack);
354 382
355 ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 0, 0); 383 ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
356 if (ret) { 384 if (ret) {
357 pr_err("failed to set ftrace filter for function '%s' (%d)\n", 385 pr_err("failed to set ftrace filter for function '%s' (%d)\n",
358 func->old_name, ret); 386 func->old_name, ret);
@@ -363,7 +391,7 @@ static int klp_enable_func(struct klp_func *func)
363 if (ret) { 391 if (ret) {
364 pr_err("failed to register ftrace handler for function '%s' (%d)\n", 392 pr_err("failed to register ftrace handler for function '%s' (%d)\n",
365 func->old_name, ret); 393 func->old_name, ret);
366 ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0); 394 ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
367 goto err; 395 goto err;
368 } 396 }
369 397