• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // SPDX-License-Identifier: GPL-2.0
2  // Copyright (C) 2019 SUSE
3  
4  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5  
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 1
13  /* Version 1 does not support migration. */
14  #define CONSOLE_LOGLEVEL_STATE_VERSION 1
15  
16  static 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  
callback_info(const char * callback,struct klp_object * obj)23  static 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  	else
29  		pr_info("%s: vmlinux\n", callback);
30  }
31  
32  static struct klp_patch patch;
33  
allocate_loglevel_state(void)34  static int allocate_loglevel_state(void)
35  {
36  	struct klp_state *loglevel_state;
37  
38  	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
39  	if (!loglevel_state)
40  		return -EINVAL;
41  
42  	loglevel_state->data = kzalloc(sizeof(console_loglevel), GFP_KERNEL);
43  	if (!loglevel_state->data)
44  		return -ENOMEM;
45  
46  	pr_info("%s: allocating space to store console_loglevel\n",
47  		__func__);
48  	return 0;
49  }
50  
fix_console_loglevel(void)51  static void fix_console_loglevel(void)
52  {
53  	struct klp_state *loglevel_state;
54  
55  	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
56  	if (!loglevel_state)
57  		return;
58  
59  	pr_info("%s: fixing console_loglevel\n", __func__);
60  	*(int *)loglevel_state->data = console_loglevel;
61  	console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
62  }
63  
restore_console_loglevel(void)64  static void restore_console_loglevel(void)
65  {
66  	struct klp_state *loglevel_state;
67  
68  	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
69  	if (!loglevel_state)
70  		return;
71  
72  	pr_info("%s: restoring console_loglevel\n", __func__);
73  	console_loglevel = *(int *)loglevel_state->data;
74  }
75  
free_loglevel_state(void)76  static void free_loglevel_state(void)
77  {
78  	struct klp_state *loglevel_state;
79  
80  	loglevel_state = klp_get_state(&patch, CONSOLE_LOGLEVEL_STATE);
81  	if (!loglevel_state)
82  		return;
83  
84  	pr_info("%s: freeing space for the stored console_loglevel\n",
85  		__func__);
86  	kfree(loglevel_state->data);
87  }
88  
89  /* Executed on object patching (ie, patch enablement) */
pre_patch_callback(struct klp_object * obj)90  static int pre_patch_callback(struct klp_object *obj)
91  {
92  	callback_info(__func__, obj);
93  	return allocate_loglevel_state();
94  }
95  
96  /* Executed on object unpatching (ie, patch disablement) */
post_patch_callback(struct klp_object * obj)97  static void post_patch_callback(struct klp_object *obj)
98  {
99  	callback_info(__func__, obj);
100  	fix_console_loglevel();
101  }
102  
103  /* Executed on object unpatching (ie, patch disablement) */
pre_unpatch_callback(struct klp_object * obj)104  static void pre_unpatch_callback(struct klp_object *obj)
105  {
106  	callback_info(__func__, obj);
107  	restore_console_loglevel();
108  }
109  
110  /* Executed on object unpatching (ie, patch disablement) */
post_unpatch_callback(struct klp_object * obj)111  static void post_unpatch_callback(struct klp_object *obj)
112  {
113  	callback_info(__func__, obj);
114  	free_loglevel_state();
115  }
116  
117  static struct klp_func no_funcs[] = {
118  	{}
119  };
120  
121  static struct klp_object objs[] = {
122  	{
123  		.name = NULL,	/* vmlinux */
124  		.funcs = no_funcs,
125  		.callbacks = {
126  			.pre_patch = pre_patch_callback,
127  			.post_patch = post_patch_callback,
128  			.pre_unpatch = pre_unpatch_callback,
129  			.post_unpatch = post_unpatch_callback,
130  		},
131  	}, { }
132  };
133  
134  static struct klp_state states[] = {
135  	{
136  		.id = CONSOLE_LOGLEVEL_STATE,
137  		.version = CONSOLE_LOGLEVEL_STATE_VERSION,
138  	}, { }
139  };
140  
141  static struct klp_patch patch = {
142  	.mod = THIS_MODULE,
143  	.objs = objs,
144  	.states = states,
145  	.replace = true,
146  };
147  
test_klp_callbacks_demo_init(void)148  static int test_klp_callbacks_demo_init(void)
149  {
150  	return klp_enable_patch(&patch);
151  }
152  
test_klp_callbacks_demo_exit(void)153  static void test_klp_callbacks_demo_exit(void)
154  {
155  }
156  
157  module_init(test_klp_callbacks_demo_init);
158  module_exit(test_klp_callbacks_demo_exit);
159  MODULE_LICENSE("GPL");
160  MODULE_INFO(livepatch, "Y");
161  MODULE_AUTHOR("Petr Mladek <pmladek@suse.com>");
162  MODULE_DESCRIPTION("Livepatch test: system state modification");
163