mirror of
https://github.com/torvalds/linux
synced 2024-07-23 03:29:48 +00:00
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:
parent
9c4866b209
commit
f88723a609
|
@ -1,3 +1,4 @@
|
|||
dexcr_test
|
||||
hashchk_test
|
||||
chdexcr
|
||||
lsdexcr
|
||||
|
|
|
@ -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
|
||||
|
|
112
tools/testing/selftests/powerpc/dexcr/chdexcr.c
Normal file
112
tools/testing/selftests/powerpc/dexcr/chdexcr.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue