Skip to content

Commit 28e7cbd

Browse files
committed
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>
1 parent 04cf31a commit 28e7cbd

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

kernel/livepatch/core.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,19 @@ static void notrace klp_ftrace_handler(unsigned long ip,
298298
rcu_read_unlock();
299299
}
300300

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
308+
static unsigned long klp_get_ftrace_location(unsigned long faddr)
309+
{
310+
return faddr;
311+
}
312+
#endif
313+
301314
static void klp_disable_func(struct klp_func *func)
302315
{
303316
struct klp_ops *ops;
@@ -312,8 +325,14 @@ static void klp_disable_func(struct klp_func *func)
312325
return;
313326

314327
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+
315334
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));
317336

318337
list_del_rcu(&func->stack_node);
319338
list_del(&ops->node);
@@ -338,6 +357,15 @@ static int klp_enable_func(struct klp_func *func)
338357

339358
ops = klp_find_ops(func->old_addr);
340359
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+
341369
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
342370
if (!ops)
343371
return -ENOMEM;
@@ -352,7 +380,7 @@ static int klp_enable_func(struct klp_func *func)
352380
INIT_LIST_HEAD(&ops->func_stack);
353381
list_add_rcu(&func->stack_node, &ops->func_stack);
354382

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);
356384
if (ret) {
357385
pr_err("failed to set ftrace filter for function '%s' (%d)\n",
358386
func->old_name, ret);
@@ -363,7 +391,7 @@ static int klp_enable_func(struct klp_func *func)
363391
if (ret) {
364392
pr_err("failed to register ftrace handler for function '%s' (%d)\n",
365393
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);
367395
goto err;
368396
}
369397

0 commit comments

Comments
 (0)