192 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (C) 2019 SUSE3 4#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt5 6#include <linux/slab.h>7#include <linux/module.h>8#include <linux/kernel.h>9#include <linux/printk.h>10#include <linux/livepatch.h>11 12#define CONSOLE_LOGLEVEL_STATE 113/* Version 2 supports migration. */14#define CONSOLE_LOGLEVEL_STATE_VERSION 215 16static const char *const module_state[] = {17 [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",18 [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",19 [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",20 [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",21};22 23static void callback_info(const char *callback, struct klp_object *obj)24{25 if (obj->mod)26 pr_info("%s: %s -> %s\n", callback, obj->mod->name,27 module_state[obj->mod->state]);28 else29 pr_info("%s: vmlinux\n", callback);30}31 32static struct klp_patch patch;33 34static int allocate_loglevel_state(void)35{36 struct klp_state *loglevel_state, *prev_loglevel_state;37 38 prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);39 if (prev_loglevel_state) {40 pr_info("%s: space to store console_loglevel already allocated\n",41 __func__);42 return 0;43 }44 45 loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);46 if (!loglevel_state)47 return -EINVAL;48 49 loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);50 if (!loglevel_state->data)51 return -ENOMEM;52 53 pr_info("%s: allocating space to store console_loglevel\n",54 __func__);55 return 0;56}57 58static void fix_console_loglevel(void)59{60 struct klp_state *loglevel_state, *prev_loglevel_state;61 62 loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);63 if (!loglevel_state)64 return;65 66 prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);67 if (prev_loglevel_state) {68 pr_info("%s: taking over the console_loglevel change\n",69 __func__);70 loglevel_state->data = prev_loglevel_state->data;71 return;72 }73 74 pr_info("%s: fixing console_loglevel\n", __func__);75 *(int *)loglevel_state->data = console_loglevel;76 console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;77}78 79static void restore_console_loglevel(void)80{81 struct klp_state *loglevel_state, *prev_loglevel_state;82 83 prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);84 if (prev_loglevel_state) {85 pr_info("%s: passing the console_loglevel change back to the old livepatch\n",86 __func__);87 return;88 }89 90 loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);91 if (!loglevel_state)92 return;93 94 pr_info("%s: restoring console_loglevel\n", __func__);95 console_loglevel = *(int *)loglevel_state->data;96}97 98static void free_loglevel_state(void)99{100 struct klp_state *loglevel_state, *prev_loglevel_state;101 102 prev_loglevel_state = klp_get_prev_state(CONSOLE_LOGLEVEL_STATE);103 if (prev_loglevel_state) {104 pr_info("%s: keeping space to store console_loglevel\n",105 __func__);106 return;107 }108 109 loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);110 if (!loglevel_state)111 return;112 113 pr_info("%s: freeing space for the stored console_loglevel\n",114 __func__);115 kfree(loglevel_state->data);116}117 118/* Executed on object patching (ie, patch enablement) */119static int pre_patch_callback(struct klp_object *obj)120{121 callback_info(__func__, obj);122 return allocate_loglevel_state();123}124 125/* Executed on object unpatching (ie, patch disablement) */126static void post_patch_callback(struct klp_object *obj)127{128 callback_info(__func__, obj);129 fix_console_loglevel();130}131 132/* Executed on object unpatching (ie, patch disablement) */133static void pre_unpatch_callback(struct klp_object *obj)134{135 callback_info(__func__, obj);136 restore_console_loglevel();137}138 139/* Executed on object unpatching (ie, patch disablement) */140static void post_unpatch_callback(struct klp_object *obj)141{142 callback_info(__func__, obj);143 free_loglevel_state();144}145 146static struct klp_func no_funcs[] = {147 {}148};149 150static struct klp_object objs[] = {151 {152 .name = NULL, /* vmlinux */153 .funcs = no_funcs,154 .callbacks = {155 .pre_patch = pre_patch_callback,156 .post_patch = post_patch_callback,157 .pre_unpatch = pre_unpatch_callback,158 .post_unpatch = post_unpatch_callback,159 },160 }, { }161};162 163static struct klp_state states[] = {164 {165 .id = CONSOLE_LOGLEVEL_STATE,166 .version = CONSOLE_LOGLEVEL_STATE_VERSION,167 }, { }168};169 170static struct klp_patch patch = {171 .mod = THIS_MODULE,172 .objs = objs,173 .states = states,174 .replace = true,175};176 177static int test_klp_callbacks_demo_init(void)178{179 return klp_enable_patch(&patch);180}181 182static void test_klp_callbacks_demo_exit(void)183{184}185 186module_init(test_klp_callbacks_demo_init);187module_exit(test_klp_callbacks_demo_exit);188MODULE_LICENSE("GPL");189MODULE_INFO(livepatch, "Y");190MODULE_AUTHOR("Petr Mladek <pmladek@suse.com>");191MODULE_DESCRIPTION("Livepatch test: system state modification");192