linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: Sandipan Das <sandipan@linux.ibm.com>
To: mpe@ellerman.id.au
Cc: naveen.n.rao@linux.ibm.com, paulus@samba.org,
	linuxppc-dev@lists.ozlabs.org, ravi.bangoria@linux.ibm.com
Subject: [RFC PATCH 3/5] powerpc: sstep: Add instruction emulation selftests
Date: Mon,  4 Feb 2019 09:48:37 +0530	[thread overview]
Message-ID: <196d2330aef453b4eb3cb66febeb79110aadd567.1549253769.git.sandipan@linux.ibm.com> (raw)
In-Reply-To: <cover.1549253769.git.sandipan@linux.ibm.com>

This adds a selftest framework for the in-kernel instruction
emulation infrastructure. This currently does not support the
load/store and branch instructions and is limited to integer
ALU instructions. Support for SPRs is also limited to LR, CR
and XER for now.

Tests run at boot time if CONFIG_DEBUG_KERNEL, CONFIG_PPC64
and CONFIG_EMULATE_STEP_SELFTEST were set before the kernel
build.

When writing the tests, one must not use any instructions
that might overwrite the Stack Pointer (GPR1) or the Thread
Pointer (GPR13).

Signed-off-by: Sandipan Das <sandipan@linux.ibm.com>
---
 arch/powerpc/Kconfig.debug         |   5 +
 arch/powerpc/lib/Makefile          |   1 +
 arch/powerpc/lib/exec_test_instr.S | 150 +++++++++++++++++++++++++++
 arch/powerpc/lib/sstep_tests.c     | 158 +++++++++++++++++++++++++++++
 4 files changed, 314 insertions(+)
 create mode 100644 arch/powerpc/lib/exec_test_instr.S
 create mode 100644 arch/powerpc/lib/sstep_tests.c

diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index f4961fbcb48d..d75c165538e9 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -56,6 +56,11 @@ config CODE_PATCHING_SELFTEST
 	bool "Run self-tests of the code-patching code"
 	depends on DEBUG_KERNEL
 
+config EMULATE_STEP_SELFTEST
+	bool "Run self-tests of the instruction emulation code"
+	depends on PPC64 && DEBUG_KERNEL
+	default n
+
 config JUMP_LABEL_FEATURE_CHECKS
 	bool "Enable use of jump label for cpu/mmu_has_feature()"
 	depends on JUMP_LABEL
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index 3bf9fc6fd36c..c4485e004838 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -31,6 +31,7 @@ obj64-y	+= copypage_64.o copyuser_64.o mem_64.o hweight_64.o \
 obj64-$(CONFIG_SMP)	+= locks.o
 obj64-$(CONFIG_ALTIVEC)	+= vmx-helper.o
 obj64-$(CONFIG_KPROBES_SANITY_TEST) += test_emulate_step.o
+obj64-$(CONFIG_EMULATE_STEP_SELFTEST)	+= exec_test_instr.o sstep_tests.o
 
 obj-y			+= checksum_$(BITS).o checksum_wrappers.o \
 			   string_$(BITS).o memcmp_$(BITS).o
diff --git a/arch/powerpc/lib/exec_test_instr.S b/arch/powerpc/lib/exec_test_instr.S
new file mode 100644
index 000000000000..217e83415eaf
--- /dev/null
+++ b/arch/powerpc/lib/exec_test_instr.S
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Non-emulated single-stepping support limited to basic integer ops for
+ * validating the instruction emulation infrastructure.
+ *
+ * Copyright (C) 2019 IBM Corporation
+ */
+
+#include <asm/asm-offsets.h>
+#include <asm/ppc_asm.h>
+#include <linux/errno.h>
+
+/* int exec_instr(struct pt_regs *regs) */
+_GLOBAL(exec_instr)
+
+	/*
+	 * Stack frame layout (INT_FRAME_SIZE bytes)
+	 *   In-memory pt_regs	(SP + STACK_FRAME_OVERHEAD)
+	 *   Scratch space	(SP + 8)
+	 *   Back chain		(SP + 0)
+	 */
+
+	/*
+	 * Allocate a new stack frame with enough space to hold the register
+	 * states in an in-memory pt_regs and also create the back chain to
+	 * the caller's stack frame.
+	 */
+	stdu	r1, -INT_FRAME_SIZE(r1)
+
+	/*
+	 * Save non-volatile GPRs on stack. This includes TOC pointer (GPR2)
+	 * and local variables (GPR14 to GPR31). The register for the pt_regs
+	 * parameter (GPR3) is saved additionally to ensure that the resulting
+	 * register state can still be saved even if GPR3 gets overwritten
+	 * when loading the initial register state for the test instruction.
+	 * The stack pointer (GPR1) and the thread pointer (GPR13) are not
+	 * saved as these should not be modified anyway.
+	 */
+	SAVE_2GPRS(2, r1)
+	SAVE_NVGPRS(r1)
+
+	/*
+	 * Save LR on stack to ensure that the return address is available
+	 * even if it gets overwritten by the test instruction.
+	 */
+	mflr	r0
+	std	r0, _LINK(r1)
+
+	/*
+	 * Save CR on stack. For simplicity, the entire register is saved
+	 * even though only fields 2 to 4 are non-volatile.
+	 */
+	mfcr	r0
+	std	r0, _CCR(r1)
+
+	/*
+	 * Load register state for the test instruction without touching the
+	 * critical non-volatile registers. The register state is passed as a
+	 * pointer to a pt_regs instance.
+	 */
+	subi	r31, r3, GPR0
+
+	/* Load LR from pt_regs */
+	ld	r0, _LINK(r31)
+	mtlr	r0
+
+	/* Load CR from pt_regs */
+	ld	r0, _CCR(r31)
+	mtcr	r0
+
+	/* Load XER from pt_regs */
+	ld	r0, _XER(r31)
+	mtxer	r0
+
+	/* Load GPRs from pt_regs */
+	REST_GPR(0, r31)
+	REST_10GPRS(2, r31)
+	REST_GPR(12, r31)
+	REST_NVGPRS(r31)
+
+	.global	exec_instr_execute
+exec_instr_execute:
+	/* Placeholder for the test instruction */
+1:	nop
+
+	/*
+	 * Since GPR3 is overwritten, temporarily restore it back to its
+	 * original state, i.e. the pointer to pt_regs, to ensure that the
+	 * resulting register state can be saved. Before doing this, a copy
+	 * of it is created in the scratch space which is used later on to
+	 * save it to pt_regs.
+	 */
+	std	r3, 8(r1)
+	REST_GPR(3, r1)
+
+	/* Save resulting GPR state to pt_regs */
+	subi	r3, r3, GPR0
+	SAVE_GPR(0, r3)
+	SAVE_GPR(2, r3)
+	SAVE_8GPRS(4, r3)
+	SAVE_GPR(12, r3)
+	SAVE_NVGPRS(r3)
+
+	/* Save resulting LR to pt_regs */
+	mflr	r0
+	std	r0, _LINK(r3)
+
+	/* Save resulting CR to pt_regs */
+	mfcr	r0
+	std	r0, _CCR(r3)
+
+	/* Save resulting XER to pt_regs */
+	mfxer	r0
+	std	r0, _XER(r3)
+
+	/* Restore resulting GPR3 from scratch space and save it to pt_regs */
+	ld	r0, 8(r1)
+	std	r0, GPR3(r3)
+
+	/* Set return value to denote execution success */
+	li	r3, 0
+
+	/* Continue */
+	b	3f
+
+	/* Set return value to denote execution failure */
+2:	li	r3, -EFAULT
+
+	/* Restore the non-volatile GPRs from stack */
+3:	REST_GPR(2, r1)
+	REST_NVGPRS(r1)
+
+	/* Restore LR from stack to be able to return */
+	ld	r0, _LINK(r1)
+	mtlr	r0
+
+	/* Restore CR from stack */
+	ld	r0, _CCR(r1)
+	mtcr	r0
+
+	/* Tear down stack frame */
+	addi	r1, r1, INT_FRAME_SIZE
+
+	/* Return */
+	blr
+
+	/* Setup exception table */
+	EX_TABLE(1b, 2b)
+
+_ASM_NOKPROBE_SYMBOL(exec_instr)
diff --git a/arch/powerpc/lib/sstep_tests.c b/arch/powerpc/lib/sstep_tests.c
new file mode 100644
index 000000000000..a610c778044d
--- /dev/null
+++ b/arch/powerpc/lib/sstep_tests.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Selftests for the instruction emulation infrastructure.
+ *
+ * Copyright (C) 2019 IBM Corporation
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+
+#include <asm/code-patching.h>
+#include <asm/ppc-opcode.h>
+#include <asm/sstep.h>
+
+#define MAX_SUBTESTS	16
+#define MAX_INSNS	32
+
+#define PASS	1
+#define FAIL	0
+
+#define IGNORE_GPR(n)	(0x1UL << (n))
+#define IGNORE_XER	(0x1UL << 32)
+#define IGNORE_CCR	(0x1UL << 33)
+
+struct sstep_test {
+	const char *mnemonic;
+	struct {
+		const char *descr;
+		unsigned long flags;
+		unsigned int instr;
+		struct pt_regs regs;
+	} subtests[MAX_SUBTESTS + 1];
+};
+
+static struct sstep_test tests[] = {
+	{
+		.mnemonic = "nop",
+		.subtests =
+		{
+			{
+				.descr = "R0 = LONG_MAX",
+				.instr = PPC_INST_NOP | ___PPC_RA(0) | ___PPC_RS(0) | __PPC_UI(0x0),
+				.regs =
+				{
+					.gpr[0] = LONG_MAX,
+				}
+			}
+		}
+	},
+};
+
+int emulate_instr(struct pt_regs *regs, unsigned int instr)
+{
+	struct instruction_op op;
+
+	if (!regs || !instr)
+		return -EINVAL;
+
+	if (analyse_instr(&op, regs, instr) != 1 || GETTYPE(op.type) != COMPUTE) {
+		pr_info("emulation failed or not supported, opcode = 0x%08x\n", instr);
+		return -EFAULT;
+	}
+
+	emulate_update_regs(regs, &op);
+	return 0;
+}
+
+int execute_instr(struct pt_regs *regs, unsigned int instr)
+{
+	extern unsigned int exec_instr_execute[];
+	extern int exec_instr(struct pt_regs *regs);
+
+	if (!regs || !instr)
+		return -EINVAL;
+
+	/* Patch the NOP with the actual instruction */
+	patch_instruction(&exec_instr_execute[0], instr);
+	if (exec_instr(regs)) {
+		pr_info("execution failed, opcode = 0x%08x\n", instr);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int __init run_sstep_tests(void)
+{
+	bool ignore_gpr;
+	bool ignore_xer;
+	bool ignore_ccr;
+	unsigned int instr;
+	unsigned long flags;
+	struct pt_regs a, b;
+	const char *mnemonic, *descr;
+	unsigned int i, j, k, result;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		mnemonic = tests[i].mnemonic;
+
+		for (j = 0; j < MAX_SUBTESTS && tests[i].subtests[j].descr; j++) {
+			descr = tests[i].subtests[j].descr;
+			instr = tests[i].subtests[j].instr;
+			flags = tests[i].subtests[j].flags;
+			ignore_xer = flags & IGNORE_XER;
+			ignore_ccr = flags & IGNORE_CCR;
+			result = PASS;
+
+			memcpy(&a, &tests[i].subtests[j].regs, sizeof(struct pt_regs));
+			memcpy(&b, &tests[i].subtests[j].regs, sizeof(struct pt_regs));
+
+			/*
+			 * Set a compatible MSR value explicitly to ensure
+			 * that XER and CR bits are updated appropriately
+			 */
+			a.msr = b.msr = MSR_KERNEL;
+
+			if (emulate_instr(&a, instr) || execute_instr(&b, instr)) {
+				result = FAIL;
+				goto print;
+			}
+
+			/* Verify GPR values */
+			for (k = 0; k < 32; k++) {
+				ignore_gpr = flags & IGNORE_GPR(k);
+				if (!ignore_gpr && a.gpr[k] != b.gpr[k]) {
+					result = FAIL;
+					pr_info("GPR%u got = 0x%016lx, exp = 0x%016lx\n", k, a.gpr[k], b.gpr[k]);
+				}
+			}
+
+			/* Verify LR value */
+			if (a.link != b.link) {
+				result = FAIL;
+				pr_info("LR got = 0x%016lx, exp = 0x%016lx\n", a.link, b.link);
+			}
+
+			/* Verify XER value */
+			if (!ignore_xer && a.xer != b.xer) {
+				result = FAIL;
+				pr_info("XER got = 0x%016lx, exp = 0x%016lx\n", a.xer, b.xer);
+			}
+
+			/* Verify CR value */
+			if (!ignore_ccr && a.ccr != b.ccr) {
+				result = FAIL;
+				pr_info("CR got = 0x%08lx, exp = 0x%08lx\n", a.ccr, b.ccr);
+			}
+
+print:
+			pr_info("%-8s: %-50s [%s]", mnemonic, descr, result ? "PASS" : "FAIL");
+		}
+	}
+
+	return 0;
+}
+late_initcall(run_sstep_tests);
-- 
2.19.2


  parent reply	other threads:[~2019-02-04  4:24 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-04  4:18 [RFC PATCH 0/5] powerpc: sstep: Emulation test infrastructure Sandipan Das
2019-02-04  4:18 ` [RFC PATCH 1/5] powerpc: Add bitmasks for D-form instruction fields Sandipan Das
2019-02-04  4:18 ` [RFC PATCH 2/5] powerpc: Add bitmask for Rc instruction field Sandipan Das
2019-02-04  4:18 ` Sandipan Das [this message]
2019-02-11  0:47   ` [RFC PATCH 3/5] powerpc: sstep: Add instruction emulation selftests Daniel Axtens
2019-02-11 10:15     ` Sandipan Das
2019-02-04  4:18 ` [RFC PATCH 4/5] powerpc: sstep: Add selftests for add[.] instruction Sandipan Das
2019-02-04  4:18 ` [RFC PATCH 5/5] powerpc: sstep: Add selftests for addc[.] instruction Sandipan Das
2019-02-11  1:00   ` Daniel Axtens
2019-02-11 10:14     ` Sandipan Das
2019-02-11  1:01 ` [RFC PATCH 0/5] powerpc: sstep: Emulation test infrastructure Daniel Axtens

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=196d2330aef453b4eb3cb66febeb79110aadd567.1549253769.git.sandipan@linux.ibm.com \
    --to=sandipan@linux.ibm.com \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=mpe@ellerman.id.au \
    --cc=naveen.n.rao@linux.ibm.com \
    --cc=paulus@samba.org \
    --cc=ravi.bangoria@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).