Commit | Line | Data |
---|---|---|
badbf397 IL |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Test module for unwind_for_each_frame | |
4 | */ | |
5 | ||
6 | #define pr_fmt(fmt) "test_unwind: " fmt | |
7 | #include <asm/unwind.h> | |
8 | #include <linux/completion.h> | |
9 | #include <linux/kallsyms.h> | |
10 | #include <linux/kthread.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/string.h> | |
de6921cc | 13 | #include <linux/kprobes.h> |
badbf397 | 14 | #include <linux/wait.h> |
e7409367 VG |
15 | #include <asm/irq.h> |
16 | #include <asm/delay.h> | |
badbf397 IL |
17 | |
18 | #define BT_BUF_SIZE (PAGE_SIZE * 4) | |
19 | ||
20 | /* | |
21 | * To avoid printk line limit split backtrace by lines | |
22 | */ | |
23 | static void print_backtrace(char *bt) | |
24 | { | |
25 | char *p; | |
26 | ||
27 | while (true) { | |
28 | p = strsep(&bt, "\n"); | |
29 | if (!p) | |
30 | break; | |
31 | pr_err("%s\n", p); | |
32 | } | |
33 | } | |
34 | ||
35 | /* | |
36 | * Calls unwind_for_each_frame(task, regs, sp) and verifies that the result | |
37 | * contains unwindme_func2 followed by unwindme_func1. | |
38 | */ | |
39 | static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, | |
40 | unsigned long sp) | |
41 | { | |
42 | int frame_count, prev_is_func2, seen_func2_func1; | |
43 | const int max_frames = 128; | |
44 | struct unwind_state state; | |
45 | size_t bt_pos = 0; | |
46 | int ret = 0; | |
47 | char *bt; | |
48 | ||
7868249f | 49 | bt = kmalloc(BT_BUF_SIZE, GFP_ATOMIC); |
badbf397 IL |
50 | if (!bt) { |
51 | pr_err("failed to allocate backtrace buffer\n"); | |
52 | return -ENOMEM; | |
53 | } | |
54 | /* Unwind. */ | |
55 | frame_count = 0; | |
56 | prev_is_func2 = 0; | |
57 | seen_func2_func1 = 0; | |
58 | unwind_for_each_frame(&state, task, regs, sp) { | |
59 | unsigned long addr = unwind_get_return_address(&state); | |
60 | char sym[KSYM_SYMBOL_LEN]; | |
61 | ||
06101546 | 62 | if (frame_count++ == max_frames) |
badbf397 | 63 | break; |
06101546 VG |
64 | if (state.reliable && !addr) { |
65 | pr_err("unwind state reliable but addr is 0\n"); | |
66 | return -EINVAL; | |
67 | } | |
badbf397 IL |
68 | sprint_symbol(sym, addr); |
69 | if (bt_pos < BT_BUF_SIZE) { | |
06101546 VG |
70 | bt_pos += snprintf(bt + bt_pos, BT_BUF_SIZE - bt_pos, |
71 | state.reliable ? " [%-7s%px] %pSR\n" : | |
72 | "([%-7s%px] %pSR)\n", | |
73 | stack_type_name(state.stack_info.type), | |
74 | (void *)state.sp, (void *)state.ip); | |
badbf397 IL |
75 | if (bt_pos >= BT_BUF_SIZE) |
76 | pr_err("backtrace buffer is too small\n"); | |
77 | } | |
78 | frame_count += 1; | |
79 | if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1")) | |
80 | seen_func2_func1 = 1; | |
81 | prev_is_func2 = str_has_prefix(sym, "unwindme_func2"); | |
82 | } | |
83 | ||
84 | /* Check the results. */ | |
f44fa79b VG |
85 | if (unwind_error(&state)) { |
86 | pr_err("unwind error\n"); | |
87 | ret = -EINVAL; | |
88 | } | |
badbf397 IL |
89 | if (!seen_func2_func1) { |
90 | pr_err("unwindme_func2 and unwindme_func1 not found\n"); | |
91 | ret = -EINVAL; | |
92 | } | |
93 | if (frame_count == max_frames) { | |
94 | pr_err("Maximum number of frames exceeded\n"); | |
95 | ret = -EINVAL; | |
96 | } | |
97 | if (ret) | |
98 | print_backtrace(bt); | |
99 | kfree(bt); | |
100 | return ret; | |
101 | } | |
102 | ||
103 | /* State of the task being unwound. */ | |
104 | struct unwindme { | |
105 | int flags; | |
e7409367 VG |
106 | int ret; |
107 | struct task_struct *task; | |
badbf397 IL |
108 | struct completion task_ready; |
109 | wait_queue_head_t task_wq; | |
110 | unsigned long sp; | |
111 | }; | |
112 | ||
e7409367 VG |
113 | static struct unwindme *unwindme; |
114 | ||
badbf397 | 115 | /* Values of unwindme.flags. */ |
7868249f VG |
116 | #define UWM_DEFAULT 0x0 |
117 | #define UWM_THREAD 0x1 /* Unwind a separate task. */ | |
118 | #define UWM_REGS 0x2 /* Pass regs to test_unwind(). */ | |
119 | #define UWM_SP 0x4 /* Pass sp to test_unwind(). */ | |
120 | #define UWM_CALLER 0x8 /* Unwind starting from caller. */ | |
121 | #define UWM_SWITCH_STACK 0x10 /* Use CALL_ON_STACK. */ | |
e7409367 | 122 | #define UWM_IRQ 0x20 /* Unwind from irq context. */ |
de6921cc | 123 | #define UWM_PGM 0x40 /* Unwind from program check handler. */ |
badbf397 IL |
124 | |
125 | static __always_inline unsigned long get_psw_addr(void) | |
126 | { | |
127 | unsigned long psw_addr; | |
128 | ||
129 | asm volatile( | |
130 | "basr %[psw_addr],0\n" | |
131 | : [psw_addr] "=d" (psw_addr)); | |
132 | return psw_addr; | |
133 | } | |
134 | ||
de6921cc VG |
135 | #ifdef CONFIG_KPROBES |
136 | static int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs) | |
137 | { | |
138 | struct unwindme *u = unwindme; | |
139 | ||
140 | u->ret = test_unwind(NULL, (u->flags & UWM_REGS) ? regs : NULL, | |
141 | (u->flags & UWM_SP) ? u->sp : 0); | |
142 | return 0; | |
143 | } | |
144 | #endif | |
145 | ||
badbf397 IL |
146 | /* This function may or may not appear in the backtrace. */ |
147 | static noinline int unwindme_func4(struct unwindme *u) | |
148 | { | |
149 | if (!(u->flags & UWM_CALLER)) | |
150 | u->sp = current_frame_address(); | |
151 | if (u->flags & UWM_THREAD) { | |
152 | complete(&u->task_ready); | |
153 | wait_event(u->task_wq, kthread_should_park()); | |
154 | kthread_parkme(); | |
155 | return 0; | |
de6921cc VG |
156 | #ifdef CONFIG_KPROBES |
157 | } else if (u->flags & UWM_PGM) { | |
158 | struct kprobe kp; | |
159 | int ret; | |
160 | ||
161 | unwindme = u; | |
162 | memset(&kp, 0, sizeof(kp)); | |
163 | kp.symbol_name = "do_report_trap"; | |
164 | kp.pre_handler = pgm_pre_handler; | |
165 | ret = register_kprobe(&kp); | |
166 | if (ret < 0) { | |
167 | pr_err("register_kprobe failed %d\n", ret); | |
168 | return -EINVAL; | |
169 | } | |
170 | ||
171 | /* | |
172 | * trigger specification exception | |
173 | */ | |
174 | asm volatile( | |
175 | " mvcl %%r1,%%r1\n" | |
176 | "0: nopr %%r7\n" | |
177 | EX_TABLE(0b, 0b) | |
178 | :); | |
179 | ||
180 | unregister_kprobe(&kp); | |
181 | unwindme = NULL; | |
182 | return u->ret; | |
183 | #endif | |
badbf397 IL |
184 | } else { |
185 | struct pt_regs regs; | |
186 | ||
187 | memset(®s, 0, sizeof(regs)); | |
188 | regs.psw.addr = get_psw_addr(); | |
189 | regs.gprs[15] = current_stack_pointer(); | |
190 | return test_unwind(NULL, | |
191 | (u->flags & UWM_REGS) ? ®s : NULL, | |
192 | (u->flags & UWM_SP) ? u->sp : 0); | |
193 | } | |
194 | } | |
195 | ||
196 | /* This function may or may not appear in the backtrace. */ | |
197 | static noinline int unwindme_func3(struct unwindme *u) | |
198 | { | |
199 | u->sp = current_frame_address(); | |
200 | return unwindme_func4(u); | |
201 | } | |
202 | ||
203 | /* This function must appear in the backtrace. */ | |
204 | static noinline int unwindme_func2(struct unwindme *u) | |
205 | { | |
7868249f VG |
206 | int rc; |
207 | ||
208 | if (u->flags & UWM_SWITCH_STACK) { | |
209 | preempt_disable(); | |
210 | rc = CALL_ON_STACK(unwindme_func3, S390_lowcore.nodat_stack, 1, u); | |
211 | preempt_enable(); | |
212 | return rc; | |
213 | } else { | |
214 | return unwindme_func3(u); | |
215 | } | |
badbf397 IL |
216 | } |
217 | ||
218 | /* This function must follow unwindme_func2 in the backtrace. */ | |
219 | static noinline int unwindme_func1(void *u) | |
220 | { | |
221 | return unwindme_func2((struct unwindme *)u); | |
222 | } | |
223 | ||
e7409367 VG |
224 | static void unwindme_irq_handler(struct ext_code ext_code, |
225 | unsigned int param32, | |
226 | unsigned long param64) | |
227 | { | |
228 | struct unwindme *u = READ_ONCE(unwindme); | |
229 | ||
230 | if (u && u->task == current) { | |
231 | unwindme = NULL; | |
232 | u->task = NULL; | |
233 | u->ret = unwindme_func1(u); | |
234 | } | |
235 | } | |
236 | ||
237 | static int test_unwind_irq(struct unwindme *u) | |
238 | { | |
239 | preempt_disable(); | |
240 | if (register_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler)) { | |
7e914fd1 | 241 | pr_info("Couldn't register external interrupt handler"); |
e7409367 VG |
242 | return -1; |
243 | } | |
244 | u->task = current; | |
245 | unwindme = u; | |
246 | udelay(1); | |
247 | unregister_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler); | |
248 | preempt_enable(); | |
249 | return u->ret; | |
250 | } | |
251 | ||
badbf397 IL |
252 | /* Spawns a task and passes it to test_unwind(). */ |
253 | static int test_unwind_task(struct unwindme *u) | |
254 | { | |
255 | struct task_struct *task; | |
256 | int ret; | |
257 | ||
258 | /* Initialize thread-related fields. */ | |
259 | init_completion(&u->task_ready); | |
260 | init_waitqueue_head(&u->task_wq); | |
261 | ||
262 | /* | |
263 | * Start the task and wait until it reaches unwindme_func4() and sleeps | |
264 | * in (task_ready, unwind_done] range. | |
265 | */ | |
266 | task = kthread_run(unwindme_func1, u, "%s", __func__); | |
267 | if (IS_ERR(task)) { | |
268 | pr_err("kthread_run() failed\n"); | |
269 | return PTR_ERR(task); | |
270 | } | |
271 | /* | |
272 | * Make sure task reaches unwindme_func4 before parking it, | |
273 | * we might park it before kthread function has been executed otherwise | |
274 | */ | |
275 | wait_for_completion(&u->task_ready); | |
276 | kthread_park(task); | |
277 | /* Unwind. */ | |
278 | ret = test_unwind(task, NULL, (u->flags & UWM_SP) ? u->sp : 0); | |
279 | kthread_stop(task); | |
280 | return ret; | |
281 | } | |
282 | ||
283 | static int test_unwind_flags(int flags) | |
284 | { | |
285 | struct unwindme u; | |
286 | ||
287 | u.flags = flags; | |
288 | if (u.flags & UWM_THREAD) | |
289 | return test_unwind_task(&u); | |
e7409367 VG |
290 | else if (u.flags & UWM_IRQ) |
291 | return test_unwind_irq(&u); | |
badbf397 IL |
292 | else |
293 | return unwindme_func1(&u); | |
294 | } | |
295 | ||
296 | static int test_unwind_init(void) | |
297 | { | |
298 | int ret = 0; | |
299 | ||
300 | #define TEST(flags) \ | |
301 | do { \ | |
302 | pr_info("[ RUN ] " #flags "\n"); \ | |
303 | if (!test_unwind_flags((flags))) { \ | |
304 | pr_info("[ OK ] " #flags "\n"); \ | |
305 | } else { \ | |
306 | pr_err("[ FAILED ] " #flags "\n"); \ | |
307 | ret = -EINVAL; \ | |
308 | } \ | |
309 | } while (0) | |
310 | ||
311 | TEST(UWM_DEFAULT); | |
312 | TEST(UWM_SP); | |
313 | TEST(UWM_REGS); | |
7868249f | 314 | TEST(UWM_SWITCH_STACK); |
badbf397 IL |
315 | TEST(UWM_SP | UWM_REGS); |
316 | TEST(UWM_CALLER | UWM_SP); | |
317 | TEST(UWM_CALLER | UWM_SP | UWM_REGS); | |
7868249f | 318 | TEST(UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); |
badbf397 IL |
319 | TEST(UWM_THREAD); |
320 | TEST(UWM_THREAD | UWM_SP); | |
321 | TEST(UWM_THREAD | UWM_CALLER | UWM_SP); | |
e7409367 VG |
322 | TEST(UWM_IRQ); |
323 | TEST(UWM_IRQ | UWM_SWITCH_STACK); | |
324 | TEST(UWM_IRQ | UWM_SP); | |
325 | TEST(UWM_IRQ | UWM_REGS); | |
326 | TEST(UWM_IRQ | UWM_SP | UWM_REGS); | |
327 | TEST(UWM_IRQ | UWM_CALLER | UWM_SP); | |
328 | TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS); | |
329 | TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); | |
de6921cc VG |
330 | #ifdef CONFIG_KPROBES |
331 | TEST(UWM_PGM); | |
332 | TEST(UWM_PGM | UWM_SP); | |
333 | TEST(UWM_PGM | UWM_REGS); | |
334 | TEST(UWM_PGM | UWM_SP | UWM_REGS); | |
335 | #endif | |
badbf397 IL |
336 | #undef TEST |
337 | ||
338 | return ret; | |
339 | } | |
340 | ||
341 | static void test_unwind_exit(void) | |
342 | { | |
343 | } | |
344 | ||
345 | module_init(test_unwind_init); | |
346 | module_exit(test_unwind_exit); | |
347 | MODULE_LICENSE("GPL"); |