selftests/powerpc/dexcr: Add chdexcr utility
authorBenjamin Gray <bgray@linux.ibm.com>
Wed, 17 Apr 2024 11:23:24 +0000 (21:23 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 6 May 2024 12:05:17 +0000 (22:05 +1000)
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
tools/testing/selftests/powerpc/dexcr/.gitignore
tools/testing/selftests/powerpc/dexcr/Makefile
tools/testing/selftests/powerpc/dexcr/chdexcr.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dexcr/dexcr.h
tools/testing/selftests/powerpc/dexcr/lsdexcr.c

index 5d526613cd26a11a23fa49a4f458121c3632bbe3..11eefb4b9fa4fd800cce7771ba30453041708c70 100644 (file)
@@ -1,3 +1,4 @@
 dexcr_test
 hashchk_test
+chdexcr
 lsdexcr
index 3b685b28f029292e975cc41774c1adca3f089f1e..58cf9f7229051f381cd021f1bef6acfb046cb8fd 100644 (file)
@@ -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 (file)
index 0000000..bda4463
--- /dev/null
@@ -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;
+}
index a6aa7eac11da950817475f012f03b96286acf520..51e9ba3b09979d61ba84f2fcdd059fd7b896af3c 100644 (file)
@@ -9,6 +9,7 @@
 #define _SELFTESTS_POWERPC_DEXCR_DEXCR_H
 
 #include <stdbool.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 
 #include "reg.h"
 #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);
index a63db47b661098781d967a9b44ab5dafdd1ac375..7588929180ab8f020665150c86b4928eac101dc1 100644 (file)
@@ -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);
 }