Adds a utility to exercise the prctl DEXCR inheritance in the shell.
Supports setting and clearing each aspect.

Signed-off-by: Benjamin Gray <bg...@linux.ibm.com>
---
 .../selftests/powerpc/dexcr/.gitignore        |   1 +
 .../testing/selftests/powerpc/dexcr/Makefile  |   2 +-
 .../testing/selftests/powerpc/dexcr/chdexcr.c | 110 +++++++++++++++
 tools/testing/selftests/powerpc/dexcr/dexcr.h |  47 +++++++
 .../testing/selftests/powerpc/dexcr/lsdexcr.c | 130 ++++--------------
 5 files changed, 185 insertions(+), 105 deletions(-)
 create mode 100644 tools/testing/selftests/powerpc/dexcr/chdexcr.c

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 076943193c07..6a89e88ef7b2 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
 
diff --git a/tools/testing/selftests/powerpc/dexcr/chdexcr.c 
b/tools/testing/selftests/powerpc/dexcr/chdexcr.c
new file mode 100644
index 000000000000..217187a83224
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/chdexcr.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#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[], char *const envp[])
+{
+       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");
+
+       execve(argv[i], &argv[i], envp);
+       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);
 }
-- 
2.44.0

Reply via email to