Subject: [RFC PATCH 12/13] selftests/powerpc: Add DEXCR status utility lsdexcr

From: Benjamin Gray <[email-masked]>
Date: Mon, 28 Nov 2022 13:44:57 +1100

Add a utility 'lsdexcr' to print the current DEXCR status. Useful for
quickly checking the status when debugging test failures, using the
sysctl interfaces manually, or just wanting to check it.

Example output:

          Requested: 84000000 (SBHE, NPHIE)
Hypervisor enforced: 00000000
          Effective: 84000000 (SBHE, NPHIE)

        SBHE * (0): set, prctl editable  	(Speculative branch hint enable)
      IBRTPD   (3): clear, prctl editable  	(Indirect branch recurrent target prediction disable)
       SRAPD   (4): clear, prctl editable  	(Subroutine return address prediction disable)
       NPHIE * (5): set  	(Non-privileged hash instruction enable)

Global SBHE override: 1 (set)

Signed-off-by: Benjamin Gray <[email-masked]>
---
 .../selftests/powerpc/dexcr/.gitignore        |   1 +
 .../testing/selftests/powerpc/dexcr/Makefile  |   2 +
 .../testing/selftests/powerpc/dexcr/lsdexcr.c | 178 ++++++++++++++++++
 3 files changed, 181 insertions(+)
 create mode 100644 tools/testing/selftests/powerpc/dexcr/lsdexcr.c

diff --git a/tools/testing/selftests/powerpc/dexcr/.gitignore b/tools/testing/selftests/powerpc/dexcr/.gitignore
index 035a1fcd8fb3..7dd2fad93732 100644
--- a/tools/testing/selftests/powerpc/dexcr/.gitignore
+++ b/tools/testing/selftests/powerpc/dexcr/.gitignore
@@ -1,2 +1,3 @@
 dexcr_test
 hashchk_user
+lsdexcr
diff --git a/tools/testing/selftests/powerpc/dexcr/Makefile b/tools/testing/selftests/powerpc/dexcr/Makefile
index 9814e72a4afa..8cb732cda7e7 100644
--- a/tools/testing/selftests/powerpc/dexcr/Makefile
+++ b/tools/testing/selftests/powerpc/dexcr/Makefile
@@ -1,4 +1,5 @@
 TEST_GEN_PROGS := dexcr_test hashchk_test
+TEST_GEN_FILES := lsdexcr
 
 TEST_FILES := settings
 top_srcdir = ../../../../..
@@ -7,3 +8,4 @@ include ../../lib.mk
 HASHCHK_TEST_CFLAGS = -no-pie $(call cc-option,-mno-rop-protect)
 
 $(TEST_GEN_PROGS): ../harness.c ../utils.c ./dexcr.c ./cap.c
+$(TEST_GEN_FILES): ../utils.c ./dexcr.c
diff --git a/tools/testing/selftests/powerpc/dexcr/lsdexcr.c b/tools/testing/selftests/powerpc/dexcr/lsdexcr.c
new file mode 100644
index 000000000000..c9f0035f8e2e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/lsdexcr.c
@@ -0,0 +1,178 @@
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include "dexcr.h"
+#include "utils.h"
+
+static unsigned int requested;
+static unsigned int enforced;
+static unsigned int effective;
+
+struct dexcr_aspect {
+	const char *name;
+	const char *desc;
+	unsigned int index;
+	unsigned long pr_val;
+};
+
+static const struct dexcr_aspect aspects[] = {
+	{
+		.name = "SBHE",
+		.desc = "Speculative branch hint enable",
+		.index = 0,
+		.pr_val = PR_PPC_DEXCR_SBHE,
+	},
+	{
+		.name = "IBRTPD",
+		.desc = "Indirect branch recurrent target prediction disable",
+		.index = 3,
+		.pr_val = PR_PPC_DEXCR_IBRTPD,
+	},
+	{
+		.name = "SRAPD",
+		.desc = "Subroutine return address prediction disable",
+		.index = 4,
+		.pr_val = PR_PPC_DEXCR_SRAPD,
+	},
+	{
+		.name = "NPHIE",
+		.desc = "Non-privileged hash instruction enable",
+		.index = 5,
+		.pr_val = PR_PPC_DEXCR_NPHIE,
+	},
+};
+
+#define NUM_ASPECTS (sizeof(aspects) / sizeof(struct dexcr_aspect))
+
+static void print_list(const char *list[], size_t len)
+{
+	for (size_t i = 0; i < len; i++) {
+		printf("%s", list[i]);
+		if (i + 1 < len)
+			printf(", ");
+	}
+}
+
+static void print_dexcr(char *name, unsigned int bits)
+{
+	const char *enabled_aspects[32] = {NULL};
+	size_t j = 0;
+
+	printf("%s: %08x", name, bits);
+
+	if (bits == 0) {
+		printf("\n");
+		return;
+	}
+
+	for (size_t i = 0; i < NUM_ASPECTS; i++) {
+		unsigned int mask = pr_aspect_to_dexcr_mask(aspects[i].pr_val);
+		if (bits & mask) {
+			enabled_aspects[j++] = aspects[i].name;
+			bits &= ~mask;
+		}
+	}
+
+	if (bits)
+		enabled_aspects[j++] = "unknown";
+
+	printf(" (");
+	print_list(enabled_aspects, j);
+	printf(")\n");
+}
+
+static void print_aspect(const struct dexcr_aspect *aspect)
+{
+	const char *attributes[32] = {NULL};
+	size_t j = 0;
+	unsigned long mask;
+	int pr_status;
+
+	/* Kernel-independent info about aspect */
+	mask = pr_aspect_to_dexcr_mask(aspect->pr_val);
+	if (requested & mask)
+		attributes[j++] = "set";
+	if (enforced & mask)
+		attributes[j++] = "hypervisor enforced";
+	if (!(effective & mask))
+		attributes[j++] = "clear";
+
+	/* Kernel understanding of the aspect */
+	pr_status = prctl(PR_PPC_GET_DEXCR, aspect->pr_val, 0, 0, 0);
+	if (pr_status == -1) {
+		switch (errno) {
+		case ENODEV:
+			attributes[j++] = "aspect not present";
+			break;
+		case EINVAL:
+			attributes[j++] = "unrecognised aspect";
+			break;
+		default:
+			attributes[j++] = "unknown kernel error";
+			break;
+		}
+	} else {
+		if (pr_status & PR_PPC_DEXCR_SET_ASPECT)
+			attributes[j++] = "prctl set";
+		if (pr_status & PR_PPC_DEXCR_FORCE_SET_ASPECT)
+			attributes[j++] = "prctl force set";
+		if (pr_status & PR_PPC_DEXCR_CLEAR_ASPECT)
+			attributes[j++] = "prctl clear";
+		if (pr_status & PR_PPC_DEXCR_PRCTL)
+			attributes[j++] = "prctl editable";
+	}
+
+	printf("%12s %c (%d): ", aspect->name, effective & mask ? '*' : ' ', aspect->index);
+	print_list(attributes, j);
+	printf("  \t(%s)\n", aspect->desc);
+}
+
+static void print_overrides(void) {
+	long sbhe;
+	int err;
+
+	printf("Global SBHE override: ");
+	if ((err = read_long(SYSCTL_DEXCR_SBHE, &sbhe, 10))) {
+		printf("error reading " SYSCTL_DEXCR_SBHE ": %d (%s)\n", err, strerror(err));
+	} else {
+		const char *meaning;
+		switch (sbhe) {
+		case -1:
+			meaning = "default";
+			break;
+		case 0:
+			meaning = "clear";
+			break;
+		case 1:
+			meaning = "set";
+			break;
+		default:
+			meaning = "unknown";
+		}
+
+		printf("%ld (%s)\n", sbhe, meaning);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	requested = get_dexcr(UDEXCR);
+	enforced = get_dexcr(ENFORCED);
+	effective = requested | enforced;
+
+	print_dexcr("          Requested", requested);
+	print_dexcr("Hypervisor enforced", enforced);
+	print_dexcr("          Effective", effective);
+	printf("\n");
+
+	for (size_t i = 0; i < NUM_ASPECTS; i++)
+		print_aspect(&aspects[i]);
+	printf("\n");
+
+	print_overrides();
+
+	return 0;
+}
-- 
2.38.1