Commit | Line | Data |
---|---|---|
ecd25094 PM |
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 2 supports migration. */ | |
14 | #define CONSOLE_LOGLEVEL_STATE_VERSION 2 | |
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 | ||
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 | ||
34 | static 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 | ||
58 | static 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 | ||
79 | static 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 | ||
98 | static 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) */ | |
119 | static 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) */ | |
126 | static 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) */ | |
133 | static 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) */ | |
140 | static void post_unpatch_callback(struct klp_object *obj) | |
141 | { | |
142 | callback_info(__func__, obj); | |
143 | free_loglevel_state(); | |
144 | } | |
145 | ||
146 | static struct klp_func no_funcs[] = { | |
147 | {} | |
148 | }; | |
149 | ||
150 | static 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 | ||
163 | static struct klp_state states[] = { | |
164 | { | |
165 | .id = CONSOLE_LOGLEVEL_STATE, | |
166 | .version = CONSOLE_LOGLEVEL_STATE_VERSION, | |
167 | }, { } | |
168 | }; | |
169 | ||
170 | static struct klp_patch patch = { | |
171 | .mod = THIS_MODULE, | |
172 | .objs = objs, | |
173 | .states = states, | |
174 | .replace = true, | |
175 | }; | |
176 | ||
177 | static int test_klp_callbacks_demo_init(void) | |
178 | { | |
179 | return klp_enable_patch(&patch); | |
180 | } | |
181 | ||
182 | static void test_klp_callbacks_demo_exit(void) | |
183 | { | |
184 | } | |
185 | ||
186 | module_init(test_klp_callbacks_demo_init); | |
187 | module_exit(test_klp_callbacks_demo_exit); | |
188 | MODULE_LICENSE("GPL"); | |
189 | MODULE_INFO(livepatch, "Y"); | |
190 | MODULE_AUTHOR("Petr Mladek <pmladek@suse.com>"); | |
191 | MODULE_DESCRIPTION("Livepatch test: system state modification"); |