1 /* 2 * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 /* 19 * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo 20 * 21 * 22 * Purpose 23 * ------- 24 * 25 * Demonstration of registering livepatch (un)patching callbacks. 26 * 27 * 28 * Usage 29 * ----- 30 * 31 * Step 1 - load the simple module 32 * 33 * insmod samples/livepatch/livepatch-callbacks-mod.ko 34 * 35 * 36 * Step 2 - load the demonstration livepatch (with callbacks) 37 * 38 * insmod samples/livepatch/livepatch-callbacks-demo.ko 39 * 40 * 41 * Step 3 - cleanup 42 * 43 * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled 44 * rmmod livepatch_callbacks_demo 45 * rmmod livepatch_callbacks_mod 46 * 47 * Watch dmesg output to see livepatch enablement, callback execution 48 * and patching operations for both vmlinux and module targets. 49 * 50 * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and 51 * livepatch-callbacks-demo.ko to observe what happens when a 52 * target module is loaded after a livepatch with callbacks. 53 * 54 * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch 55 * callback return status. Try setting up a non-zero status 56 * such as -19 (-ENODEV): 57 * 58 * # Load demo livepatch, vmlinux is patched 59 * insmod samples/livepatch/livepatch-callbacks-demo.ko 60 * 61 * # Setup next pre-patch callback to return -ENODEV 62 * echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret 63 * 64 * # Module loader refuses to load the target module 65 * insmod samples/livepatch/livepatch-callbacks-mod.ko 66 * insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device 67 * 68 * NOTE: There is a second target module, 69 * livepatch-callbacks-busymod.ko, available for experimenting 70 * with livepatch (un)patch callbacks. This module contains 71 * a 'sleep_secs' parameter that parks the module on one of the 72 * functions that the livepatch demo module wants to patch. 73 * Modifying this value and tweaking the order of module loads can 74 * effectively demonstrate stalled patch transitions: 75 * 76 * # Load a target module, let it park on 'busymod_work_func' for 77 * # thirty seconds 78 * insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 79 * 80 * # Meanwhile load the livepatch 81 * insmod samples/livepatch/livepatch-callbacks-demo.ko 82 * 83 * # ... then load and unload another target module while the 84 * # transition is in progress 85 * insmod samples/livepatch/livepatch-callbacks-mod.ko 86 * rmmod samples/livepatch/livepatch-callbacks-mod.ko 87 * 88 * # Finally cleanup 89 * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled 90 * rmmod samples/livepatch/livepatch-callbacks-demo.ko 91 */ 92 93 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 94 95 #include <linux/module.h> 96 #include <linux/kernel.h> 97 #include <linux/livepatch.h> 98 99 static int pre_patch_ret; 100 module_param(pre_patch_ret, int, 0644); 101 MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)"); 102 103 static const char *const module_state[] = { 104 [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", 105 [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", 106 [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", 107 [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", 108 }; 109 110 static void callback_info(const char *callback, struct klp_object *obj) 111 { 112 if (obj->mod) 113 pr_info("%s: %s -> %s\n", callback, obj->mod->name, 114 module_state[obj->mod->state]); 115 else 116 pr_info("%s: vmlinux\n", callback); 117 } 118 119 /* Executed on object patching (ie, patch enablement) */ 120 static int pre_patch_callback(struct klp_object *obj) 121 { 122 callback_info(__func__, obj); 123 return pre_patch_ret; 124 } 125 126 /* Executed on object unpatching (ie, patch disablement) */ 127 static void post_patch_callback(struct klp_object *obj) 128 { 129 callback_info(__func__, obj); 130 } 131 132 /* Executed on object unpatching (ie, patch disablement) */ 133 static void pre_unpatch_callback(struct klp_object *obj) 134 { 135 callback_info(__func__, obj); 136 } 137 138 /* Executed on object unpatching (ie, patch disablement) */ 139 static void post_unpatch_callback(struct klp_object *obj) 140 { 141 callback_info(__func__, obj); 142 } 143 144 static void patched_work_func(struct work_struct *work) 145 { 146 pr_info("%s\n", __func__); 147 } 148 149 static struct klp_func no_funcs[] = { 150 { } 151 }; 152 153 static struct klp_func busymod_funcs[] = { 154 { 155 .old_name = "busymod_work_func", 156 .new_func = patched_work_func, 157 }, { } 158 }; 159 160 static struct klp_object objs[] = { 161 { 162 .name = NULL, /* vmlinux */ 163 .funcs = no_funcs, 164 .callbacks = { 165 .pre_patch = pre_patch_callback, 166 .post_patch = post_patch_callback, 167 .pre_unpatch = pre_unpatch_callback, 168 .post_unpatch = post_unpatch_callback, 169 }, 170 }, { 171 .name = "livepatch_callbacks_mod", 172 .funcs = no_funcs, 173 .callbacks = { 174 .pre_patch = pre_patch_callback, 175 .post_patch = post_patch_callback, 176 .pre_unpatch = pre_unpatch_callback, 177 .post_unpatch = post_unpatch_callback, 178 }, 179 }, { 180 .name = "livepatch_callbacks_busymod", 181 .funcs = busymod_funcs, 182 .callbacks = { 183 .pre_patch = pre_patch_callback, 184 .post_patch = post_patch_callback, 185 .pre_unpatch = pre_unpatch_callback, 186 .post_unpatch = post_unpatch_callback, 187 }, 188 }, { } 189 }; 190 191 static struct klp_patch patch = { 192 .mod = THIS_MODULE, 193 .objs = objs, 194 }; 195 196 static int livepatch_callbacks_demo_init(void) 197 { 198 int ret; 199 200 ret = klp_register_patch(&patch); 201 if (ret) 202 return ret; 203 ret = klp_enable_patch(&patch); 204 if (ret) { 205 WARN_ON(klp_unregister_patch(&patch)); 206 return ret; 207 } 208 return 0; 209 } 210 211 static void livepatch_callbacks_demo_exit(void) 212 { 213 WARN_ON(klp_unregister_patch(&patch)); 214 } 215 216 module_init(livepatch_callbacks_demo_init); 217 module_exit(livepatch_callbacks_demo_exit); 218 MODULE_LICENSE("GPL"); 219 MODULE_INFO(livepatch, "Y"); 220