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:
Linus Torvalds
2020-08-07 10:33:50 -07:00
391 změnil soubory, kde provedl 11050 přidání a 5676 odebrání

Zobrazit soubor

@@ -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
*/

Zobrazit soubor

@@ -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"}, \

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

@@ -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.

Zobrazit soubor

@@ -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 */

Zobrazit 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 */

Zobrazit soubor

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

Zobrazit soubor

@@ -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]

Zobrazit soubor

@@ -6,3 +6,4 @@ vmx_preempt
fpu_signal
vmx_signal
vsx_preempt
fpu_denormal

Zobrazit soubor

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

Zobrazit 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");
}

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

@@ -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();

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit 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");
}

Zobrazit 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");
}

Zobrazit soubor

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

Zobrazit 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

Zobrazit 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");
}

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

@@ -42,8 +42,6 @@ int cycles(void)
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
event_close(&event);

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

@@ -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();

Zobrazit soubor

@@ -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++)

Zobrazit soubor

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

Zobrazit soubor

@@ -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)

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

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

Zobrazit soubor

@@ -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) {

Zobrazit soubor

@@ -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++)

Zobrazit soubor

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

Zobrazit soubor

@@ -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)

Zobrazit soubor

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

Zobrazit soubor

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