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
This commit is contained in:
Benjamin Gray 2024-04-17 21:23:24 +10:00 committed by Michael Ellerman
parent 9c4866b209
commit f88723a609
5 changed files with 185 additions and 103 deletions

View file

@ -1,3 +1,4 @@
dexcr_test
hashchk_test
chdexcr
lsdexcr

View 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

View file

@ -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;
}

View 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"
@ -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);

View 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;
bool expected = actual; /* Assume it's fine if we don't expect a specific set/clear value */
long sysctl_ctrl = 0;
int prctl_ctrl = 0;
int err;
if (actual)
reason = "set by unknown";
else
reason = "cleared by unknown";
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) {
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);
}