summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVasily Gorbik <gor@linux.ibm.com>2022-02-03 00:49:41 +0100
committerVasily Gorbik <gor@linux.ibm.com>2022-03-01 21:05:09 +0100
commit9ba142f472c1e4ec514f3bad1134b6b6ad8f6c13 (patch)
tree2f0e9877eb0de5e59ccf5a1cef4a3aa4c4c46282
parent829ec7491c401e4a3068955a438578d2c2ae8acc (diff)
s390/test_unwind: fix and extend kprobes test
Running kprobe test on a kernel built with clang 14 didn't actually trigger pgm_pre_handler() and no unwinder code was called. Even though do_report_trap() is a global symbol, clang inlined it in several local functions including illegal_op() handler, so that kprobbing a global symbol didn't have a desired effect. To achieve the same test result (unwinding from a program check handler) introduce a local function and probe an instruction in the middle, so that kprobe doesn't take KPROBE_ON_FTRACE path. While at it, add another test for KPROBE_ON_FTRACE. Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
-rw-r--r--arch/s390/lib/test_unwind.c83
1 files changed, 50 insertions, 33 deletions
diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c
index 2224694a0888..b209014ce426 100644
--- a/arch/s390/lib/test_unwind.c
+++ b/arch/s390/lib/test_unwind.c
@@ -129,8 +129,9 @@ static struct unwindme *unwindme;
#define UWM_CALLER 0x8 /* Unwind starting from caller. */
#define UWM_SWITCH_STACK 0x10 /* Use call_on_stack. */
#define UWM_IRQ 0x20 /* Unwind from irq context. */
-#define UWM_PGM 0x40 /* Unwind from program check handler. */
-#define UWM_FTRACE 0x80 /* Unwind from ftrace handler. */
+#define UWM_PGM 0x40 /* Unwind from program check handler */
+#define UWM_KPROBE_ON_FTRACE 0x80 /* Unwind from kprobe handler called via ftrace. */
+#define UWM_FTRACE 0x100 /* Unwind from ftrace handler. */
static __always_inline unsigned long get_psw_addr(void)
{
@@ -142,7 +143,7 @@ static __always_inline unsigned long get_psw_addr(void)
return psw_addr;
}
-static int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs)
+static int kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
struct unwindme *u = unwindme;
@@ -151,6 +152,46 @@ static int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs)
return 0;
}
+extern const char test_unwind_kprobed_insn[];
+
+static noinline void test_unwind_kprobed_func(void)
+{
+ asm volatile(
+ " nopr %%r7\n"
+ "test_unwind_kprobed_insn:\n"
+ " nopr %%r7\n"
+ :);
+}
+
+static int test_unwind_kprobe(struct unwindme *u)
+{
+ struct kprobe kp;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_KPROBES))
+ kunit_skip(current_test, "requires CONFIG_KPROBES");
+ if (!IS_ENABLED(CONFIG_KPROBES_ON_FTRACE) && u->flags & UWM_KPROBE_ON_FTRACE)
+ kunit_skip(current_test, "requires CONFIG_KPROBES_ON_FTRACE");
+
+ u->ret = -1; /* make sure kprobe is called */
+ unwindme = u;
+ memset(&kp, 0, sizeof(kp));
+ kp.pre_handler = kprobe_pre_handler;
+ kp.addr = u->flags & UWM_KPROBE_ON_FTRACE ?
+ (kprobe_opcode_t *)test_unwind_kprobed_func :
+ (kprobe_opcode_t *)test_unwind_kprobed_insn;
+ ret = register_kprobe(&kp);
+ if (ret < 0) {
+ kunit_err(current_test, "register_kprobe failed %d\n", ret);
+ return -EINVAL;
+ }
+
+ test_unwind_kprobed_func();
+ unregister_kprobe(&kp);
+ unwindme = NULL;
+ return u->ret;
+}
+
static void notrace __used test_unwind_ftrace_handler(unsigned long ip,
unsigned long parent_ip,
struct ftrace_ops *fops,
@@ -212,36 +253,8 @@ static noinline int unwindme_func4(struct unwindme *u)
wait_event(u->task_wq, kthread_should_park());
kthread_parkme();
return 0;
- } else if (u->flags & UWM_PGM) {
- struct kprobe kp;
- int ret;
-
- if (!IS_ENABLED(CONFIG_KPROBES))
- kunit_skip(current_test, "requires CONFIG_KPROBES");
-
- unwindme = u;
- memset(&kp, 0, sizeof(kp));
- kp.symbol_name = "do_report_trap";
- kp.pre_handler = pgm_pre_handler;
- ret = register_kprobe(&kp);
- if (ret < 0) {
- kunit_err(current_test, "register_kprobe failed %d\n", ret);
- return -EINVAL;
- }
-
- /*
- * Trigger operation exception; use insn notation to bypass
- * llvm's integrated assembler sanity checks.
- */
- asm volatile(
- " .insn e,0x0000\n" /* illegal opcode */
- "0: nopr %%r7\n"
- EX_TABLE(0b, 0b)
- :);
-
- unregister_kprobe(&kp);
- unwindme = NULL;
- return u->ret;
+ } else if (u->flags & (UWM_PGM | UWM_KPROBE_ON_FTRACE)) {
+ return test_unwind_kprobe(u);
} else if (u->flags & UWM_FTRACE) {
return test_unwind_ftrace(u);
} else {
@@ -376,6 +389,10 @@ static const struct test_params param_list[] = {
TEST_WITH_FLAGS(UWM_PGM | UWM_SP),
TEST_WITH_FLAGS(UWM_PGM | UWM_REGS),
TEST_WITH_FLAGS(UWM_PGM | UWM_SP | UWM_REGS),
+ TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE),
+ TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_SP),
+ TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_REGS),
+ TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_SP | UWM_REGS),
TEST_WITH_FLAGS(UWM_FTRACE),
TEST_WITH_FLAGS(UWM_FTRACE | UWM_SP),
TEST_WITH_FLAGS(UWM_FTRACE | UWM_REGS),