162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* 762306a36Sopenharmony_ci * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Purpose 1162306a36Sopenharmony_ci * ------- 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Demonstration of registering livepatch (un)patching callbacks. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Usage 1762306a36Sopenharmony_ci * ----- 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Step 1 - load the simple module 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-mod.ko 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Step 2 - load the demonstration livepatch (with callbacks) 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-demo.ko 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Step 3 - cleanup 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled 3262306a36Sopenharmony_ci * rmmod livepatch_callbacks_demo 3362306a36Sopenharmony_ci * rmmod livepatch_callbacks_mod 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Watch dmesg output to see livepatch enablement, callback execution 3662306a36Sopenharmony_ci * and patching operations for both vmlinux and module targets. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and 3962306a36Sopenharmony_ci * livepatch-callbacks-demo.ko to observe what happens when a 4062306a36Sopenharmony_ci * target module is loaded after a livepatch with callbacks. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch 4362306a36Sopenharmony_ci * callback return status. Try setting up a non-zero status 4462306a36Sopenharmony_ci * such as -19 (-ENODEV): 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * # Load demo livepatch, vmlinux is patched 4762306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-demo.ko 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * # Setup next pre-patch callback to return -ENODEV 5062306a36Sopenharmony_ci * echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * # Module loader refuses to load the target module 5362306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-mod.ko 5462306a36Sopenharmony_ci * insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * NOTE: There is a second target module, 5762306a36Sopenharmony_ci * livepatch-callbacks-busymod.ko, available for experimenting 5862306a36Sopenharmony_ci * with livepatch (un)patch callbacks. This module contains 5962306a36Sopenharmony_ci * a 'sleep_secs' parameter that parks the module on one of the 6062306a36Sopenharmony_ci * functions that the livepatch demo module wants to patch. 6162306a36Sopenharmony_ci * Modifying this value and tweaking the order of module loads can 6262306a36Sopenharmony_ci * effectively demonstrate stalled patch transitions: 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * # Load a target module, let it park on 'busymod_work_func' for 6562306a36Sopenharmony_ci * # thirty seconds 6662306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * # Meanwhile load the livepatch 6962306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-demo.ko 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * # ... then load and unload another target module while the 7262306a36Sopenharmony_ci * # transition is in progress 7362306a36Sopenharmony_ci * insmod samples/livepatch/livepatch-callbacks-mod.ko 7462306a36Sopenharmony_ci * rmmod samples/livepatch/livepatch-callbacks-mod.ko 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * # Finally cleanup 7762306a36Sopenharmony_ci * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled 7862306a36Sopenharmony_ci * rmmod samples/livepatch/livepatch-callbacks-demo.ko 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#include <linux/module.h> 8462306a36Sopenharmony_ci#include <linux/kernel.h> 8562306a36Sopenharmony_ci#include <linux/livepatch.h> 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int pre_patch_ret; 8862306a36Sopenharmony_cimodule_param(pre_patch_ret, int, 0644); 8962306a36Sopenharmony_ciMODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)"); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const char *const module_state[] = { 9262306a36Sopenharmony_ci [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", 9362306a36Sopenharmony_ci [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", 9462306a36Sopenharmony_ci [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", 9562306a36Sopenharmony_ci [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void callback_info(const char *callback, struct klp_object *obj) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (obj->mod) 10162306a36Sopenharmony_ci pr_info("%s: %s -> %s\n", callback, obj->mod->name, 10262306a36Sopenharmony_ci module_state[obj->mod->state]); 10362306a36Sopenharmony_ci else 10462306a36Sopenharmony_ci pr_info("%s: vmlinux\n", callback); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Executed on object patching (ie, patch enablement) */ 10862306a36Sopenharmony_cistatic int pre_patch_callback(struct klp_object *obj) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci callback_info(__func__, obj); 11162306a36Sopenharmony_ci return pre_patch_ret; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Executed on object unpatching (ie, patch disablement) */ 11562306a36Sopenharmony_cistatic void post_patch_callback(struct klp_object *obj) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci callback_info(__func__, obj); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* Executed on object unpatching (ie, patch disablement) */ 12162306a36Sopenharmony_cistatic void pre_unpatch_callback(struct klp_object *obj) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci callback_info(__func__, obj); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Executed on object unpatching (ie, patch disablement) */ 12762306a36Sopenharmony_cistatic void post_unpatch_callback(struct klp_object *obj) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci callback_info(__func__, obj); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void patched_work_func(struct work_struct *work) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci pr_info("%s\n", __func__); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct klp_func no_funcs[] = { 13862306a36Sopenharmony_ci { } 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct klp_func busymod_funcs[] = { 14262306a36Sopenharmony_ci { 14362306a36Sopenharmony_ci .old_name = "busymod_work_func", 14462306a36Sopenharmony_ci .new_func = patched_work_func, 14562306a36Sopenharmony_ci }, { } 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct klp_object objs[] = { 14962306a36Sopenharmony_ci { 15062306a36Sopenharmony_ci .name = NULL, /* vmlinux */ 15162306a36Sopenharmony_ci .funcs = no_funcs, 15262306a36Sopenharmony_ci .callbacks = { 15362306a36Sopenharmony_ci .pre_patch = pre_patch_callback, 15462306a36Sopenharmony_ci .post_patch = post_patch_callback, 15562306a36Sopenharmony_ci .pre_unpatch = pre_unpatch_callback, 15662306a36Sopenharmony_ci .post_unpatch = post_unpatch_callback, 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci }, { 15962306a36Sopenharmony_ci .name = "livepatch_callbacks_mod", 16062306a36Sopenharmony_ci .funcs = no_funcs, 16162306a36Sopenharmony_ci .callbacks = { 16262306a36Sopenharmony_ci .pre_patch = pre_patch_callback, 16362306a36Sopenharmony_ci .post_patch = post_patch_callback, 16462306a36Sopenharmony_ci .pre_unpatch = pre_unpatch_callback, 16562306a36Sopenharmony_ci .post_unpatch = post_unpatch_callback, 16662306a36Sopenharmony_ci }, 16762306a36Sopenharmony_ci }, { 16862306a36Sopenharmony_ci .name = "livepatch_callbacks_busymod", 16962306a36Sopenharmony_ci .funcs = busymod_funcs, 17062306a36Sopenharmony_ci .callbacks = { 17162306a36Sopenharmony_ci .pre_patch = pre_patch_callback, 17262306a36Sopenharmony_ci .post_patch = post_patch_callback, 17362306a36Sopenharmony_ci .pre_unpatch = pre_unpatch_callback, 17462306a36Sopenharmony_ci .post_unpatch = post_unpatch_callback, 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci }, { } 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic struct klp_patch patch = { 18062306a36Sopenharmony_ci .mod = THIS_MODULE, 18162306a36Sopenharmony_ci .objs = objs, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int livepatch_callbacks_demo_init(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return klp_enable_patch(&patch); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void livepatch_callbacks_demo_exit(void) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cimodule_init(livepatch_callbacks_demo_init); 19462306a36Sopenharmony_cimodule_exit(livepatch_callbacks_demo_exit); 19562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 19662306a36Sopenharmony_ciMODULE_INFO(livepatch, "Y"); 197