Commit 28e7cbd3 authored by Michael Ellerman's avatar Michael Ellerman

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: default avatarTorsten Duwe <duwe@suse.de>
Signed-off-by: default avatarBalbir Singh <bsingharora@gmail.com>
Signed-off-by: default avatarPetr Mladek <pmladek@suse.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 04cf31a7
...@@ -298,6 +298,19 @@ static void notrace klp_ftrace_handler(unsigned long ip, ...@@ -298,6 +298,19 @@ static void notrace klp_ftrace_handler(unsigned long ip,
rcu_read_unlock(); rcu_read_unlock();
} }
/*
* Convert a function address into the appropriate ftrace location.
*
* Usually this is just the address of the function, but on some architectures
* it's more complicated so allow them to provide a custom behaviour.
*/
#ifndef klp_get_ftrace_location
static unsigned long klp_get_ftrace_location(unsigned long faddr)
{
return faddr;
}
#endif
static void klp_disable_func(struct klp_func *func) static void klp_disable_func(struct klp_func *func)
{ {
struct klp_ops *ops; struct klp_ops *ops;
...@@ -312,8 +325,14 @@ static void klp_disable_func(struct klp_func *func) ...@@ -312,8 +325,14 @@ static void klp_disable_func(struct klp_func *func)
return; return;
if (list_is_singular(&ops->func_stack)) { if (list_is_singular(&ops->func_stack)) {
unsigned long ftrace_loc;
ftrace_loc = klp_get_ftrace_location(func->old_addr);
if (WARN_ON(!ftrace_loc))
return;
WARN_ON(unregister_ftrace_function(&ops->fops)); WARN_ON(unregister_ftrace_function(&ops->fops));
WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0)); WARN_ON(ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0));
list_del_rcu(&func->stack_node); list_del_rcu(&func->stack_node);
list_del(&ops->node); list_del(&ops->node);
...@@ -338,6 +357,15 @@ static int klp_enable_func(struct klp_func *func) ...@@ -338,6 +357,15 @@ static int klp_enable_func(struct klp_func *func)
ops = klp_find_ops(func->old_addr); ops = klp_find_ops(func->old_addr);
if (!ops) { if (!ops) {
unsigned long ftrace_loc;
ftrace_loc = klp_get_ftrace_location(func->old_addr);
if (!ftrace_loc) {
pr_err("failed to find location for function '%s'\n",
func->old_name);
return -EINVAL;
}
ops = kzalloc(sizeof(*ops), GFP_KERNEL); ops = kzalloc(sizeof(*ops), GFP_KERNEL);
if (!ops) if (!ops)
return -ENOMEM; return -ENOMEM;
...@@ -352,7 +380,7 @@ static int klp_enable_func(struct klp_func *func) ...@@ -352,7 +380,7 @@ static int klp_enable_func(struct klp_func *func)
INIT_LIST_HEAD(&ops->func_stack); INIT_LIST_HEAD(&ops->func_stack);
list_add_rcu(&func->stack_node, &ops->func_stack); list_add_rcu(&func->stack_node, &ops->func_stack);
ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 0, 0); ret = ftrace_set_filter_ip(&ops->fops, ftrace_loc, 0, 0);
if (ret) { if (ret) {
pr_err("failed to set ftrace filter for function '%s' (%d)\n", pr_err("failed to set ftrace filter for function '%s' (%d)\n",
func->old_name, ret); func->old_name, ret);
...@@ -363,7 +391,7 @@ static int klp_enable_func(struct klp_func *func) ...@@ -363,7 +391,7 @@ static int klp_enable_func(struct klp_func *func)
if (ret) { if (ret) {
pr_err("failed to register ftrace handler for function '%s' (%d)\n", pr_err("failed to register ftrace handler for function '%s' (%d)\n",
func->old_name, ret); func->old_name, ret);
ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0); ftrace_set_filter_ip(&ops->fops, ftrace_loc, 1, 0);
goto err; goto err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment