diff options
Diffstat (limited to 'kernel/livepatch')
| -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 | ||
