1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com> 4 */ 5 6 /* 7 * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo 8 * 9 * 10 * Purpose 11 * ------- 12 * 13 * Demonstration of registering livepatch (un)patching callbacks. 14 * 15 * 16 * Usage 17 * ----- 18 * 19 * Step 1 - load the simple module 20 * 21 * insmod samples/livepatch/livepatch-callbacks-mod.ko 22 * 23 * 24 * Step 2 - load the demonstration livepatch (with callbacks) 25 * 26 * insmod samples/livepatch/livepatch-callbacks-demo.ko 27 * 28 * 29 * Step 3 - cleanup 30 * 31 * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled 32 * rmmod livepatch_callbacks_demo 33 * rmmod livepatch_callbacks_mod 34 * 35 * Watch dmesg output to see livepatch enablement, callback execution 36 * and patching operations for both vmlinux and module targets. 37 * 38 * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and 39 * livepatch-callbacks-demo.ko to observe what happens when a 40 * target module is loaded after a livepatch with callbacks. 41 * 42 * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch 43 * callback return status. Try setting up a non-zero status 44 * such as -19 (-ENODEV): 45 * 46 * # Load demo livepatch, vmlinux is patched 47 * insmod samples/livepatch/livepatch-callbacks-demo.ko 48 * 49 * # Setup next pre-patch callback to return -ENODEV 50 * echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret 51 * 52 * # Module loader refuses to load the target module 53 * insmod samples/livepatch/livepatch-callbacks-mod.ko 54 * insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device 55 * 56 * NOTE: There is a second target module, 57 * livepatch-callbacks-busymod.ko, available for experimenting 58 * with livepatch (un)patch callbacks. This module contains 59 * a 'sleep_secs' parameter that parks the module on one of the 60 * functions that the livepatch demo module wants to patch. 61 * Modifying this value and tweaking the order of module loads can 62 * effectively demonstrate stalled patch transitions: 63 * 64 * # Load a target module, let it park on 'busymod_work_func' for 65 * # thirty seconds 66 * insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 67 * 68 * # Meanwhile load the livepatch 69 * insmod samples/livepatch/livepatch-callbacks-demo.ko 70 * 71 * # ... then load and unload another target module while the 72 * # transition is in progress 73 * insmod samples/livepatch/livepatch-callbacks-mod.ko 74 * rmmod samples/livepatch/livepatch-callbacks-mod.ko 75 * 76 * # Finally cleanup 77 * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled 78 * rmmod samples/livepatch/livepatch-callbacks-demo.ko 79 */ 80 81 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 82 83 #include <linux/module.h> 84 #include <linux/kernel.h> 85 #include <linux/livepatch.h> 86 87 static int pre_patch_ret; 88 module_param(pre_patch_ret, int, 0644); 89 MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)"); 90 91 static 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 98 static 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 else 104 pr_info("%s: vmlinux\n", callback); 105 } 106 107 /* Executed on object patching (ie, patch enablement) */ 108 static 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) */ 115 static void post_patch_callback(struct klp_object *obj) 116 { 117 callback_info(__func__, obj); 118 } 119 120 /* Executed on object unpatching (ie, patch disablement) */ 121 static void pre_unpatch_callback(struct klp_object *obj) 122 { 123 callback_info(__func__, obj); 124 } 125 126 /* Executed on object unpatching (ie, patch disablement) */ 127 static void post_unpatch_callback(struct klp_object *obj) 128 { 129 callback_info(__func__, obj); 130 } 131 132 static void patched_work_func(struct work_struct *work) 133 { 134 pr_info("%s\n", __func__); 135 } 136 137 static struct klp_func no_funcs[] = { 138 { } 139 }; 140 141 static struct klp_func busymod_funcs[] = { 142 { 143 .old_name = "busymod_work_func", 144 .new_func = patched_work_func, 145 }, { } 146 }; 147 148 static 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 179 static struct klp_patch patch = { 180 .mod = THIS_MODULE, 181 .objs = objs, 182 }; 183 184 static int livepatch_callbacks_demo_init(void) 185 { 186 return klp_enable_patch(&patch); 187 } 188 189 static void livepatch_callbacks_demo_exit(void) 190 { 191 } 192 193 module_init(livepatch_callbacks_demo_init); 194 module_exit(livepatch_callbacks_demo_exit); 195 MODULE_LICENSE("GPL"); 196 MODULE_INFO(livepatch, "Y"); 197