diff options
Diffstat (limited to 'kernel/livepatch/core.c')
-rw-r--r-- | kernel/livepatch/core.c | 34 |
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 | ||
308 | static unsigned long klp_get_ftrace_location(unsigned long faddr) | ||
309 | { | ||
310 | return faddr; | ||
311 | } | ||
312 | #endif | ||
313 | |||
301 | static void klp_disable_func(struct klp_func *func) | 314 | static 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 | ||