Merge tag 'powerpc-5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc updates from Michael Ellerman: - Add support for (optionally) using queued spinlocks & rwlocks. - Support for a new faster system call ABI using the scv instruction on Power9 or later. - Drop support for the PROT_SAO mmap/mprotect flag as it will be unsupported on Power10 and future processors, leaving us with no way to implement the functionality it requests. This risks breaking userspace, though we believe it is unused in practice. - A bug fix for, and then the removal of, our custom stack expansion checking. We now allow stack expansion up to the rlimit, like other architectures. - Remove the remnants of our (previously disabled) topology update code, which tried to react to NUMA layout changes on virtualised systems, but was prone to crashes and other problems. - Add PMU support for Power10 CPUs. - A change to our signal trampoline so that we don't unbalance the link stack (branch return predictor) in the signal delivery path. - Lots of other cleanups, refactorings, smaller features and so on as usual. Thanks to: Abhishek Goel, Alastair D'Silva, Alexander A. Klimov, Alexey Kardashevskiy, Alistair Popple, Andrew Donnellan, Aneesh Kumar K.V, Anju T Sudhakar, Anton Blanchard, Arnd Bergmann, Athira Rajeev, Balamuruhan S, Bharata B Rao, Bill Wendling, Bin Meng, Cédric Le Goater, Chris Packham, Christophe Leroy, Christoph Hellwig, Daniel Axtens, Dan Williams, David Lamparter, Desnes A. Nunes do Rosario, Erhard F., Finn Thain, Frederic Barrat, Ganesh Goudar, Gautham R. Shenoy, Geoff Levand, Greg Kurz, Gustavo A. R. Silva, Hari Bathini, Harish, Imre Kaloz, Joel Stanley, Joe Perches, John Crispin, Jordan Niethe, Kajol Jain, Kamalesh Babulal, Kees Cook, Laurent Dufour, Leonardo Bras, Li RongQing, Madhavan Srinivasan, Mahesh Salgaonkar, Mark Cave-Ayland, Michal Suchanek, Milton Miller, Mimi Zohar, Murilo Opsfelder Araujo, Nathan Chancellor, Nathan Lynch, Naveen N. Rao, Nayna Jain, Nicholas Piggin, Oliver O'Halloran, Palmer Dabbelt, Pedro Miraglia Franco de Carvalho, Philippe Bergheaud, Pingfan Liu, Pratik Rajesh Sampat, Qian Cai, Qinglang Miao, Randy Dunlap, Ravi Bangoria, Sachin Sant, Sam Bobroff, Sandipan Das, Santosh Sivaraj, Satheesh Rajendran, Shirisha Ganta, Sourabh Jain, Srikar Dronamraju, Stan Johnson, Stephen Rothwell, Thadeu Lima de Souza Cascardo, Thiago Jung Bauermann, Tom Lane, Vaibhav Jain, Vladis Dronov, Wei Yongjun, Wen Xiong, YueHaibing. * tag 'powerpc-5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (337 commits) selftests/powerpc: Fix pkey syscall redefinitions powerpc: Fix circular dependency between percpu.h and mmu.h powerpc/powernv/sriov: Fix use of uninitialised variable selftests/powerpc: Skip vmx/vsx/tar/etc tests on older CPUs powerpc/40x: Fix assembler warning about r0 powerpc/papr_scm: Add support for fetching nvdimm 'fuel-gauge' metric powerpc/papr_scm: Fetch nvdimm performance stats from PHYP cpuidle: pseries: Fixup exit latency for CEDE(0) cpuidle: pseries: Add function to parse extended CEDE records cpuidle: pseries: Set the latency-hint before entering CEDE selftests/powerpc: Fix online CPU selection powerpc/perf: Consolidate perf_callchain_user_[64|32]() powerpc/pseries/hotplug-cpu: Remove double free in error path powerpc/pseries/mobility: Add pr_debug() for device tree changes powerpc/pseries/mobility: Set pr_fmt() powerpc/cacheinfo: Warn if cache object chain becomes unordered powerpc/cacheinfo: Improve diagnostics about malformed cache lists powerpc/cacheinfo: Use name@unit instead of full DT path in debug messages powerpc/cacheinfo: Set pr_fmt() powerpc: fix function annotations to avoid section mismatch warnings with gcc-10 ...
Tento commit je obsažen v:
@@ -640,6 +640,11 @@ struct kvm_ppc_cpu_char {
|
||||
#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf)
|
||||
#define KVM_REG_PPC_PTCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc0)
|
||||
|
||||
/* POWER10 registers */
|
||||
#define KVM_REG_PPC_MMCR3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc1)
|
||||
#define KVM_REG_PPC_SIER2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc2)
|
||||
#define KVM_REG_PPC_SIER3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc3)
|
||||
|
||||
/* Transactional Memory checkpointed state:
|
||||
* This is all GPRs, all VSX regs and a subset of SPRs
|
||||
*/
|
||||
|
@@ -84,7 +84,7 @@
|
||||
{0x1a4, "H_CREATE_RPT"}, \
|
||||
{0x1a8, "H_REMOVE_RPT"}, \
|
||||
{0x1ac, "H_REGISTER_RPAGES"}, \
|
||||
{0x1b0, "H_DISABLE_AND_GETC"}, \
|
||||
{0x1b0, "H_DISABLE_AND_GET"}, \
|
||||
{0x1b4, "H_ERROR_DATA"}, \
|
||||
{0x1b8, "H_GET_HCA_INFO"}, \
|
||||
{0x1bc, "H_GET_PERF_COUNT"}, \
|
||||
|
@@ -9,7 +9,17 @@
|
||||
* This selftest exercises the powerpc alignment fault handler.
|
||||
*
|
||||
* We create two sets of source and destination buffers, one in regular memory,
|
||||
* the other cache-inhibited (we use /dev/fb0 for this).
|
||||
* the other cache-inhibited (by default we use /dev/fb0 for this, but an
|
||||
* alterative path for cache-inhibited memory may be provided).
|
||||
*
|
||||
* One way to get cache-inhibited memory is to use the "mem" kernel parameter
|
||||
* to limit the kernel to less memory than actually exists. Addresses above
|
||||
* the limit may still be accessed but will be treated as cache-inhibited. For
|
||||
* example, if there is actually 4GB of memory and the parameter "mem=3GB" is
|
||||
* used, memory from address 0xC0000000 onwards is treated as cache-inhibited.
|
||||
* To access this region /dev/mem is used. The kernel should be configured
|
||||
* without CONFIG_STRICT_DEVMEM. In this case use:
|
||||
* ./alignment_handler /dev/mem 0xc0000000
|
||||
*
|
||||
* We initialise the source buffers, then use whichever set of load/store
|
||||
* instructions is under test to copy bytes from the source buffers to the
|
||||
@@ -48,11 +58,14 @@
|
||||
#include <asm/cputable.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "instructions.h"
|
||||
|
||||
int bufsize;
|
||||
int debug;
|
||||
int testing;
|
||||
volatile int gotsig;
|
||||
char *cipath = "/dev/fb0";
|
||||
long cioffset;
|
||||
|
||||
void sighandler(int sig, siginfo_t *info, void *ctx)
|
||||
{
|
||||
@@ -84,6 +97,17 @@ void sighandler(int sig, siginfo_t *info, void *ctx)
|
||||
} \
|
||||
rc |= do_test(#name, test_##name)
|
||||
|
||||
#define TESTP(name, ld_op, st_op, ld_reg, st_reg) \
|
||||
void test_##name(char *s, char *d) \
|
||||
{ \
|
||||
asm volatile( \
|
||||
ld_op(ld_reg, %0, 0, 0) \
|
||||
st_op(st_reg, %1, 0, 0) \
|
||||
:: "r"(s), "r"(d), "r"(0) \
|
||||
: "memory", "vs0", "vs32", "r31"); \
|
||||
} \
|
||||
rc |= do_test(#name, test_##name)
|
||||
|
||||
#define LOAD_VSX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 32, 32)
|
||||
#define STORE_VSX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 32)
|
||||
#define LOAD_VSX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 32, 32)
|
||||
@@ -103,6 +127,17 @@ void sighandler(int sig, siginfo_t *info, void *ctx)
|
||||
#define LOAD_FLOAT_XFORM_TEST(op) TEST(op, op, stfdx, XFORM, 0, 0)
|
||||
#define STORE_FLOAT_XFORM_TEST(op) TEST(op, lfdx, op, XFORM, 0, 0)
|
||||
|
||||
#define LOAD_MLS_PREFIX_TEST(op) TESTP(op, op, PSTD, 31, 31)
|
||||
#define STORE_MLS_PREFIX_TEST(op) TESTP(op, PLD, op, 31, 31)
|
||||
|
||||
#define LOAD_8LS_PREFIX_TEST(op) TESTP(op, op, PSTD, 31, 31)
|
||||
#define STORE_8LS_PREFIX_TEST(op) TESTP(op, PLD, op, 31, 31)
|
||||
|
||||
#define LOAD_FLOAT_MLS_PREFIX_TEST(op) TESTP(op, op, PSTFD, 0, 0)
|
||||
#define STORE_FLOAT_MLS_PREFIX_TEST(op) TESTP(op, PLFD, op, 0, 0)
|
||||
|
||||
#define LOAD_VSX_8LS_PREFIX_TEST(op, tail) TESTP(op, op, PSTXV ## tail, 0, 32)
|
||||
#define STORE_VSX_8LS_PREFIX_TEST(op, tail) TESTP(op, PLXV ## tail, op, 32, 0)
|
||||
|
||||
/* FIXME: Unimplemented tests: */
|
||||
// STORE_DFORM_TEST(stq) /* FIXME: need two registers for quad */
|
||||
@@ -195,17 +230,18 @@ int do_test(char *test_name, void (*test_func)(char *, char *))
|
||||
|
||||
printf("\tDoing %s:\t", test_name);
|
||||
|
||||
fd = open("/dev/fb0", O_RDWR);
|
||||
fd = open(cipath, O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("\n");
|
||||
perror("Can't open /dev/fb0 now?");
|
||||
perror("Can't open ci file now?");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ci0 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED,
|
||||
fd, 0x0);
|
||||
ci1 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED,
|
||||
fd, bufsize);
|
||||
ci0 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED,
|
||||
fd, cioffset);
|
||||
ci1 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED,
|
||||
fd, cioffset + bufsize);
|
||||
|
||||
if ((ci0 == MAP_FAILED) || (ci1 == MAP_FAILED)) {
|
||||
printf("\n");
|
||||
perror("mmap failed");
|
||||
@@ -270,11 +306,11 @@ int do_test(char *test_name, void (*test_func)(char *, char *))
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool can_open_fb0(void)
|
||||
static bool can_open_cifile(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/fb0", O_RDWR);
|
||||
fd = open(cipath, O_RDWR);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
@@ -286,7 +322,7 @@ int test_alignment_handler_vsx_206(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
|
||||
|
||||
printf("VSX: 2.06B\n");
|
||||
@@ -304,7 +340,7 @@ int test_alignment_handler_vsx_207(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
printf("VSX: 2.07B\n");
|
||||
@@ -320,7 +356,7 @@ int test_alignment_handler_vsx_300(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));
|
||||
printf("VSX: 3.00B\n");
|
||||
@@ -348,11 +384,30 @@ int test_alignment_handler_vsx_300(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_vsx_prefix(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
|
||||
|
||||
printf("VSX: PREFIX\n");
|
||||
LOAD_VSX_8LS_PREFIX_TEST(PLXSD, 0);
|
||||
LOAD_VSX_8LS_PREFIX_TEST(PLXSSP, 0);
|
||||
LOAD_VSX_8LS_PREFIX_TEST(PLXV0, 0);
|
||||
LOAD_VSX_8LS_PREFIX_TEST(PLXV1, 1);
|
||||
STORE_VSX_8LS_PREFIX_TEST(PSTXSD, 0);
|
||||
STORE_VSX_8LS_PREFIX_TEST(PSTXSSP, 0);
|
||||
STORE_VSX_8LS_PREFIX_TEST(PSTXV0, 0);
|
||||
STORE_VSX_8LS_PREFIX_TEST(PSTXV1, 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_integer(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
|
||||
printf("Integer\n");
|
||||
LOAD_DFORM_TEST(lbz);
|
||||
@@ -408,7 +463,7 @@ int test_alignment_handler_integer_206(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
|
||||
|
||||
printf("Integer: 2.06\n");
|
||||
@@ -419,11 +474,32 @@ int test_alignment_handler_integer_206(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_integer_prefix(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
|
||||
|
||||
printf("Integer: PREFIX\n");
|
||||
LOAD_MLS_PREFIX_TEST(PLBZ);
|
||||
LOAD_MLS_PREFIX_TEST(PLHZ);
|
||||
LOAD_MLS_PREFIX_TEST(PLHA);
|
||||
LOAD_MLS_PREFIX_TEST(PLWZ);
|
||||
LOAD_8LS_PREFIX_TEST(PLWA);
|
||||
LOAD_8LS_PREFIX_TEST(PLD);
|
||||
STORE_MLS_PREFIX_TEST(PSTB);
|
||||
STORE_MLS_PREFIX_TEST(PSTH);
|
||||
STORE_MLS_PREFIX_TEST(PSTW);
|
||||
STORE_8LS_PREFIX_TEST(PSTD);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_vmx(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_ALTIVEC));
|
||||
|
||||
printf("VMX\n");
|
||||
@@ -451,7 +527,7 @@ int test_alignment_handler_fp(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
|
||||
printf("Floating point\n");
|
||||
LOAD_FLOAT_DFORM_TEST(lfd);
|
||||
@@ -479,7 +555,7 @@ int test_alignment_handler_fp_205(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_05));
|
||||
|
||||
printf("Floating point: 2.05\n");
|
||||
@@ -497,7 +573,7 @@ int test_alignment_handler_fp_206(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_fb0());
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
|
||||
|
||||
printf("Floating point: 2.06\n");
|
||||
@@ -507,13 +583,32 @@ int test_alignment_handler_fp_206(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int test_alignment_handler_fp_prefix(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!can_open_cifile());
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1));
|
||||
|
||||
printf("Floating point: PREFIX\n");
|
||||
LOAD_FLOAT_DFORM_TEST(lfs);
|
||||
LOAD_FLOAT_MLS_PREFIX_TEST(PLFS);
|
||||
LOAD_FLOAT_MLS_PREFIX_TEST(PLFD);
|
||||
STORE_FLOAT_MLS_PREFIX_TEST(PSTFS);
|
||||
STORE_FLOAT_MLS_PREFIX_TEST(PSTFD);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void usage(char *prog)
|
||||
{
|
||||
printf("Usage: %s [options]\n", prog);
|
||||
printf("Usage: %s [options] [path [offset]]\n", prog);
|
||||
printf(" -d Enable debug error output\n");
|
||||
printf("\n");
|
||||
printf("This test requires a POWER8 or POWER9 CPU and a usable ");
|
||||
printf("framebuffer at /dev/fb0.\n");
|
||||
printf("This test requires a POWER8, POWER9 or POWER10 CPU ");
|
||||
printf("and either a usable framebuffer at /dev/fb0 or ");
|
||||
printf("the path to usable cache inhibited memory and optional ");
|
||||
printf("offset to be provided\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -533,6 +628,13 @@ int main(int argc, char *argv[])
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc > 0)
|
||||
cipath = argv[0];
|
||||
if (argc > 1)
|
||||
cioffset = strtol(argv[1], 0, 0x10);
|
||||
|
||||
bufsize = getpagesize();
|
||||
|
||||
@@ -552,10 +654,14 @@ int main(int argc, char *argv[])
|
||||
"test_alignment_handler_vsx_207");
|
||||
rc |= test_harness(test_alignment_handler_vsx_300,
|
||||
"test_alignment_handler_vsx_300");
|
||||
rc |= test_harness(test_alignment_handler_vsx_prefix,
|
||||
"test_alignment_handler_vsx_prefix");
|
||||
rc |= test_harness(test_alignment_handler_integer,
|
||||
"test_alignment_handler_integer");
|
||||
rc |= test_harness(test_alignment_handler_integer_206,
|
||||
"test_alignment_handler_integer_206");
|
||||
rc |= test_harness(test_alignment_handler_integer_prefix,
|
||||
"test_alignment_handler_integer_prefix");
|
||||
rc |= test_harness(test_alignment_handler_vmx,
|
||||
"test_alignment_handler_vmx");
|
||||
rc |= test_harness(test_alignment_handler_fp,
|
||||
@@ -564,5 +670,7 @@ int main(int argc, char *argv[])
|
||||
"test_alignment_handler_fp_205");
|
||||
rc |= test_harness(test_alignment_handler_fp_206,
|
||||
"test_alignment_handler_fp_206");
|
||||
rc |= test_harness(test_alignment_handler_fp_prefix,
|
||||
"test_alignment_handler_fp_prefix");
|
||||
return rc;
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <limits.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/shm.h>
|
||||
#include <linux/futex.h>
|
||||
@@ -104,8 +105,9 @@ static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu)
|
||||
|
||||
static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu)
|
||||
{
|
||||
int pid;
|
||||
cpu_set_t cpuset;
|
||||
int pid, ncpus;
|
||||
cpu_set_t *cpuset;
|
||||
size_t size;
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
@@ -116,14 +118,23 @@ static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu)
|
||||
if (pid)
|
||||
return;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpu, &cpuset);
|
||||
ncpus = get_nprocs();
|
||||
size = CPU_ALLOC_SIZE(ncpus);
|
||||
cpuset = CPU_ALLOC(ncpus);
|
||||
if (!cpuset) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
CPU_ZERO_S(size, cpuset);
|
||||
CPU_SET_S(cpu, size, cpuset);
|
||||
|
||||
if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {
|
||||
if (sched_setaffinity(0, size, cpuset)) {
|
||||
perror("sched_setaffinity");
|
||||
CPU_FREE(cpuset);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
CPU_FREE(cpuset);
|
||||
fn(arg);
|
||||
|
||||
exit(0);
|
||||
|
@@ -5,12 +5,17 @@ pe_ok() {
|
||||
local dev="$1"
|
||||
local path="/sys/bus/pci/devices/$dev/eeh_pe_state"
|
||||
|
||||
if ! [ -e "$path" ] ; then
|
||||
# if a driver doesn't support the error handling callbacks then the
|
||||
# device is recovered by removing and re-probing it. This causes the
|
||||
# sysfs directory to disappear so read the PE state once and squash
|
||||
# any potential error messages
|
||||
local eeh_state="$(cat $path 2>/dev/null)"
|
||||
if [ -z "$eeh_state" ]; then
|
||||
return 1;
|
||||
fi
|
||||
|
||||
local fw_state="$(cut -d' ' -f1 < $path)"
|
||||
local sw_state="$(cut -d' ' -f2 < $path)"
|
||||
local fw_state="$(echo $eeh_state | cut -d' ' -f1)"
|
||||
local sw_state="$(echo $eeh_state | cut -d' ' -f2)"
|
||||
|
||||
# If EEH_PE_ISOLATED or EEH_PE_RECOVERING are set then the PE is in an
|
||||
# error state or being recovered. Either way, not ok.
|
||||
|
@@ -66,4 +66,81 @@ static inline int paste_last(void *i)
|
||||
#define PPC_INST_PASTE __PASTE(0, 0, 0, 0)
|
||||
#define PPC_INST_PASTE_LAST __PASTE(0, 0, 1, 1)
|
||||
|
||||
/* This defines the prefixed load/store instructions */
|
||||
#ifdef __ASSEMBLY__
|
||||
# define stringify_in_c(...) __VA_ARGS__
|
||||
#else
|
||||
# define __stringify_in_c(...) #__VA_ARGS__
|
||||
# define stringify_in_c(...) __stringify_in_c(__VA_ARGS__) " "
|
||||
#endif
|
||||
|
||||
#define __PPC_RA(a) (((a) & 0x1f) << 16)
|
||||
#define __PPC_RS(s) (((s) & 0x1f) << 21)
|
||||
#define __PPC_RT(t) __PPC_RS(t)
|
||||
#define __PPC_PREFIX_R(r) (((r) & 0x1) << 20)
|
||||
|
||||
#define PPC_PREFIX_MLS 0x06000000
|
||||
#define PPC_PREFIX_8LS 0x04000000
|
||||
|
||||
#define PPC_INST_LBZ 0x88000000
|
||||
#define PPC_INST_LHZ 0xa0000000
|
||||
#define PPC_INST_LHA 0xa8000000
|
||||
#define PPC_INST_LWZ 0x80000000
|
||||
#define PPC_INST_STB 0x98000000
|
||||
#define PPC_INST_STH 0xb0000000
|
||||
#define PPC_INST_STW 0x90000000
|
||||
#define PPC_INST_STD 0xf8000000
|
||||
#define PPC_INST_LFS 0xc0000000
|
||||
#define PPC_INST_LFD 0xc8000000
|
||||
#define PPC_INST_STFS 0xd0000000
|
||||
#define PPC_INST_STFD 0xd8000000
|
||||
|
||||
#define PREFIX_MLS(instr, t, a, r, d) stringify_in_c(.balign 64, , 4;) \
|
||||
stringify_in_c(.long PPC_PREFIX_MLS | \
|
||||
__PPC_PREFIX_R(r) | \
|
||||
(((d) >> 16) & 0x3ffff);) \
|
||||
stringify_in_c(.long (instr) | \
|
||||
__PPC_RT(t) | \
|
||||
__PPC_RA(a) | \
|
||||
((d) & 0xffff);\n)
|
||||
|
||||
#define PREFIX_8LS(instr, t, a, r, d) stringify_in_c(.balign 64, , 4;) \
|
||||
stringify_in_c(.long PPC_PREFIX_8LS | \
|
||||
__PPC_PREFIX_R(r) | \
|
||||
(((d) >> 16) & 0x3ffff);) \
|
||||
stringify_in_c(.long (instr) | \
|
||||
__PPC_RT(t) | \
|
||||
__PPC_RA(a) | \
|
||||
((d) & 0xffff);\n)
|
||||
|
||||
/* Prefixed Integer Load/Store instructions */
|
||||
#define PLBZ(t, a, r, d) PREFIX_MLS(PPC_INST_LBZ, t, a, r, d)
|
||||
#define PLHZ(t, a, r, d) PREFIX_MLS(PPC_INST_LHZ, t, a, r, d)
|
||||
#define PLHA(t, a, r, d) PREFIX_MLS(PPC_INST_LHA, t, a, r, d)
|
||||
#define PLWZ(t, a, r, d) PREFIX_MLS(PPC_INST_LWZ, t, a, r, d)
|
||||
#define PLWA(t, a, r, d) PREFIX_8LS(0xa4000000, t, a, r, d)
|
||||
#define PLD(t, a, r, d) PREFIX_8LS(0xe4000000, t, a, r, d)
|
||||
#define PLQ(t, a, r, d) PREFIX_8LS(0xe0000000, t, a, r, d)
|
||||
#define PSTB(s, a, r, d) PREFIX_MLS(PPC_INST_STB, s, a, r, d)
|
||||
#define PSTH(s, a, r, d) PREFIX_MLS(PPC_INST_STH, s, a, r, d)
|
||||
#define PSTW(s, a, r, d) PREFIX_MLS(PPC_INST_STW, s, a, r, d)
|
||||
#define PSTD(s, a, r, d) PREFIX_8LS(0xf4000000, s, a, r, d)
|
||||
#define PSTQ(s, a, r, d) PREFIX_8LS(0xf0000000, s, a, r, d)
|
||||
|
||||
/* Prefixed Floating-Point Load/Store Instructions */
|
||||
#define PLFS(frt, a, r, d) PREFIX_MLS(PPC_INST_LFS, frt, a, r, d)
|
||||
#define PLFD(frt, a, r, d) PREFIX_MLS(PPC_INST_LFD, frt, a, r, d)
|
||||
#define PSTFS(frs, a, r, d) PREFIX_MLS(PPC_INST_STFS, frs, a, r, d)
|
||||
#define PSTFD(frs, a, r, d) PREFIX_MLS(PPC_INST_STFD, frs, a, r, d)
|
||||
|
||||
/* Prefixed VSX Load/Store Instructions */
|
||||
#define PLXSD(vrt, a, r, d) PREFIX_8LS(0xa8000000, vrt, a, r, d)
|
||||
#define PLXSSP(vrt, a, r, d) PREFIX_8LS(0xac000000, vrt, a, r, d)
|
||||
#define PLXV0(s, a, r, d) PREFIX_8LS(0xc8000000, s, a, r, d)
|
||||
#define PLXV1(s, a, r, d) PREFIX_8LS(0xcc000000, s, a, r, d)
|
||||
#define PSTXSD(vrs, a, r, d) PREFIX_8LS(0xb8000000, vrs, a, r, d)
|
||||
#define PSTXSSP(vrs, a, r, d) PREFIX_8LS(0xbc000000, vrs, a, r, d)
|
||||
#define PSTXV0(s, a, r, d) PREFIX_8LS(0xd8000000, s, a, r, d)
|
||||
#define PSTXV1(s, a, r, d) PREFIX_8LS(0xdc000000, s, a, r, d)
|
||||
|
||||
#endif /* _SELFTESTS_POWERPC_INSTRUCTIONS_H */
|
||||
|
136
tools/testing/selftests/powerpc/include/pkeys.h
Normální soubor
136
tools/testing/selftests/powerpc/include/pkeys.h
Normální soubor
@@ -0,0 +1,136 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2020, Sandipan Das, IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef _SELFTESTS_POWERPC_PKEYS_H
|
||||
#define _SELFTESTS_POWERPC_PKEYS_H
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "reg.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
* Older versions of libc use the Intel-specific access rights.
|
||||
* Hence, override the definitions as they might be incorrect.
|
||||
*/
|
||||
#undef PKEY_DISABLE_ACCESS
|
||||
#define PKEY_DISABLE_ACCESS 0x3
|
||||
|
||||
#undef PKEY_DISABLE_WRITE
|
||||
#define PKEY_DISABLE_WRITE 0x2
|
||||
|
||||
#undef PKEY_DISABLE_EXECUTE
|
||||
#define PKEY_DISABLE_EXECUTE 0x4
|
||||
|
||||
/* Older versions of libc do not not define this */
|
||||
#ifndef SEGV_PKUERR
|
||||
#define SEGV_PKUERR 4
|
||||
#endif
|
||||
|
||||
#define SI_PKEY_OFFSET 0x20
|
||||
|
||||
#define __NR_pkey_mprotect 386
|
||||
#define __NR_pkey_alloc 384
|
||||
#define __NR_pkey_free 385
|
||||
|
||||
#define PKEY_BITS_PER_PKEY 2
|
||||
#define NR_PKEYS 32
|
||||
#define PKEY_BITS_MASK ((1UL << PKEY_BITS_PER_PKEY) - 1)
|
||||
|
||||
inline unsigned long pkeyreg_get(void)
|
||||
{
|
||||
return mfspr(SPRN_AMR);
|
||||
}
|
||||
|
||||
inline void pkeyreg_set(unsigned long amr)
|
||||
{
|
||||
set_amr(amr);
|
||||
}
|
||||
|
||||
void pkey_set_rights(int pkey, unsigned long rights)
|
||||
{
|
||||
unsigned long amr, shift;
|
||||
|
||||
shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY;
|
||||
amr = pkeyreg_get();
|
||||
amr &= ~(PKEY_BITS_MASK << shift);
|
||||
amr |= (rights & PKEY_BITS_MASK) << shift;
|
||||
pkeyreg_set(amr);
|
||||
}
|
||||
|
||||
int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey)
|
||||
{
|
||||
return syscall(__NR_pkey_mprotect, addr, len, prot, pkey);
|
||||
}
|
||||
|
||||
int sys_pkey_alloc(unsigned long flags, unsigned long rights)
|
||||
{
|
||||
return syscall(__NR_pkey_alloc, flags, rights);
|
||||
}
|
||||
|
||||
int sys_pkey_free(int pkey)
|
||||
{
|
||||
return syscall(__NR_pkey_free, pkey);
|
||||
}
|
||||
|
||||
int pkeys_unsupported(void)
|
||||
{
|
||||
bool hash_mmu = false;
|
||||
int pkey;
|
||||
|
||||
/* Protection keys are currently supported on Hash MMU only */
|
||||
FAIL_IF(using_hash_mmu(&hash_mmu));
|
||||
SKIP_IF(!hash_mmu);
|
||||
|
||||
/* Check if the system call is supported */
|
||||
pkey = sys_pkey_alloc(0, 0);
|
||||
SKIP_IF(pkey < 0);
|
||||
sys_pkey_free(pkey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int siginfo_pkey(siginfo_t *si)
|
||||
{
|
||||
/*
|
||||
* In older versions of libc, siginfo_t does not have si_pkey as
|
||||
* a member.
|
||||
*/
|
||||
#ifdef si_pkey
|
||||
return si->si_pkey;
|
||||
#else
|
||||
return *((int *)(((char *) si) + SI_PKEY_OFFSET));
|
||||
#endif
|
||||
}
|
||||
|
||||
#define pkey_rights(r) ({ \
|
||||
static char buf[4] = "rwx"; \
|
||||
unsigned int amr_bits; \
|
||||
if ((r) & PKEY_DISABLE_EXECUTE) \
|
||||
buf[2] = '-'; \
|
||||
amr_bits = (r) & PKEY_BITS_MASK; \
|
||||
if (amr_bits & PKEY_DISABLE_WRITE) \
|
||||
buf[1] = '-'; \
|
||||
if (amr_bits & PKEY_DISABLE_ACCESS & ~PKEY_DISABLE_WRITE) \
|
||||
buf[0] = '-'; \
|
||||
buf; \
|
||||
})
|
||||
|
||||
unsigned long next_pkey_rights(unsigned long rights)
|
||||
{
|
||||
if (rights == PKEY_DISABLE_ACCESS)
|
||||
return PKEY_DISABLE_EXECUTE;
|
||||
else if (rights == (PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE))
|
||||
return 0;
|
||||
|
||||
if ((rights & PKEY_BITS_MASK) == 0)
|
||||
rights |= PKEY_DISABLE_WRITE;
|
||||
else if ((rights & PKEY_BITS_MASK) == PKEY_DISABLE_WRITE)
|
||||
rights |= PKEY_DISABLE_ACCESS;
|
||||
|
||||
return rights;
|
||||
}
|
||||
|
||||
#endif /* _SELFTESTS_POWERPC_PKEYS_H */
|
@@ -57,6 +57,12 @@
|
||||
#define SPRN_PPR 896 /* Program Priority Register */
|
||||
#define SPRN_AMR 13 /* Authority Mask Register - problem state */
|
||||
|
||||
#define set_amr(v) asm volatile("isync;" \
|
||||
"mtspr " __stringify(SPRN_AMR) ",%0;" \
|
||||
"isync" : \
|
||||
: "r" ((unsigned long)(v)) \
|
||||
: "memory")
|
||||
|
||||
/* TEXASR register bits */
|
||||
#define TEXASR_FC 0xFE00000000000000
|
||||
#define TEXASR_FP 0x0100000000000000
|
||||
|
@@ -42,6 +42,16 @@ int perf_event_enable(int fd);
|
||||
int perf_event_disable(int fd);
|
||||
int perf_event_reset(int fd);
|
||||
|
||||
#if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 30)
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
static inline pid_t gettid(void)
|
||||
{
|
||||
return syscall(SYS_gettid);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool have_hwcap(unsigned long ftr)
|
||||
{
|
||||
return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr;
|
||||
@@ -60,6 +70,7 @@ static inline bool have_hwcap2(unsigned long ftr2)
|
||||
#endif
|
||||
|
||||
bool is_ppc64le(void);
|
||||
int using_hash_mmu(bool *using_hash);
|
||||
|
||||
/* Yes, this is evil */
|
||||
#define FAIL_IF(x) \
|
||||
@@ -71,6 +82,15 @@ do { \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FAIL_IF_EXIT(x) \
|
||||
do { \
|
||||
if ((x)) { \
|
||||
fprintf(stderr, \
|
||||
"[FAIL] Test FAILED on line %d\n", __LINE__); \
|
||||
_exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* The test harness uses this, yes it's gross */
|
||||
#define MAGIC_SKIP_RETURN_VALUE 99
|
||||
|
||||
@@ -96,11 +116,20 @@ do { \
|
||||
#define _str(s) #s
|
||||
#define str(s) _str(s)
|
||||
|
||||
#define sigsafe_err(msg) ({ \
|
||||
ssize_t nbytes __attribute__((unused)); \
|
||||
nbytes = write(STDERR_FILENO, msg, strlen(msg)); })
|
||||
|
||||
/* POWER9 feature */
|
||||
#ifndef PPC_FEATURE2_ARCH_3_00
|
||||
#define PPC_FEATURE2_ARCH_3_00 0x00800000
|
||||
#endif
|
||||
|
||||
/* POWER10 feature */
|
||||
#ifndef PPC_FEATURE2_ARCH_3_1
|
||||
#define PPC_FEATURE2_ARCH_3_1 0x00040000
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc64__)
|
||||
#define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP]
|
||||
#define UCONTEXT_MSR(UC) (UC)->uc_mcontext.gp_regs[PT_MSR]
|
||||
|
1
tools/testing/selftests/powerpc/math/.gitignore
vendorováno
1
tools/testing/selftests/powerpc/math/.gitignore
vendorováno
@@ -6,3 +6,4 @@ vmx_preempt
|
||||
fpu_signal
|
||||
vmx_signal
|
||||
vsx_preempt
|
||||
fpu_denormal
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
TEST_GEN_PROGS := fpu_syscall fpu_preempt fpu_signal vmx_syscall vmx_preempt vmx_signal vsx_preempt
|
||||
TEST_GEN_PROGS := fpu_syscall fpu_preempt fpu_signal fpu_denormal vmx_syscall vmx_preempt vmx_signal vsx_preempt
|
||||
|
||||
top_srcdir = ../../../../..
|
||||
include ../../lib.mk
|
||||
@@ -11,9 +11,9 @@ $(OUTPUT)/fpu_syscall: fpu_asm.S
|
||||
$(OUTPUT)/fpu_preempt: fpu_asm.S
|
||||
$(OUTPUT)/fpu_signal: fpu_asm.S
|
||||
|
||||
$(OUTPUT)/vmx_syscall: vmx_asm.S
|
||||
$(OUTPUT)/vmx_preempt: vmx_asm.S
|
||||
$(OUTPUT)/vmx_signal: vmx_asm.S
|
||||
$(OUTPUT)/vmx_syscall: vmx_asm.S ../utils.c
|
||||
$(OUTPUT)/vmx_preempt: vmx_asm.S ../utils.c
|
||||
$(OUTPUT)/vmx_signal: vmx_asm.S ../utils.c
|
||||
|
||||
$(OUTPUT)/vsx_preempt: CFLAGS += -mvsx
|
||||
$(OUTPUT)/vsx_preempt: vsx_asm.S
|
||||
$(OUTPUT)/vsx_preempt: vsx_asm.S ../utils.c
|
||||
|
38
tools/testing/selftests/powerpc/math/fpu_denormal.c
Normální soubor
38
tools/testing/selftests/powerpc/math/fpu_denormal.c
Normální soubor
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* This test attempts to cause a FP denormal exception on POWER8 CPUs. Unfortunately
|
||||
* if the denormal handler is not configured or working properly, this can cause a bad
|
||||
* crash in kernel mode when the kernel tries to save FP registers when the process
|
||||
* exits.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static int test_denormal_fpu(void)
|
||||
{
|
||||
unsigned int m32;
|
||||
unsigned long m64;
|
||||
volatile float f;
|
||||
volatile double d;
|
||||
|
||||
/* try to induce lfs <denormal> ; stfd */
|
||||
|
||||
m32 = 0x00715fcf; /* random denormal */
|
||||
memcpy((float *)&f, &m32, sizeof(f));
|
||||
d = f;
|
||||
memcpy(&m64, (double *)&d, sizeof(d));
|
||||
|
||||
FAIL_IF((long)(m64 != 0x380c57f3c0000000)); /* renormalised value */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return test_harness(test_denormal_fpu, "fpu_denormal");
|
||||
}
|
@@ -57,6 +57,9 @@ int test_preempt_vmx(void)
|
||||
int i, rc, threads;
|
||||
pthread_t *tids;
|
||||
|
||||
// vcmpequd used in vmx_asm.S is v2.07
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR;
|
||||
tids = malloc(threads * sizeof(pthread_t));
|
||||
FAIL_IF(!tids);
|
||||
|
@@ -96,6 +96,9 @@ int test_signal_vmx(void)
|
||||
void *rc_p;
|
||||
pthread_t *tids;
|
||||
|
||||
// vcmpequd used in vmx_asm.S is v2.07
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR;
|
||||
tids = malloc(threads * sizeof(pthread_t));
|
||||
FAIL_IF(!tids);
|
||||
|
@@ -49,9 +49,14 @@ int test_vmx_syscall(void)
|
||||
* Setup an environment with much context switching
|
||||
*/
|
||||
pid_t pid2;
|
||||
pid_t pid = fork();
|
||||
pid_t pid;
|
||||
int ret;
|
||||
int child_ret;
|
||||
|
||||
// vcmpequd used in vmx_asm.S is v2.07
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
pid = fork();
|
||||
FAIL_IF(pid == -1);
|
||||
|
||||
pid2 = fork();
|
||||
|
@@ -92,6 +92,8 @@ int test_preempt_vsx(void)
|
||||
int i, rc, threads;
|
||||
pthread_t *tids;
|
||||
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_VSX));
|
||||
|
||||
threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR;
|
||||
tids = malloc(threads * sizeof(pthread_t));
|
||||
FAIL_IF(!tids);
|
||||
|
5
tools/testing/selftests/powerpc/mm/.gitignore
vendorováno
5
tools/testing/selftests/powerpc/mm/.gitignore
vendorováno
@@ -2,9 +2,12 @@
|
||||
hugetlb_vs_thp_test
|
||||
subpage_prot
|
||||
tempfile
|
||||
prot_sao
|
||||
segv_errors
|
||||
wild_bctr
|
||||
large_vm_fork_separation
|
||||
bad_accesses
|
||||
tlbie_test
|
||||
pkey_exec_prot
|
||||
pkey_siginfo
|
||||
stack_expansion_ldst
|
||||
stack_expansion_signal
|
||||
|
@@ -2,23 +2,31 @@
|
||||
noarg:
|
||||
$(MAKE) -C ../
|
||||
|
||||
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \
|
||||
large_vm_fork_separation bad_accesses
|
||||
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot segv_errors wild_bctr \
|
||||
large_vm_fork_separation bad_accesses pkey_exec_prot \
|
||||
pkey_siginfo stack_expansion_signal stack_expansion_ldst
|
||||
|
||||
TEST_GEN_PROGS_EXTENDED := tlbie_test
|
||||
TEST_GEN_FILES := tempfile
|
||||
|
||||
top_srcdir = ../../../../..
|
||||
include ../../lib.mk
|
||||
|
||||
$(TEST_GEN_PROGS): ../harness.c
|
||||
|
||||
$(OUTPUT)/prot_sao: ../utils.c
|
||||
$(TEST_GEN_PROGS): ../harness.c ../utils.c
|
||||
|
||||
$(OUTPUT)/wild_bctr: CFLAGS += -m64
|
||||
$(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64
|
||||
$(OUTPUT)/bad_accesses: CFLAGS += -m64
|
||||
$(OUTPUT)/pkey_exec_prot: CFLAGS += -m64
|
||||
$(OUTPUT)/pkey_siginfo: CFLAGS += -m64
|
||||
|
||||
$(OUTPUT)/stack_expansion_signal: ../utils.c ../pmu/lib.c
|
||||
|
||||
$(OUTPUT)/stack_expansion_ldst: CFLAGS += -fno-stack-protector
|
||||
$(OUTPUT)/stack_expansion_ldst: ../utils.c
|
||||
|
||||
$(OUTPUT)/tempfile:
|
||||
dd if=/dev/zero of=$@ bs=64k count=1
|
||||
|
||||
$(OUTPUT)/tlbie_test: LDLIBS += -lpthread
|
||||
$(OUTPUT)/pkey_siginfo: LDLIBS += -lpthread
|
||||
|
@@ -64,34 +64,6 @@ int bad_access(char *p, bool write)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int using_hash_mmu(bool *using_hash)
|
||||
{
|
||||
char line[128];
|
||||
FILE *f;
|
||||
int rc;
|
||||
|
||||
f = fopen("/proc/cpuinfo", "r");
|
||||
FAIL_IF(!f);
|
||||
|
||||
rc = 0;
|
||||
while (fgets(line, sizeof(line), f) != NULL) {
|
||||
if (strcmp(line, "MMU : Hash\n") == 0) {
|
||||
*using_hash = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(line, "MMU : Radix\n") == 0) {
|
||||
*using_hash = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = -1;
|
||||
out:
|
||||
fclose(f);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int test(void)
|
||||
{
|
||||
unsigned long i, j, addr, region_shift, page_shift, page_size;
|
||||
|
294
tools/testing/selftests/powerpc/mm/pkey_exec_prot.c
Normální soubor
294
tools/testing/selftests/powerpc/mm/pkey_exec_prot.c
Normální soubor
@@ -0,0 +1,294 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Copyright 2020, Sandipan Das, IBM Corp.
|
||||
*
|
||||
* Test if applying execute protection on pages using memory
|
||||
* protection keys works as expected.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pkeys.h"
|
||||
|
||||
#define PPC_INST_NOP 0x60000000
|
||||
#define PPC_INST_TRAP 0x7fe00008
|
||||
#define PPC_INST_BLR 0x4e800020
|
||||
|
||||
static volatile sig_atomic_t fault_pkey, fault_code, fault_type;
|
||||
static volatile sig_atomic_t remaining_faults;
|
||||
static volatile unsigned int *fault_addr;
|
||||
static unsigned long pgsize, numinsns;
|
||||
static unsigned int *insns;
|
||||
|
||||
static void trap_handler(int signum, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
/* Check if this fault originated from the expected address */
|
||||
if (sinfo->si_addr != (void *) fault_addr)
|
||||
sigsafe_err("got a fault for an unexpected address\n");
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
int signal_pkey;
|
||||
|
||||
signal_pkey = siginfo_pkey(sinfo);
|
||||
fault_code = sinfo->si_code;
|
||||
|
||||
/* Check if this fault originated from the expected address */
|
||||
if (sinfo->si_addr != (void *) fault_addr) {
|
||||
sigsafe_err("got a fault for an unexpected address\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Check if too many faults have occurred for a single test case */
|
||||
if (!remaining_faults) {
|
||||
sigsafe_err("got too many faults for the same address\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* Restore permissions in order to continue */
|
||||
switch (fault_code) {
|
||||
case SEGV_ACCERR:
|
||||
if (mprotect(insns, pgsize, PROT_READ | PROT_WRITE)) {
|
||||
sigsafe_err("failed to set access permissions\n");
|
||||
_exit(1);
|
||||
}
|
||||
break;
|
||||
case SEGV_PKUERR:
|
||||
if (signal_pkey != fault_pkey) {
|
||||
sigsafe_err("got a fault for an unexpected pkey\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
switch (fault_type) {
|
||||
case PKEY_DISABLE_ACCESS:
|
||||
pkey_set_rights(fault_pkey, 0);
|
||||
break;
|
||||
case PKEY_DISABLE_EXECUTE:
|
||||
/*
|
||||
* Reassociate the exec-only pkey with the region
|
||||
* to be able to continue. Unlike AMR, we cannot
|
||||
* set IAMR directly from userspace to restore the
|
||||
* permissions.
|
||||
*/
|
||||
if (mprotect(insns, pgsize, PROT_EXEC)) {
|
||||
sigsafe_err("failed to set execute permissions\n");
|
||||
_exit(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sigsafe_err("got a fault with an unexpected type\n");
|
||||
_exit(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sigsafe_err("got a fault with an unexpected code\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
remaining_faults--;
|
||||
}
|
||||
|
||||
static int test(void)
|
||||
{
|
||||
struct sigaction segv_act, trap_act;
|
||||
unsigned long rights;
|
||||
int pkey, ret, i;
|
||||
|
||||
ret = pkeys_unsupported();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Setup SIGSEGV handler */
|
||||
segv_act.sa_handler = 0;
|
||||
segv_act.sa_sigaction = segv_handler;
|
||||
FAIL_IF(sigprocmask(SIG_SETMASK, 0, &segv_act.sa_mask) != 0);
|
||||
segv_act.sa_flags = SA_SIGINFO;
|
||||
segv_act.sa_restorer = 0;
|
||||
FAIL_IF(sigaction(SIGSEGV, &segv_act, NULL) != 0);
|
||||
|
||||
/* Setup SIGTRAP handler */
|
||||
trap_act.sa_handler = 0;
|
||||
trap_act.sa_sigaction = trap_handler;
|
||||
FAIL_IF(sigprocmask(SIG_SETMASK, 0, &trap_act.sa_mask) != 0);
|
||||
trap_act.sa_flags = SA_SIGINFO;
|
||||
trap_act.sa_restorer = 0;
|
||||
FAIL_IF(sigaction(SIGTRAP, &trap_act, NULL) != 0);
|
||||
|
||||
/* Setup executable region */
|
||||
pgsize = getpagesize();
|
||||
numinsns = pgsize / sizeof(unsigned int);
|
||||
insns = (unsigned int *) mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
FAIL_IF(insns == MAP_FAILED);
|
||||
|
||||
/* Write the instruction words */
|
||||
for (i = 1; i < numinsns - 1; i++)
|
||||
insns[i] = PPC_INST_NOP;
|
||||
|
||||
/*
|
||||
* Set the first instruction as an unconditional trap. If
|
||||
* the last write to this address succeeds, this should
|
||||
* get overwritten by a no-op.
|
||||
*/
|
||||
insns[0] = PPC_INST_TRAP;
|
||||
|
||||
/*
|
||||
* Later, to jump to the executable region, we use a branch
|
||||
* and link instruction (bctrl) which sets the return address
|
||||
* automatically in LR. Use that to return back.
|
||||
*/
|
||||
insns[numinsns - 1] = PPC_INST_BLR;
|
||||
|
||||
/* Allocate a pkey that restricts execution */
|
||||
rights = PKEY_DISABLE_EXECUTE;
|
||||
pkey = sys_pkey_alloc(0, rights);
|
||||
FAIL_IF(pkey < 0);
|
||||
|
||||
/*
|
||||
* Pick the first instruction's address from the executable
|
||||
* region.
|
||||
*/
|
||||
fault_addr = insns;
|
||||
|
||||
/* The following two cases will avoid SEGV_PKUERR */
|
||||
fault_type = -1;
|
||||
fault_pkey = -1;
|
||||
|
||||
/*
|
||||
* Read an instruction word from the address when AMR bits
|
||||
* are not set i.e. the pkey permits both read and write
|
||||
* access.
|
||||
*
|
||||
* This should not generate a fault as having PROT_EXEC
|
||||
* implies PROT_READ on GNU systems. The pkey currently
|
||||
* restricts execution only based on the IAMR bits. The
|
||||
* AMR bits are cleared.
|
||||
*/
|
||||
remaining_faults = 0;
|
||||
FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
|
||||
printf("read from %p, pkey permissions are %s\n", fault_addr,
|
||||
pkey_rights(rights));
|
||||
i = *fault_addr;
|
||||
FAIL_IF(remaining_faults != 0);
|
||||
|
||||
/*
|
||||
* Write an instruction word to the address when AMR bits
|
||||
* are not set i.e. the pkey permits both read and write
|
||||
* access.
|
||||
*
|
||||
* This should generate an access fault as having just
|
||||
* PROT_EXEC also restricts writes. The pkey currently
|
||||
* restricts execution only based on the IAMR bits. The
|
||||
* AMR bits are cleared.
|
||||
*/
|
||||
remaining_faults = 1;
|
||||
FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
|
||||
printf("write to %p, pkey permissions are %s\n", fault_addr,
|
||||
pkey_rights(rights));
|
||||
*fault_addr = PPC_INST_TRAP;
|
||||
FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR);
|
||||
|
||||
/* The following three cases will generate SEGV_PKUERR */
|
||||
rights |= PKEY_DISABLE_ACCESS;
|
||||
fault_type = PKEY_DISABLE_ACCESS;
|
||||
fault_pkey = pkey;
|
||||
|
||||
/*
|
||||
* Read an instruction word from the address when AMR bits
|
||||
* are set i.e. the pkey permits neither read nor write
|
||||
* access.
|
||||
*
|
||||
* This should generate a pkey fault based on AMR bits only
|
||||
* as having PROT_EXEC implicitly allows reads.
|
||||
*/
|
||||
remaining_faults = 1;
|
||||
FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
|
||||
pkey_set_rights(pkey, rights);
|
||||
printf("read from %p, pkey permissions are %s\n", fault_addr,
|
||||
pkey_rights(rights));
|
||||
i = *fault_addr;
|
||||
FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR);
|
||||
|
||||
/*
|
||||
* Write an instruction word to the address when AMR bits
|
||||
* are set i.e. the pkey permits neither read nor write
|
||||
* access.
|
||||
*
|
||||
* This should generate two faults. First, a pkey fault
|
||||
* based on AMR bits and then an access fault since
|
||||
* PROT_EXEC does not allow writes.
|
||||
*/
|
||||
remaining_faults = 2;
|
||||
FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
|
||||
pkey_set_rights(pkey, rights);
|
||||
printf("write to %p, pkey permissions are %s\n", fault_addr,
|
||||
pkey_rights(rights));
|
||||
*fault_addr = PPC_INST_NOP;
|
||||
FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR);
|
||||
|
||||
/* Free the current pkey */
|
||||
sys_pkey_free(pkey);
|
||||
|
||||
rights = 0;
|
||||
do {
|
||||
/*
|
||||
* Allocate pkeys with all valid combinations of read,
|
||||
* write and execute restrictions.
|
||||
*/
|
||||
pkey = sys_pkey_alloc(0, rights);
|
||||
FAIL_IF(pkey < 0);
|
||||
|
||||
/*
|
||||
* Jump to the executable region. AMR bits may or may not
|
||||
* be set but they should not affect execution.
|
||||
*
|
||||
* This should generate pkey faults based on IAMR bits which
|
||||
* may be set to restrict execution.
|
||||
*
|
||||
* The first iteration also checks if the overwrite of the
|
||||
* first instruction word from a trap to a no-op succeeded.
|
||||
*/
|
||||
fault_pkey = pkey;
|
||||
fault_type = -1;
|
||||
remaining_faults = 0;
|
||||
if (rights & PKEY_DISABLE_EXECUTE) {
|
||||
fault_type = PKEY_DISABLE_EXECUTE;
|
||||
remaining_faults = 1;
|
||||
}
|
||||
|
||||
FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
|
||||
printf("execute at %p, pkey permissions are %s\n", fault_addr,
|
||||
pkey_rights(rights));
|
||||
asm volatile("mtctr %0; bctrl" : : "r"(insns));
|
||||
FAIL_IF(remaining_faults != 0);
|
||||
if (rights & PKEY_DISABLE_EXECUTE)
|
||||
FAIL_IF(fault_code != SEGV_PKUERR);
|
||||
|
||||
/* Free the current pkey */
|
||||
sys_pkey_free(pkey);
|
||||
|
||||
/* Find next valid combination of pkey rights */
|
||||
rights = next_pkey_rights(rights);
|
||||
} while (rights);
|
||||
|
||||
/* Cleanup */
|
||||
munmap((void *) insns, pgsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_harness(test, "pkey_exec_prot");
|
||||
}
|
333
tools/testing/selftests/powerpc/mm/pkey_siginfo.c
Normální soubor
333
tools/testing/selftests/powerpc/mm/pkey_siginfo.c
Normální soubor
@@ -0,0 +1,333 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020, Sandipan Das, IBM Corp.
|
||||
*
|
||||
* Test if the signal information reports the correct memory protection
|
||||
* key upon getting a key access violation fault for a page that was
|
||||
* attempted to be protected by two different keys from two competing
|
||||
* threads at the same time.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "pkeys.h"
|
||||
|
||||
#define PPC_INST_NOP 0x60000000
|
||||
#define PPC_INST_BLR 0x4e800020
|
||||
#define PROT_RWX (PROT_READ | PROT_WRITE | PROT_EXEC)
|
||||
|
||||
#define NUM_ITERATIONS 1000000
|
||||
|
||||
static volatile sig_atomic_t perm_pkey, rest_pkey;
|
||||
static volatile sig_atomic_t rights, fault_count;
|
||||
static volatile unsigned int *volatile fault_addr;
|
||||
static pthread_barrier_t iteration_barrier;
|
||||
|
||||
static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
void *pgstart;
|
||||
size_t pgsize;
|
||||
int pkey;
|
||||
|
||||
pkey = siginfo_pkey(sinfo);
|
||||
|
||||
/* Check if this fault originated from a pkey access violation */
|
||||
if (sinfo->si_code != SEGV_PKUERR) {
|
||||
sigsafe_err("got a fault for an unexpected reason\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Check if this fault originated from the expected address */
|
||||
if (sinfo->si_addr != (void *) fault_addr) {
|
||||
sigsafe_err("got a fault for an unexpected address\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Check if this fault originated from the restrictive pkey */
|
||||
if (pkey != rest_pkey) {
|
||||
sigsafe_err("got a fault for an unexpected pkey\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* Check if too many faults have occurred for the same iteration */
|
||||
if (fault_count > 0) {
|
||||
sigsafe_err("got too many faults for the same address\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
pgsize = getpagesize();
|
||||
pgstart = (void *) ((unsigned long) fault_addr & ~(pgsize - 1));
|
||||
|
||||
/*
|
||||
* If the current fault occurred due to lack of execute rights,
|
||||
* reassociate the page with the exec-only pkey since execute
|
||||
* rights cannot be changed directly for the faulting pkey as
|
||||
* IAMR is inaccessible from userspace.
|
||||
*
|
||||
* Otherwise, if the current fault occurred due to lack of
|
||||
* read-write rights, change the AMR permission bits for the
|
||||
* pkey.
|
||||
*
|
||||
* This will let the test continue.
|
||||
*/
|
||||
if (rights == PKEY_DISABLE_EXECUTE &&
|
||||
mprotect(pgstart, pgsize, PROT_EXEC))
|
||||
_exit(1);
|
||||
else
|
||||
pkey_set_rights(pkey, 0);
|
||||
|
||||
fault_count++;
|
||||
}
|
||||
|
||||
struct region {
|
||||
unsigned long rights;
|
||||
unsigned int *base;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static void *protect(void *p)
|
||||
{
|
||||
unsigned long rights;
|
||||
unsigned int *base;
|
||||
size_t size;
|
||||
int tid, i;
|
||||
|
||||
tid = gettid();
|
||||
base = ((struct region *) p)->base;
|
||||
size = ((struct region *) p)->size;
|
||||
FAIL_IF_EXIT(!base);
|
||||
|
||||
/* No read, write and execute restrictions */
|
||||
rights = 0;
|
||||
|
||||
printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
|
||||
|
||||
/* Allocate the permissive pkey */
|
||||
perm_pkey = sys_pkey_alloc(0, rights);
|
||||
FAIL_IF_EXIT(perm_pkey < 0);
|
||||
|
||||
/*
|
||||
* Repeatedly try to protect the common region with a permissive
|
||||
* pkey
|
||||
*/
|
||||
for (i = 0; i < NUM_ITERATIONS; i++) {
|
||||
/*
|
||||
* Wait until the other thread has finished allocating the
|
||||
* restrictive pkey or until the next iteration has begun
|
||||
*/
|
||||
pthread_barrier_wait(&iteration_barrier);
|
||||
|
||||
/* Try to associate the permissive pkey with the region */
|
||||
FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
|
||||
perm_pkey));
|
||||
}
|
||||
|
||||
/* Free the permissive pkey */
|
||||
sys_pkey_free(perm_pkey);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *protect_access(void *p)
|
||||
{
|
||||
size_t size, numinsns;
|
||||
unsigned int *base;
|
||||
int tid, i;
|
||||
|
||||
tid = gettid();
|
||||
base = ((struct region *) p)->base;
|
||||
size = ((struct region *) p)->size;
|
||||
rights = ((struct region *) p)->rights;
|
||||
numinsns = size / sizeof(base[0]);
|
||||
FAIL_IF_EXIT(!base);
|
||||
|
||||
/* Allocate the restrictive pkey */
|
||||
rest_pkey = sys_pkey_alloc(0, rights);
|
||||
FAIL_IF_EXIT(rest_pkey < 0);
|
||||
|
||||
printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
|
||||
printf("tid %d, %s randomly in range [%p, %p]\n", tid,
|
||||
(rights == PKEY_DISABLE_EXECUTE) ? "execute" :
|
||||
(rights == PKEY_DISABLE_WRITE) ? "write" : "read",
|
||||
base, base + numinsns);
|
||||
|
||||
/*
|
||||
* Repeatedly try to protect the common region with a restrictive
|
||||
* pkey and read, write or execute from it
|
||||
*/
|
||||
for (i = 0; i < NUM_ITERATIONS; i++) {
|
||||
/*
|
||||
* Wait until the other thread has finished allocating the
|
||||
* permissive pkey or until the next iteration has begun
|
||||
*/
|
||||
pthread_barrier_wait(&iteration_barrier);
|
||||
|
||||
/* Try to associate the restrictive pkey with the region */
|
||||
FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
|
||||
rest_pkey));
|
||||
|
||||
/* Choose a random instruction word address from the region */
|
||||
fault_addr = base + (rand() % numinsns);
|
||||
fault_count = 0;
|
||||
|
||||
switch (rights) {
|
||||
/* Read protection test */
|
||||
case PKEY_DISABLE_ACCESS:
|
||||
/*
|
||||
* Read an instruction word from the region and
|
||||
* verify if it has not been overwritten to
|
||||
* something unexpected
|
||||
*/
|
||||
FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP &&
|
||||
*fault_addr != PPC_INST_BLR);
|
||||
break;
|
||||
|
||||
/* Write protection test */
|
||||
case PKEY_DISABLE_WRITE:
|
||||
/*
|
||||
* Write an instruction word to the region and
|
||||
* verify if the overwrite has succeeded
|
||||
*/
|
||||
*fault_addr = PPC_INST_BLR;
|
||||
FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR);
|
||||
break;
|
||||
|
||||
/* Execute protection test */
|
||||
case PKEY_DISABLE_EXECUTE:
|
||||
/* Jump to the region and execute instructions */
|
||||
asm volatile(
|
||||
"mtctr %0; bctrl"
|
||||
: : "r"(fault_addr) : "ctr", "lr");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the restrictions originally imposed by the
|
||||
* restrictive pkey as the signal handler would have
|
||||
* cleared out the corresponding AMR bits
|
||||
*/
|
||||
pkey_set_rights(rest_pkey, rights);
|
||||
}
|
||||
|
||||
/* Free restrictive pkey */
|
||||
sys_pkey_free(rest_pkey);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void reset_pkeys(unsigned long rights)
|
||||
{
|
||||
int pkeys[NR_PKEYS], i;
|
||||
|
||||
/* Exhaustively allocate all available pkeys */
|
||||
for (i = 0; i < NR_PKEYS; i++)
|
||||
pkeys[i] = sys_pkey_alloc(0, rights);
|
||||
|
||||
/* Free all allocated pkeys */
|
||||
for (i = 0; i < NR_PKEYS; i++)
|
||||
sys_pkey_free(pkeys[i]);
|
||||
}
|
||||
|
||||
static int test(void)
|
||||
{
|
||||
pthread_t prot_thread, pacc_thread;
|
||||
struct sigaction act;
|
||||
pthread_attr_t attr;
|
||||
size_t numinsns;
|
||||
struct region r;
|
||||
int ret, i;
|
||||
|
||||
srand(time(NULL));
|
||||
ret = pkeys_unsupported();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Allocate the region */
|
||||
r.size = getpagesize();
|
||||
r.base = mmap(NULL, r.size, PROT_RWX,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
FAIL_IF(r.base == MAP_FAILED);
|
||||
|
||||
/*
|
||||
* Fill the region with no-ops with a branch at the end
|
||||
* for returning to the caller
|
||||
*/
|
||||
numinsns = r.size / sizeof(r.base[0]);
|
||||
for (i = 0; i < numinsns - 1; i++)
|
||||
r.base[i] = PPC_INST_NOP;
|
||||
r.base[i] = PPC_INST_BLR;
|
||||
|
||||
/* Setup SIGSEGV handler */
|
||||
act.sa_handler = 0;
|
||||
act.sa_sigaction = segv_handler;
|
||||
FAIL_IF(sigprocmask(SIG_SETMASK, 0, &act.sa_mask) != 0);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_restorer = 0;
|
||||
FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0);
|
||||
|
||||
/*
|
||||
* For these tests, the parent process should clear all bits of
|
||||
* AMR and IAMR, i.e. impose no restrictions, for all available
|
||||
* pkeys. This will be the base for the initial AMR and IAMR
|
||||
* values for all the test thread pairs.
|
||||
*
|
||||
* If the AMR and IAMR bits of all available pkeys are cleared
|
||||
* before running the tests and a fault is generated when
|
||||
* attempting to read, write or execute instructions from a
|
||||
* pkey protected region, the pkey responsible for this must be
|
||||
* the one from the protect-and-access thread since the other
|
||||
* one is fully permissive. Despite that, if the pkey reported
|
||||
* by siginfo is not the restrictive pkey, then there must be a
|
||||
* kernel bug.
|
||||
*/
|
||||
reset_pkeys(0);
|
||||
|
||||
/* Setup barrier for protect and protect-and-access threads */
|
||||
FAIL_IF(pthread_attr_init(&attr) != 0);
|
||||
FAIL_IF(pthread_barrier_init(&iteration_barrier, NULL, 2) != 0);
|
||||
|
||||
/* Setup and start protect and protect-and-read threads */
|
||||
puts("starting thread pair (protect, protect-and-read)");
|
||||
r.rights = PKEY_DISABLE_ACCESS;
|
||||
FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
|
||||
FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
|
||||
FAIL_IF(pthread_join(prot_thread, NULL) != 0);
|
||||
FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
|
||||
|
||||
/* Setup and start protect and protect-and-write threads */
|
||||
puts("starting thread pair (protect, protect-and-write)");
|
||||
r.rights = PKEY_DISABLE_WRITE;
|
||||
FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
|
||||
FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
|
||||
FAIL_IF(pthread_join(prot_thread, NULL) != 0);
|
||||
FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
|
||||
|
||||
/* Setup and start protect and protect-and-execute threads */
|
||||
puts("starting thread pair (protect, protect-and-execute)");
|
||||
r.rights = PKEY_DISABLE_EXECUTE;
|
||||
FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
|
||||
FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
|
||||
FAIL_IF(pthread_join(prot_thread, NULL) != 0);
|
||||
FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
|
||||
|
||||
/* Cleanup */
|
||||
FAIL_IF(pthread_attr_destroy(&attr) != 0);
|
||||
FAIL_IF(pthread_barrier_destroy(&iteration_barrier) != 0);
|
||||
munmap(r.base, r.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_harness(test, "pkey_siginfo");
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2016, Michael Ellerman, IBM Corp.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <asm/cputable.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define SIZE (64 * 1024)
|
||||
|
||||
int test_prot_sao(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
/* 2.06 or later should support SAO */
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
|
||||
|
||||
/*
|
||||
* Ensure we can ask for PROT_SAO.
|
||||
* We can't really verify that it does the right thing, but at least we
|
||||
* confirm the kernel will accept it.
|
||||
*/
|
||||
p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE | PROT_SAO,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
FAIL_IF(p == MAP_FAILED);
|
||||
|
||||
/* Write to the mapping, to at least cause a fault */
|
||||
memset(p, 0xaa, SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test_harness(test_prot_sao, "prot-sao");
|
||||
}
|
202
tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c
Normální soubor
202
tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c
Normální soubor
@@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Test that loads/stores expand the stack segment, or trigger a SEGV, in
|
||||
* various conditions.
|
||||
*
|
||||
* Based on test code by Tom Lane.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define _KB (1024)
|
||||
#define _MB (1024 * 1024)
|
||||
|
||||
volatile char *stack_top_ptr;
|
||||
volatile unsigned long stack_top_sp;
|
||||
volatile char c;
|
||||
|
||||
enum access_type {
|
||||
LOAD,
|
||||
STORE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Consume stack until the stack pointer is below @target_sp, then do an access
|
||||
* (load or store) at offset @delta from either the base of the stack or the
|
||||
* current stack pointer.
|
||||
*/
|
||||
__attribute__ ((noinline))
|
||||
int consume_stack(unsigned long target_sp, unsigned long stack_high, int delta, enum access_type type)
|
||||
{
|
||||
unsigned long target;
|
||||
char stack_cur;
|
||||
|
||||
if ((unsigned long)&stack_cur > target_sp)
|
||||
return consume_stack(target_sp, stack_high, delta, type);
|
||||
else {
|
||||
// We don't really need this, but without it GCC might not
|
||||
// generate a recursive call above.
|
||||
stack_top_ptr = &stack_cur;
|
||||
|
||||
#ifdef __powerpc__
|
||||
asm volatile ("mr %[sp], %%r1" : [sp] "=r" (stack_top_sp));
|
||||
#else
|
||||
asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp));
|
||||
#endif
|
||||
target = stack_high - delta + 1;
|
||||
volatile char *p = (char *)target;
|
||||
|
||||
if (type == STORE)
|
||||
*p = c;
|
||||
else
|
||||
c = *p;
|
||||
|
||||
// Do something to prevent the stack frame being popped prior to
|
||||
// our access above.
|
||||
getpid();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high)
|
||||
{
|
||||
unsigned long start, end;
|
||||
static char buf[4096];
|
||||
char name[128];
|
||||
FILE *f;
|
||||
int rc;
|
||||
|
||||
f = fopen("/proc/self/maps", "r");
|
||||
if (!f) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n",
|
||||
&start, &end, name);
|
||||
if (rc == 2)
|
||||
continue;
|
||||
|
||||
if (rc != 3) {
|
||||
printf("sscanf errored\n");
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strstr(name, needle)) {
|
||||
*low = start;
|
||||
*high = end - 1;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int child(unsigned int stack_used, int delta, enum access_type type)
|
||||
{
|
||||
unsigned long low, stack_high;
|
||||
|
||||
assert(search_proc_maps("[stack]", &low, &stack_high) == 0);
|
||||
|
||||
assert(consume_stack(stack_high - stack_used, stack_high, delta, type) == 0);
|
||||
|
||||
printf("Access OK: %s delta %-7d used size 0x%06x stack high 0x%lx top_ptr %p top sp 0x%lx actual used 0x%lx\n",
|
||||
type == LOAD ? "load" : "store", delta, stack_used, stack_high,
|
||||
stack_top_ptr, stack_top_sp, stack_high - stack_top_sp + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_one(unsigned int stack_used, int delta, enum access_type type)
|
||||
{
|
||||
pid_t pid;
|
||||
int rc;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0)
|
||||
exit(child(stack_used, delta, type));
|
||||
|
||||
assert(waitpid(pid, &rc, 0) != -1);
|
||||
|
||||
if (WIFEXITED(rc) && WEXITSTATUS(rc) == 0)
|
||||
return 0;
|
||||
|
||||
// We don't expect a non-zero exit that's not a signal
|
||||
assert(!WIFEXITED(rc));
|
||||
|
||||
printf("Faulted: %s delta %-7d used size 0x%06x signal %d\n",
|
||||
type == LOAD ? "load" : "store", delta, stack_used,
|
||||
WTERMSIG(rc));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This is fairly arbitrary but is well below any of the targets below,
|
||||
// so that the delta between the stack pointer and the target is large.
|
||||
#define DEFAULT_SIZE (32 * _KB)
|
||||
|
||||
static void test_one_type(enum access_type type, unsigned long page_size, unsigned long rlim_cur)
|
||||
{
|
||||
unsigned long delta;
|
||||
|
||||
// We should be able to access anywhere within the rlimit
|
||||
for (delta = page_size; delta <= rlim_cur; delta += page_size)
|
||||
assert(test_one(DEFAULT_SIZE, delta, type) == 0);
|
||||
|
||||
assert(test_one(DEFAULT_SIZE, rlim_cur, type) == 0);
|
||||
|
||||
// But if we go past the rlimit it should fail
|
||||
assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0);
|
||||
}
|
||||
|
||||
static int test(void)
|
||||
{
|
||||
unsigned long page_size;
|
||||
struct rlimit rlimit;
|
||||
|
||||
page_size = getpagesize();
|
||||
getrlimit(RLIMIT_STACK, &rlimit);
|
||||
printf("Stack rlimit is 0x%lx\n", rlimit.rlim_cur);
|
||||
|
||||
printf("Testing loads ...\n");
|
||||
test_one_type(LOAD, page_size, rlimit.rlim_cur);
|
||||
printf("Testing stores ...\n");
|
||||
test_one_type(STORE, page_size, rlimit.rlim_cur);
|
||||
|
||||
printf("All OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __powerpc__
|
||||
#include "utils.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test_harness(test, "stack_expansion_ldst");
|
||||
}
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
return test();
|
||||
}
|
||||
#endif
|
118
tools/testing/selftests/powerpc/mm/stack_expansion_signal.c
Normální soubor
118
tools/testing/selftests/powerpc/mm/stack_expansion_signal.c
Normální soubor
@@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Test that signal delivery is able to expand the stack segment without
|
||||
* triggering a SEGV.
|
||||
*
|
||||
* Based on test code by Tom Lane.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../pmu/lib.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define _KB (1024)
|
||||
#define _MB (1024 * 1024)
|
||||
|
||||
static char *stack_base_ptr;
|
||||
static char *stack_top_ptr;
|
||||
|
||||
static volatile sig_atomic_t sig_occurred = 0;
|
||||
|
||||
static void sigusr1_handler(int signal_arg)
|
||||
{
|
||||
sig_occurred = 1;
|
||||
}
|
||||
|
||||
static int consume_stack(unsigned int stack_size, union pipe write_pipe)
|
||||
{
|
||||
char stack_cur;
|
||||
|
||||
if ((stack_base_ptr - &stack_cur) < stack_size)
|
||||
return consume_stack(stack_size, write_pipe);
|
||||
else {
|
||||
stack_top_ptr = &stack_cur;
|
||||
|
||||
FAIL_IF(notify_parent(write_pipe));
|
||||
|
||||
while (!sig_occurred)
|
||||
barrier();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int child(unsigned int stack_size, union pipe write_pipe)
|
||||
{
|
||||
struct sigaction act;
|
||||
char stack_base;
|
||||
|
||||
act.sa_handler = sigusr1_handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGUSR1, &act, NULL) < 0)
|
||||
err(1, "sigaction");
|
||||
|
||||
stack_base_ptr = (char *) (((size_t) &stack_base + 65535) & ~65535UL);
|
||||
|
||||
FAIL_IF(consume_stack(stack_size, write_pipe));
|
||||
|
||||
printf("size 0x%06x: OK, stack base %p top %p (%zx used)\n",
|
||||
stack_size, stack_base_ptr, stack_top_ptr,
|
||||
stack_base_ptr - stack_top_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_one_size(unsigned int stack_size)
|
||||
{
|
||||
union pipe read_pipe, write_pipe;
|
||||
pid_t pid;
|
||||
|
||||
FAIL_IF(pipe(read_pipe.fds) == -1);
|
||||
FAIL_IF(pipe(write_pipe.fds) == -1);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
close(read_pipe.read_fd);
|
||||
close(write_pipe.write_fd);
|
||||
exit(child(stack_size, read_pipe));
|
||||
}
|
||||
|
||||
close(read_pipe.write_fd);
|
||||
close(write_pipe.read_fd);
|
||||
FAIL_IF(sync_with_child(read_pipe, write_pipe));
|
||||
|
||||
kill(pid, SIGUSR1);
|
||||
|
||||
FAIL_IF(wait_for_child(pid));
|
||||
|
||||
close(read_pipe.read_fd);
|
||||
close(write_pipe.write_fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test(void)
|
||||
{
|
||||
unsigned int i, size;
|
||||
|
||||
// Test with used stack from 1MB - 64K to 1MB + 64K
|
||||
// Increment by 64 to get more coverage of odd sizes
|
||||
for (i = 0; i < (128 * _KB); i += 64) {
|
||||
size = i + (1 * _MB) - (64 * _KB);
|
||||
FAIL_IF(test_one_size(size));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test_harness(test, "stack_expansion_signal");
|
||||
}
|
@@ -9,6 +9,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <asm/cputable.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "utils.h"
|
||||
@@ -104,6 +105,9 @@ static int test_body(void)
|
||||
struct event events[3];
|
||||
u64 overhead;
|
||||
|
||||
// The STCX_FAIL event we use works on Power8 or later
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, PERF_TYPE_HARDWARE, "instructions");
|
||||
setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "cycles");
|
||||
setup_event(&events[2], PM_STCX_FAIL, PERF_TYPE_RAW, "stcx_fail");
|
||||
|
@@ -91,8 +91,6 @@ int back_to_back_ebbs(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
event_close(&event);
|
||||
|
@@ -42,8 +42,6 @@ int cycles(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
event_close(&event);
|
||||
|
@@ -99,8 +99,6 @@ int cycles_with_freeze(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
printf("EBBs while frozen %d\n", ebbs_while_frozen);
|
||||
|
@@ -71,8 +71,6 @@ int cycles_with_mmcr2(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
event_close(&event);
|
||||
|
@@ -396,8 +396,6 @@ int ebb_child(union pipe read_pipe, union pipe write_pipe)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
event_close(&event);
|
||||
|
@@ -38,8 +38,6 @@ static int victim_child(union pipe read_pipe, union pipe write_pipe)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
FAIL_IF(ebb_state.stats.ebb_count == 0);
|
||||
|
@@ -75,7 +75,6 @@ static int test_body(void)
|
||||
ebb_freeze_pmcs();
|
||||
ebb_global_disable();
|
||||
|
||||
count_pmc(4, sample_period);
|
||||
mtspr(SPRN_PMC4, 0xdead);
|
||||
|
||||
dump_summary_ebb_state();
|
||||
|
@@ -70,13 +70,6 @@ int multi_counter(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
count_pmc(2, sample_period);
|
||||
count_pmc(3, sample_period);
|
||||
count_pmc(4, sample_period);
|
||||
count_pmc(5, sample_period);
|
||||
count_pmc(6, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
|
@@ -61,8 +61,6 @@ static int cycles_child(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_summary_ebb_state();
|
||||
|
||||
event_close(&event);
|
||||
|
@@ -82,8 +82,6 @@ static int test_body(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(1, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
if (mmcr0_mismatch)
|
||||
|
@@ -76,8 +76,6 @@ int pmc56_overflow(void)
|
||||
ebb_global_disable();
|
||||
ebb_freeze_pmcs();
|
||||
|
||||
count_pmc(2, sample_period);
|
||||
|
||||
dump_ebb_state();
|
||||
|
||||
printf("PMC5/6 overflow %d\n", pmc56_overflowed);
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
|
||||
#define __SELFTESTS_POWERPC_PMU_LIB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
@@ -12,6 +12,8 @@
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <asm/cputable.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "lib.h"
|
||||
#include "utils.h"
|
||||
@@ -23,12 +25,9 @@
|
||||
static int per_event_excludes(void)
|
||||
{
|
||||
struct event *e, events[4];
|
||||
char *platform;
|
||||
int i;
|
||||
|
||||
platform = (char *)get_auxv_entry(AT_BASE_PLATFORM);
|
||||
FAIL_IF(!platform);
|
||||
SKIP_IF(strcmp(platform, "power8") != 0);
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
/*
|
||||
* We need to create the events disabled, otherwise the running/enabled
|
||||
|
@@ -150,7 +150,7 @@ static int child(struct shared_info *info)
|
||||
printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
|
||||
user_write, info->amr, pkey1, pkey2, pkey3);
|
||||
|
||||
mtspr(SPRN_AMR, info->amr);
|
||||
set_amr(info->amr);
|
||||
|
||||
/*
|
||||
* We won't use pkey3. This tests whether the kernel restores the UAMOR
|
||||
|
@@ -44,7 +44,7 @@ struct shared_info {
|
||||
unsigned long amr2;
|
||||
|
||||
/* AMR value that ptrace should refuse to write to the child. */
|
||||
unsigned long amr3;
|
||||
unsigned long invalid_amr;
|
||||
|
||||
/* IAMR value the parent expects to read from the child. */
|
||||
unsigned long expected_iamr;
|
||||
@@ -57,8 +57,8 @@ struct shared_info {
|
||||
* (even though they're valid ones) because userspace doesn't have
|
||||
* access to those registers.
|
||||
*/
|
||||
unsigned long new_iamr;
|
||||
unsigned long new_uamor;
|
||||
unsigned long invalid_iamr;
|
||||
unsigned long invalid_uamor;
|
||||
};
|
||||
|
||||
static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
|
||||
@@ -66,11 +66,6 @@ static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
|
||||
return syscall(__NR_pkey_alloc, flags, init_access_rights);
|
||||
}
|
||||
|
||||
static int sys_pkey_free(int pkey)
|
||||
{
|
||||
return syscall(__NR_pkey_free, pkey);
|
||||
}
|
||||
|
||||
static int child(struct shared_info *info)
|
||||
{
|
||||
unsigned long reg;
|
||||
@@ -100,33 +95,37 @@ static int child(struct shared_info *info)
|
||||
|
||||
info->amr1 |= 3ul << pkeyshift(pkey1);
|
||||
info->amr2 |= 3ul << pkeyshift(pkey2);
|
||||
info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3);
|
||||
/*
|
||||
* invalid amr value where we try to force write
|
||||
* things which are deined by a uamor setting.
|
||||
*/
|
||||
info->invalid_amr = info->amr2 | (~0x0UL & ~info->expected_uamor);
|
||||
|
||||
/*
|
||||
* if PKEY_DISABLE_EXECUTE succeeded we should update the expected_iamr
|
||||
*/
|
||||
if (disable_execute)
|
||||
info->expected_iamr |= 1ul << pkeyshift(pkey1);
|
||||
else
|
||||
info->expected_iamr &= ~(1ul << pkeyshift(pkey1));
|
||||
|
||||
info->expected_iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3));
|
||||
|
||||
info->expected_uamor |= 3ul << pkeyshift(pkey1) |
|
||||
3ul << pkeyshift(pkey2);
|
||||
info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2);
|
||||
info->new_uamor |= 3ul << pkeyshift(pkey1);
|
||||
/*
|
||||
* We allocated pkey2 and pkey 3 above. Clear the IAMR bits.
|
||||
*/
|
||||
info->expected_iamr &= ~(1ul << pkeyshift(pkey2));
|
||||
info->expected_iamr &= ~(1ul << pkeyshift(pkey3));
|
||||
|
||||
/*
|
||||
* We won't use pkey3. We just want a plausible but invalid key to test
|
||||
* whether ptrace will let us write to AMR bits we are not supposed to.
|
||||
*
|
||||
* This also tests whether the kernel restores the UAMOR permissions
|
||||
* after a key is freed.
|
||||
* Create an IAMR value different from expected value.
|
||||
* Kernel will reject an IAMR and UAMOR change.
|
||||
*/
|
||||
sys_pkey_free(pkey3);
|
||||
info->invalid_iamr = info->expected_iamr | (1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2));
|
||||
info->invalid_uamor = info->expected_uamor & ~(0x3ul << pkeyshift(pkey1));
|
||||
|
||||
printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
|
||||
user_write, info->amr1, pkey1, pkey2, pkey3);
|
||||
|
||||
mtspr(SPRN_AMR, info->amr1);
|
||||
set_amr(info->amr1);
|
||||
|
||||
/* Wait for parent to read our AMR value and write a new one. */
|
||||
ret = prod_parent(&info->child_sync);
|
||||
@@ -196,9 +195,9 @@ static int parent(struct shared_info *info, pid_t pid)
|
||||
PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync);
|
||||
PARENT_FAIL_IF(ret, &info->child_sync);
|
||||
|
||||
info->amr1 = info->amr2 = info->amr3 = regs[0];
|
||||
info->expected_iamr = info->new_iamr = regs[1];
|
||||
info->expected_uamor = info->new_uamor = regs[2];
|
||||
info->amr1 = info->amr2 = regs[0];
|
||||
info->expected_iamr = regs[1];
|
||||
info->expected_uamor = regs[2];
|
||||
|
||||
/* Wake up child so that it can set itself up. */
|
||||
ret = prod_child(&info->child_sync);
|
||||
@@ -234,10 +233,10 @@ static int parent(struct shared_info *info, pid_t pid)
|
||||
return ret;
|
||||
|
||||
/* Write invalid AMR value in child. */
|
||||
ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1);
|
||||
ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->invalid_amr, 1);
|
||||
PARENT_FAIL_IF(ret, &info->child_sync);
|
||||
|
||||
printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3);
|
||||
printf("%-30s AMR: %016lx\n", ptrace_write_running, info->invalid_amr);
|
||||
|
||||
/* Wake up child so that it can verify it didn't change. */
|
||||
ret = prod_child(&info->child_sync);
|
||||
@@ -249,7 +248,7 @@ static int parent(struct shared_info *info, pid_t pid)
|
||||
|
||||
/* Try to write to IAMR. */
|
||||
regs[0] = info->amr1;
|
||||
regs[1] = info->new_iamr;
|
||||
regs[1] = info->invalid_iamr;
|
||||
ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
|
||||
PARENT_FAIL_IF(!ret, &info->child_sync);
|
||||
|
||||
@@ -257,7 +256,7 @@ static int parent(struct shared_info *info, pid_t pid)
|
||||
ptrace_write_running, regs[0], regs[1]);
|
||||
|
||||
/* Try to write to IAMR and UAMOR. */
|
||||
regs[2] = info->new_uamor;
|
||||
regs[2] = info->invalid_uamor;
|
||||
ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
|
||||
PARENT_FAIL_IF(!ret, &info->child_sync);
|
||||
|
||||
|
@@ -78,6 +78,9 @@ int ptrace_tar(void)
|
||||
pid_t pid;
|
||||
int ret, status;
|
||||
|
||||
// TAR was added in v2.07
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
|
||||
shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT);
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
|
@@ -61,6 +61,8 @@ int ptrace_vsx(void)
|
||||
pid_t pid;
|
||||
int ret, status, i;
|
||||
|
||||
SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_VSX));
|
||||
|
||||
shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
|
||||
|
||||
for (i = 0; i < VEC_MAX; i++)
|
||||
|
@@ -183,6 +183,16 @@ int spectre_v2_test(void)
|
||||
if (miss_percent > 15) {
|
||||
printf("Branch misses > 15%% unexpected in this configuration!\n");
|
||||
printf("Possible mis-match between reported & actual mitigation\n");
|
||||
/*
|
||||
* Such a mismatch may be caused by a guest system
|
||||
* reporting as vulnerable when the host is mitigated.
|
||||
* Return skip code to avoid detecting this as an error.
|
||||
* We are not vulnerable and reporting otherwise, so
|
||||
* missing such a mismatch is safe.
|
||||
*/
|
||||
if (state == VULNERABLE)
|
||||
return 4;
|
||||
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
@@ -8,7 +8,7 @@ build_32bit = $(shell if ($(CC) $(CFLAGS) -m32 -o /dev/null memcmp.c >/dev/null
|
||||
|
||||
TEST_GEN_PROGS := memcmp_64 strlen
|
||||
|
||||
$(OUTPUT)/memcmp_64: memcmp.c
|
||||
$(OUTPUT)/memcmp_64: memcmp.c ../utils.c
|
||||
$(OUTPUT)/memcmp_64: CFLAGS += -m64 -maltivec
|
||||
|
||||
ifeq ($(build_32bit),1)
|
||||
|
@@ -2,7 +2,9 @@
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <asm/cputable.h>
|
||||
#include "utils.h"
|
||||
|
||||
#define SIZE 256
|
||||
@@ -13,6 +15,9 @@
|
||||
#define LARGE_MAX_OFFSET 32
|
||||
#define LARGE_SIZE_START 4096
|
||||
|
||||
/* This is big enough to fit LARGE_SIZE and works on 4K & 64K kernels */
|
||||
#define MAP_SIZE (64 * 1024)
|
||||
|
||||
#define MAX_OFFSET_DIFF_S1_S2 48
|
||||
|
||||
int vmx_count;
|
||||
@@ -68,25 +73,25 @@ static void test_one(char *s1, char *s2, unsigned long max_offset,
|
||||
|
||||
static int testcase(bool islarge)
|
||||
{
|
||||
char *s1;
|
||||
char *s2;
|
||||
unsigned long i;
|
||||
unsigned long i, comp_size, alloc_size;
|
||||
char *p, *s1, *s2;
|
||||
int iterations;
|
||||
|
||||
unsigned long comp_size = (islarge ? LARGE_SIZE : SIZE);
|
||||
unsigned long alloc_size = comp_size + MAX_OFFSET_DIFF_S1_S2;
|
||||
int iterations = islarge ? LARGE_ITERATIONS : ITERATIONS;
|
||||
comp_size = (islarge ? LARGE_SIZE : SIZE);
|
||||
alloc_size = comp_size + MAX_OFFSET_DIFF_S1_S2;
|
||||
iterations = islarge ? LARGE_ITERATIONS : ITERATIONS;
|
||||
|
||||
s1 = memalign(128, alloc_size);
|
||||
if (!s1) {
|
||||
perror("memalign");
|
||||
exit(1);
|
||||
}
|
||||
p = mmap(NULL, 4 * MAP_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
FAIL_IF(p == MAP_FAILED);
|
||||
|
||||
s2 = memalign(128, alloc_size);
|
||||
if (!s2) {
|
||||
perror("memalign");
|
||||
exit(1);
|
||||
}
|
||||
/* Put s1/s2 at the end of a page */
|
||||
s1 = p + MAP_SIZE - alloc_size;
|
||||
s2 = p + 3 * MAP_SIZE - alloc_size;
|
||||
|
||||
/* And unmap the subsequent page to force a fault if we overread */
|
||||
munmap(p + MAP_SIZE, MAP_SIZE);
|
||||
munmap(p + 3 * MAP_SIZE, MAP_SIZE);
|
||||
|
||||
srandom(time(0));
|
||||
|
||||
@@ -147,6 +152,11 @@ static int testcase(bool islarge)
|
||||
|
||||
static int testcases(void)
|
||||
{
|
||||
#ifdef __powerpc64__
|
||||
// vcmpequd used in memcmp_64.S is v2.07
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
|
||||
#endif
|
||||
|
||||
testcase(0);
|
||||
testcase(1);
|
||||
return 0;
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
@@ -88,28 +89,40 @@ void *get_auxv_entry(int type)
|
||||
|
||||
int pick_online_cpu(void)
|
||||
{
|
||||
cpu_set_t mask;
|
||||
int cpu;
|
||||
int ncpus, cpu = -1;
|
||||
cpu_set_t *mask;
|
||||
size_t size;
|
||||
|
||||
CPU_ZERO(&mask);
|
||||
|
||||
if (sched_getaffinity(0, sizeof(mask), &mask)) {
|
||||
perror("sched_getaffinity");
|
||||
ncpus = get_nprocs_conf();
|
||||
size = CPU_ALLOC_SIZE(ncpus);
|
||||
mask = CPU_ALLOC(ncpus);
|
||||
if (!mask) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CPU_ZERO_S(size, mask);
|
||||
|
||||
if (sched_getaffinity(0, size, mask)) {
|
||||
perror("sched_getaffinity");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We prefer a primary thread, but skip 0 */
|
||||
for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8)
|
||||
if (CPU_ISSET(cpu, &mask))
|
||||
return cpu;
|
||||
for (cpu = 8; cpu < ncpus; cpu += 8)
|
||||
if (CPU_ISSET_S(cpu, size, mask))
|
||||
goto done;
|
||||
|
||||
/* Search for anything, but in reverse */
|
||||
for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--)
|
||||
if (CPU_ISSET(cpu, &mask))
|
||||
return cpu;
|
||||
for (cpu = ncpus - 1; cpu >= 0; cpu--)
|
||||
if (CPU_ISSET_S(cpu, size, mask))
|
||||
goto done;
|
||||
|
||||
printf("No cpus in affinity mask?!\n");
|
||||
return -1;
|
||||
|
||||
done:
|
||||
CPU_FREE(mask);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
bool is_ppc64le(void)
|
||||
@@ -293,3 +306,31 @@ void set_dscr(unsigned long val)
|
||||
|
||||
asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR));
|
||||
}
|
||||
|
||||
int using_hash_mmu(bool *using_hash)
|
||||
{
|
||||
char line[128];
|
||||
FILE *f;
|
||||
int rc;
|
||||
|
||||
f = fopen("/proc/cpuinfo", "r");
|
||||
FAIL_IF(!f);
|
||||
|
||||
rc = 0;
|
||||
while (fgets(line, sizeof(line), f) != NULL) {
|
||||
if (strcmp(line, "MMU : Hash\n") == 0) {
|
||||
*using_hash = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(line, "MMU : Radix\n") == 0) {
|
||||
*using_hash = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = -1;
|
||||
out:
|
||||
fclose(f);
|
||||
return rc;
|
||||
}
|
||||
|
Odkázat v novém úkolu
Zablokovat Uživatele