From f88723a609787254f7645eb6ac261b8363e8a5bc Mon Sep 17 00:00:00 2001 From: Benjamin Gray <bgray@linux.ibm.com> Date: Wed, 17 Apr 2024 21:23:24 +1000 Subject: [PATCH] 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 --- .../selftests/powerpc/dexcr/.gitignore | 1 + .../testing/selftests/powerpc/dexcr/Makefile | 2 +- .../testing/selftests/powerpc/dexcr/chdexcr.c | 112 +++++++++++++++ tools/testing/selftests/powerpc/dexcr/dexcr.h | 47 +++++++ .../testing/selftests/powerpc/dexcr/lsdexcr.c | 130 ++++-------------- 5 files changed, 187 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 5d526613cd26a..11eefb4b9fa4f 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 3b685b28f0292..58cf9f7229051 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 0000000000000..bda44630cada2 --- /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 a6aa7eac11da9..51e9ba3b09979 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 a63db47b66109..7588929180ab8 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); } -- GitLab