aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/livepatch/core.c')
-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