197 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>4 */5 6/*7 * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo8 *9 *10 * Purpose11 * -------12 *13 * Demonstration of registering livepatch (un)patching callbacks.14 *15 *16 * Usage17 * -----18 *19 * Step 1 - load the simple module20 *21 * insmod samples/livepatch/livepatch-callbacks-mod.ko22 *23 *24 * Step 2 - load the demonstration livepatch (with callbacks)25 *26 * insmod samples/livepatch/livepatch-callbacks-demo.ko27 *28 *29 * Step 3 - cleanup30 *31 * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled32 * rmmod livepatch_callbacks_demo33 * rmmod livepatch_callbacks_mod34 *35 * Watch dmesg output to see livepatch enablement, callback execution36 * and patching operations for both vmlinux and module targets.37 *38 * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and39 * livepatch-callbacks-demo.ko to observe what happens when a40 * target module is loaded after a livepatch with callbacks.41 *42 * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch43 * callback return status. Try setting up a non-zero status44 * such as -19 (-ENODEV):45 *46 * # Load demo livepatch, vmlinux is patched47 * insmod samples/livepatch/livepatch-callbacks-demo.ko48 *49 * # Setup next pre-patch callback to return -ENODEV50 * echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret51 *52 * # Module loader refuses to load the target module53 * insmod samples/livepatch/livepatch-callbacks-mod.ko54 * insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device55 *56 * NOTE: There is a second target module,57 * livepatch-callbacks-busymod.ko, available for experimenting58 * with livepatch (un)patch callbacks. This module contains59 * a 'sleep_secs' parameter that parks the module on one of the60 * functions that the livepatch demo module wants to patch.61 * Modifying this value and tweaking the order of module loads can62 * effectively demonstrate stalled patch transitions:63 *64 * # Load a target module, let it park on 'busymod_work_func' for65 * # thirty seconds66 * insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=3067 *68 * # Meanwhile load the livepatch69 * insmod samples/livepatch/livepatch-callbacks-demo.ko70 *71 * # ... then load and unload another target module while the72 * # transition is in progress73 * insmod samples/livepatch/livepatch-callbacks-mod.ko74 * rmmod samples/livepatch/livepatch-callbacks-mod.ko75 *76 * # Finally cleanup77 * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled78 * rmmod samples/livepatch/livepatch-callbacks-demo.ko79 */80 81#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt82 83#include <linux/module.h>84#include <linux/kernel.h>85#include <linux/livepatch.h>86 87static int pre_patch_ret;88module_param(pre_patch_ret, int, 0644);89MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");90 91static const char *const module_state[] = {92 [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",93 [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",94 [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",95 [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",96};97 98static void callback_info(const char *callback, struct klp_object *obj)99{100 if (obj->mod)101 pr_info("%s: %s -> %s\n", callback, obj->mod->name,102 module_state[obj->mod->state]);103 else104 pr_info("%s: vmlinux\n", callback);105}106 107/* Executed on object patching (ie, patch enablement) */108static int pre_patch_callback(struct klp_object *obj)109{110 callback_info(__func__, obj);111 return pre_patch_ret;112}113 114/* Executed on object unpatching (ie, patch disablement) */115static void post_patch_callback(struct klp_object *obj)116{117 callback_info(__func__, obj);118}119 120/* Executed on object unpatching (ie, patch disablement) */121static void pre_unpatch_callback(struct klp_object *obj)122{123 callback_info(__func__, obj);124}125 126/* Executed on object unpatching (ie, patch disablement) */127static void post_unpatch_callback(struct klp_object *obj)128{129 callback_info(__func__, obj);130}131 132static void patched_work_func(struct work_struct *work)133{134 pr_info("%s\n", __func__);135}136 137static struct klp_func no_funcs[] = {138 { }139};140 141static struct klp_func busymod_funcs[] = {142 {143 .old_name = "busymod_work_func",144 .new_func = patched_work_func,145 }, { }146};147 148static struct klp_object objs[] = {149 {150 .name = NULL, /* vmlinux */151 .funcs = no_funcs,152 .callbacks = {153 .pre_patch = pre_patch_callback,154 .post_patch = post_patch_callback,155 .pre_unpatch = pre_unpatch_callback,156 .post_unpatch = post_unpatch_callback,157 },158 }, {159 .name = "livepatch_callbacks_mod",160 .funcs = no_funcs,161 .callbacks = {162 .pre_patch = pre_patch_callback,163 .post_patch = post_patch_callback,164 .pre_unpatch = pre_unpatch_callback,165 .post_unpatch = post_unpatch_callback,166 },167 }, {168 .name = "livepatch_callbacks_busymod",169 .funcs = busymod_funcs,170 .callbacks = {171 .pre_patch = pre_patch_callback,172 .post_patch = post_patch_callback,173 .pre_unpatch = pre_unpatch_callback,174 .post_unpatch = post_unpatch_callback,175 },176 }, { }177};178 179static struct klp_patch patch = {180 .mod = THIS_MODULE,181 .objs = objs,182};183 184static int livepatch_callbacks_demo_init(void)185{186 return klp_enable_patch(&patch);187}188 189static void livepatch_callbacks_demo_exit(void)190{191}192 193module_init(livepatch_callbacks_demo_init);194module_exit(livepatch_callbacks_demo_exit);195MODULE_LICENSE("GPL");196MODULE_INFO(livepatch, "Y");197