Skip to content

Commit de6921c

Browse files
author
Vasily Gorbik
committed
s390/test_unwind: add program check context tests
Add unwinding from program check handler tests. Unwinder should be able to unwind through pt_regs stored by program check handler on task stack. Signed-off-by: Vasily Gorbik <[email protected]>
1 parent e740936 commit de6921c

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

arch/s390/lib/test_unwind.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/kthread.h>
1111
#include <linux/module.h>
1212
#include <linux/string.h>
13+
#include <linux/kprobes.h>
1314
#include <linux/wait.h>
1415
#include <asm/irq.h>
1516
#include <asm/delay.h>
@@ -119,6 +120,7 @@ static struct unwindme *unwindme;
119120
#define UWM_CALLER 0x8 /* Unwind starting from caller. */
120121
#define UWM_SWITCH_STACK 0x10 /* Use CALL_ON_STACK. */
121122
#define UWM_IRQ 0x20 /* Unwind from irq context. */
123+
#define UWM_PGM 0x40 /* Unwind from program check handler. */
122124

123125
static __always_inline unsigned long get_psw_addr(void)
124126
{
@@ -130,6 +132,17 @@ static __always_inline unsigned long get_psw_addr(void)
130132
return psw_addr;
131133
}
132134

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+
133146
/* This function may or may not appear in the backtrace. */
134147
static noinline int unwindme_func4(struct unwindme *u)
135148
{
@@ -140,6 +153,34 @@ static noinline int unwindme_func4(struct unwindme *u)
140153
wait_event(u->task_wq, kthread_should_park());
141154
kthread_parkme();
142155
return 0;
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
143184
} else {
144185
struct pt_regs regs;
145186

@@ -286,6 +327,12 @@ do { \
286327
TEST(UWM_IRQ | UWM_CALLER | UWM_SP);
287328
TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS);
288329
TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK);
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
289336
#undef TEST
290337

291338
return ret;

0 commit comments

Comments
 (0)