History

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.

Michael Ellerman committed 10 years ago

Commit 28e7cbd1 parent 04cf31a1 file · +31 −3Browse files

Showing the changes to kernel/livepatch (1 of 1 file). Show all

Unified · Split

kernel/livepatch/core.c+31−3
@@ -298,6 +298,19 @@ unlock: · 297 unmodified lines
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) · 8 unmodified lines
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) · 18 unmodified lines
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) · 8 unmodified lines
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) · 4 unmodified lines
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