summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/powerpc
diff options
context:
space:
mode:
authorBenjamin Gray <bgray@linux.ibm.com>2024-04-17 14:23:24 +0300
committerMichael Ellerman <mpe@ellerman.id.au>2024-05-06 15:05:17 +0300
commitf88723a609787254f7645eb6ac261b8363e8a5bc (patch)
tree9f437b8de5b43c80e3cf8ebf5222e7551773183a /tools/testing/selftests/powerpc
parent9c4866b209ad31cae7c832d45c6137ce6a993ca0 (diff)
downloadlinux-f88723a609787254f7645eb6ac261b8363e8a5bc.tar.xz
selftests/powerpc/dexcr: Add chdexcr utility
Adds a utility to exercise the prctl DEXCR inheritance in the shell. Supports setting and clearing each aspect. Signed-off-by: Benjamin Gray <bgray@linux.ibm.com> [mpe: Use correct SPDX license, use execvp() for usability, print errors] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://msgid.link/20240417112325.728010-9-bgray@linux.ibm.com
Diffstat (limited to 'tools/testing/selftests/powerpc')
-rw-r--r--tools/testing/selftests/powerpc/dexcr/.gitignore1
-rw-r--r--tools/testing/selftests/powerpc/dexcr/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/dexcr/chdexcr.c112
-rw-r--r--tools/testing/selftests/powerpc/dexcr/dexcr.h47
-rw-r--r--tools/testing/selftests/powerpc/dexcr/lsdexcr.c130
5 files changed, 187 insertions, 105 deletions
diff --git a/tools/testing/selftests/powerpc/dexcr/.gitignore b/tools/testing/selftests/powerpc/dexcr/.gitignore
index 5d526613cd26..11eefb4b9fa4 100644
--- a/tools/testing/selftests/powerpc/dexcr/.gitignore
+++ b/tools/testing/selftests/powerpc/dexcr/.gitignore
@@ -1,3 +1,4 @@
dexcr_test
hashchk_test
+chdexcr
lsdexcr
diff --git a/tools/testing/selftests/powerpc/dexcr/Makefile b/tools/testing/selftests/powerpc/dexcr/Makefile
index 3b685b28f029..58cf9f722905 100644
--- a/tools/testing/selftests/powerpc/dexcr/Makefile
+++ b/tools/testing/selftests/powerpc/dexcr/Makefile
@@ -1,5 +1,5 @@
TEST_GEN_PROGS := dexcr_test hashchk_test
-TEST_GEN_FILES := lsdexcr
+TEST_GEN_FILES := lsdexcr chdexcr
include ../../lib.mk
include ../flags.mk
diff --git a/tools/testing/selftests/powerpc/dexcr/chdexcr.c b/tools/testing/selftests/powerpc/dexcr/chdexcr.c
new file mode 100644
index 000000000000..bda44630cada
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/chdexcr.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include "dexcr.h"
+#include "utils.h"
+
+static void die(const char *msg)
+{
+ printf("%s\n", msg);
+ exit(1);
+}
+
+static void help(void)
+{
+ printf("Invoke a provided program with a custom DEXCR on-exec reset value\n"
+ "\n"
+ "usage: chdexcr [CHDEXCR OPTIONS] -- PROGRAM [ARGS...]\n"
+ "\n"
+ "Each configurable DEXCR aspect is exposed as an option.\n"
+ "\n"
+ "The normal option sets the aspect in the DEXCR. The --no- variant\n"
+ "clears that aspect. For example, --ibrtpd sets the IBRTPD aspect bit,\n"
+ "so indirect branch predicition will be disabled in the provided program.\n"
+ "Conversely, --no-ibrtpd clears the aspect bit, so indirect branch\n"
+ "prediction may occur.\n"
+ "\n"
+ "CHDEXCR OPTIONS:\n");
+
+ for (int i = 0; i < ARRAY_SIZE(aspects); i++) {
+ const struct dexcr_aspect *aspect = &aspects[i];
+
+ if (aspect->prctl == -1)
+ continue;
+
+ printf(" --%-6s / --no-%-6s : %s\n", aspect->opt, aspect->opt, aspect->desc);
+ }
+}
+
+static const struct dexcr_aspect *opt_to_aspect(const char *opt)
+{
+ for (int i = 0; i < ARRAY_SIZE(aspects); i++)
+ if (aspects[i].prctl != -1 && !strcmp(aspects[i].opt, opt))
+ return &aspects[i];
+
+ return NULL;
+}
+
+static int apply_option(const char *option)
+{
+ const struct dexcr_aspect *aspect;
+ const char *opt = NULL;
+ const char *set_prefix = "--";
+ const char *clear_prefix = "--no-";
+ unsigned long ctrl = 0;
+ int err;
+
+ if (!strcmp(option, "-h") || !strcmp(option, "--help")) {
+ help();
+ exit(0);
+ }
+
+ /* Strip out --(no-) prefix and determine ctrl value */
+ if (!strncmp(option, clear_prefix, strlen(clear_prefix))) {
+ opt = &option[strlen(clear_prefix)];
+ ctrl |= PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC;
+ } else if (!strncmp(option, set_prefix, strlen(set_prefix))) {
+ opt = &option[strlen(set_prefix)];
+ ctrl |= PR_PPC_DEXCR_CTRL_SET_ONEXEC;
+ }
+
+ if (!opt || !*opt)
+ return 1;
+
+ aspect = opt_to_aspect(opt);
+ if (!aspect)
+ die("unknown aspect");
+
+ err = pr_set_dexcr(aspect->prctl, ctrl);
+ if (err)
+ die("failed to apply option");
+
+ return 0;
+}
+
+int main(int argc, char *const argv[])
+{
+ int i;
+
+ if (!dexcr_exists())
+ die("DEXCR not detected on this hardware");
+
+ for (i = 1; i < argc; i++)
+ if (apply_option(argv[i]))
+ break;
+
+ if (i < argc && !strcmp(argv[i], "--"))
+ i++;
+
+ if (i >= argc)
+ die("missing command");
+
+ execvp(argv[i], &argv[i]);
+ perror("execve");
+
+ return errno;
+}
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.h b/tools/testing/selftests/powerpc/dexcr/dexcr.h
index a6aa7eac11da..51e9ba3b0997 100644
--- a/tools/testing/selftests/powerpc/dexcr/dexcr.h
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.h
@@ -9,6 +9,7 @@
#define _SELFTESTS_POWERPC_DEXCR_DEXCR_H
#include <stdbool.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include "reg.h"
@@ -26,6 +27,52 @@
#define PPC_RAW_HASHCHK(b, i, a) \
str(.long (0x7C0005E4 | PPC_RAW_HASH_ARGS(b, i, a));)
+struct dexcr_aspect {
+ const char *name; /* Short display name */
+ const char *opt; /* Option name for chdexcr */
+ const char *desc; /* Expanded aspect meaning */
+ unsigned int index; /* Aspect bit index in DEXCR */
+ unsigned long prctl; /* 'which' value for get/set prctl */
+};
+
+static const struct dexcr_aspect aspects[] = {
+ {
+ .name = "SBHE",
+ .opt = "sbhe",
+ .desc = "Speculative branch hint enable",
+ .index = 0,
+ .prctl = PR_PPC_DEXCR_SBHE,
+ },
+ {
+ .name = "IBRTPD",
+ .opt = "ibrtpd",
+ .desc = "Indirect branch recurrent target prediction disable",
+ .index = 3,
+ .prctl = PR_PPC_DEXCR_IBRTPD,
+ },
+ {
+ .name = "SRAPD",
+ .opt = "srapd",
+ .desc = "Subroutine return address prediction disable",
+ .index = 4,
+ .prctl = PR_PPC_DEXCR_SRAPD,
+ },
+ {
+ .name = "NPHIE",
+ .opt = "nphie",
+ .desc = "Non-privileged hash instruction enable",
+ .index = 5,
+ .prctl = PR_PPC_DEXCR_NPHIE,
+ },
+ {
+ .name = "PHIE",
+ .opt = "phie",
+ .desc = "Privileged hash instruction enable",
+ .index = 6,
+ .prctl = -1,
+ },
+};
+
bool dexcr_exists(void);
bool pr_dexcr_aspect_supported(unsigned long which);
diff --git a/tools/testing/selftests/powerpc/dexcr/lsdexcr.c b/tools/testing/selftests/powerpc/dexcr/lsdexcr.c
index a63db47b6610..7588929180ab 100644
--- a/tools/testing/selftests/powerpc/dexcr/lsdexcr.c
+++ b/tools/testing/selftests/powerpc/dexcr/lsdexcr.c
@@ -12,52 +12,6 @@ static unsigned int dexcr;
static unsigned int hdexcr;
static unsigned int effective;
-struct dexcr_aspect {
- const char *name;
- const char *desc;
- unsigned int index;
- unsigned long prctl;
- const char *sysctl;
-};
-
-static const struct dexcr_aspect aspects[] = {
- {
- .name = "SBHE",
- .desc = "Speculative branch hint enable",
- .index = 0,
- .prctl = PR_PPC_DEXCR_SBHE,
- .sysctl = "speculative_branch_hint_enable",
- },
- {
- .name = "IBRTPD",
- .desc = "Indirect branch recurrent target prediction disable",
- .index = 3,
- .prctl = PR_PPC_DEXCR_IBRTPD,
- .sysctl = "indirect_branch_recurrent_target_prediction_disable",
- },
- {
- .name = "SRAPD",
- .desc = "Subroutine return address prediction disable",
- .index = 4,
- .prctl = PR_PPC_DEXCR_SRAPD,
- .sysctl = "subroutine_return_address_prediction_disable",
- },
- {
- .name = "NPHIE",
- .desc = "Non-privileged hash instruction enable",
- .index = 5,
- .prctl = PR_PPC_DEXCR_NPHIE,
- .sysctl = "nonprivileged_hash_instruction_enable",
- },
- {
- .name = "PHIE",
- .desc = "Privileged hash instruction enable",
- .index = 6,
- .prctl = -1,
- .sysctl = NULL,
- },
-};
-
static void print_list(const char *list[], size_t len)
{
for (size_t i = 0; i < len; i++) {
@@ -117,89 +71,57 @@ static void print_aspect(const struct dexcr_aspect *aspect)
static void print_aspect_config(const struct dexcr_aspect *aspect)
{
- char sysctl_path[128] = "/proc/sys/kernel/dexcr/";
- const char *reason = "unknown";
+ const char *reason = NULL;
const char *reason_hyp = NULL;
- const char *reason_sysctl = "no sysctl";
const char *reason_prctl = "no prctl";
bool actual = effective & DEXCR_PR_BIT(aspect->index);
- bool expected = false;
-
- long sysctl_ctrl = 0;
- int prctl_ctrl = 0;
- int err;
-
- if (aspect->prctl >= 0) {
- prctl_ctrl = pr_get_dexcr(aspect->prctl);
- if (prctl_ctrl < 0)
- reason_prctl = "(failed to read prctl)";
- else {
- if (prctl_ctrl & PR_PPC_DEXCR_CTRL_SET) {
+ bool expected = actual; /* Assume it's fine if we don't expect a specific set/clear value */
+
+ if (actual)
+ reason = "set by unknown";
+ else
+ reason = "cleared by unknown";
+
+ if (aspect->prctl != -1) {
+ int ctrl = pr_get_dexcr(aspect->prctl);
+
+ if (ctrl < 0) {
+ reason_prctl = "failed to read prctl";
+ } else {
+ if (ctrl & PR_PPC_DEXCR_CTRL_SET) {
reason_prctl = "set by prctl";
expected = true;
- } else if (prctl_ctrl & PR_PPC_DEXCR_CTRL_CLEAR) {
+ } else if (ctrl & PR_PPC_DEXCR_CTRL_CLEAR) {
reason_prctl = "cleared by prctl";
expected = false;
- } else
+ } else {
reason_prctl = "unknown prctl";
+ }
reason = reason_prctl;
}
}
- if (aspect->sysctl) {
- strcat(sysctl_path, aspect->sysctl);
- err = read_long(sysctl_path, &sysctl_ctrl, 10);
- if (err)
- reason_sysctl = "(failed to read sysctl)";
- else {
- switch (sysctl_ctrl) {
- case 0:
- reason_sysctl = "cleared by sysctl";
- reason = reason_sysctl;
- expected = false;
- break;
- case 1:
- reason_sysctl = "set by sysctl";
- reason = reason_sysctl;
- expected = true;
- break;
- case 2:
- reason_sysctl = "not modified by sysctl";
- break;
- case 3:
- reason_sysctl = "cleared by sysctl (permanent)";
- reason = reason_sysctl;
- expected = false;
- break;
- case 4:
- reason_sysctl = "set by sysctl (permanent)";
- reason = reason_sysctl;
- expected = true;
- break;
- default:
- reason_sysctl = "unknown sysctl";
- break;
- }
- }
- }
-
-
if (hdexcr & DEXCR_PR_BIT(aspect->index)) {
reason_hyp = "set by hypervisor";
reason = reason_hyp;
expected = true;
- } else
+ } else {
reason_hyp = "not modified by hypervisor";
+ }
- printf("%12s (%d): %-28s (%s, %s, %s)\n",
+ printf("%12s (%d): %-28s (%s, %s)\n",
aspect->name,
aspect->index,
reason,
reason_hyp,
- reason_sysctl,
reason_prctl);
+ /*
+ * The checks are not atomic, so this can technically trigger if the
+ * hypervisor makes a change while we are checking each source. It's
+ * far more likely to be a bug if we see this though.
+ */
if (actual != expected)
printf(" : ! actual %s does not match config\n", aspect->name);
}