Merge tag 'powerpc-5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux

Pull powerpc updates from Michael Ellerman:

 - Enable KFENCE for 32-bit.

 - Implement EBPF for 32-bit.

 - Convert 32-bit to do interrupt entry/exit in C.

 - Convert 64-bit BookE to do interrupt entry/exit in C.

 - Changes to our signal handling code to use user_access_begin/end()
   more extensively.

 - Add support for time namespaces (CONFIG_TIME_NS)

 - A series of fixes that allow us to reenable STRICT_KERNEL_RWX.

 - Other smaller features, fixes & cleanups.

Thanks to Alexey Kardashevskiy, Andreas Schwab, Andrew Donnellan, Aneesh
Kumar K.V, Athira Rajeev, Bhaskar Chowdhury, Bixuan Cui, Cédric Le
Goater, Chen Huang, Chris Packham, Christophe Leroy, Christopher M.
Riedl, Colin Ian King, Dan Carpenter, Daniel Axtens, Daniel Henrique
Barboza, David Gibson, Davidlohr Bueso, Denis Efremov, dingsenjie,
Dmitry Safonov, Dominic DeMarco, Fabiano Rosas, Ganesh Goudar, Geert
Uytterhoeven, Geetika Moolchandani, Greg Kurz, Guenter Roeck, Haren
Myneni, He Ying, Jiapeng Chong, Jordan Niethe, Laurent Dufour, Lee
Jones, Leonardo Bras, Li Huafei, Madhavan Srinivasan, Mahesh Salgaonkar,
Masahiro Yamada, Nathan Chancellor, Nathan Lynch, Nicholas Piggin,
Oliver O'Halloran, Paul Menzel, Pu Lehui, Randy Dunlap, Ravi Bangoria,
Rosen Penev, Russell Currey, Santosh Sivaraj, Sebastian Andrzej Siewior,
Segher Boessenkool, Shivaprasad G Bhat, Srikar Dronamraju, Stephen
Rothwell, Thadeu Lima de Souza Cascardo, Thomas Gleixner, Tony Ambardar,
Tyrel Datwyler, Vaibhav Jain, Vincenzo Frascino, Xiongwei Song, Yang Li,
Yu Kuai, and Zhang Yunkai.

* tag 'powerpc-5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (302 commits)
  powerpc/signal32: Fix erroneous SIGSEGV on RT signal return
  powerpc: Avoid clang uninitialized warning in __get_user_size_allowed
  powerpc/papr_scm: Mark nvdimm as unarmed if needed during probe
  powerpc/kvm: Fix build error when PPC_MEM_KEYS/PPC_PSERIES=n
  powerpc/kasan: Fix shadow start address with modules
  powerpc/kernel/iommu: Use largepool as a last resort when !largealloc
  powerpc/kernel/iommu: Align size for IOMMU_PAGE_SIZE() to save TCEs
  powerpc/44x: fix spelling mistake in Kconfig "varients" -> "variants"
  powerpc/iommu: Annotate nested lock for lockdep
  powerpc/iommu: Do not immediately panic when failed IOMMU table allocation
  powerpc/iommu: Allocate it_map by vmalloc
  selftests/powerpc: remove unneeded semicolon
  powerpc/64s: remove unneeded semicolon
  powerpc/eeh: remove unneeded semicolon
  powerpc/selftests: Add selftest to test concurrent perf/ptrace events
  powerpc/selftests/perf-hwbreak: Add testcases for 2nd DAWR
  powerpc/selftests/perf-hwbreak: Coalesce event creation code
  powerpc/selftests/ptrace-hwbreak: Add testcases for 2nd DAWR
  powerpc/configs: Add IBMVNIC to some 64-bit configs
  selftests/powerpc: Add uaccess flush test
  ...
This commit is contained in:
Linus Torvalds
2021-04-30 12:22:28 -07:00
234 changed files with 6890 additions and 5618 deletions

View File

@@ -10,16 +10,7 @@
*
* We create two sets of source and destination buffers, one in regular memory,
* 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
* alterative path for cache-inhibited memory may be provided, e.g. memtrace).
*
* 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

View File

@@ -5,6 +5,7 @@ noarg:
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \
large_vm_fork_separation bad_accesses pkey_exec_prot \
pkey_siginfo stack_expansion_signal stack_expansion_ldst
TEST_PROGS := stress_code_patching.sh
TEST_GEN_PROGS_EXTENDED := tlbie_test
TEST_GEN_FILES := tempfile

View File

@@ -0,0 +1,49 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-or-later
TIMEOUT=30
DEBUFS_DIR=`cat /proc/mounts | grep debugfs | awk '{print $2}'`
if [ ! -e "$DEBUFS_DIR" ]
then
echo "debugfs not found, skipping" 1>&2
exit 4
fi
if [ ! -e "$DEBUFS_DIR/tracing/current_tracer" ]
then
echo "Tracing files not found, skipping" 1>&2
exit 4
fi
echo "Testing for spurious faults when mapping kernel memory..."
if grep -q "FUNCTION TRACING IS CORRUPTED" "$DEBUFS_DIR/tracing/trace"
then
echo "FAILED: Ftrace already dead. Probably due to a spurious fault" 1>&2
exit 1
fi
dmesg -C
START_TIME=`date +%s`
END_TIME=`expr $START_TIME + $TIMEOUT`
while [ `date +%s` -lt $END_TIME ]
do
echo function > $DEBUFS_DIR/tracing/current_tracer
echo nop > $DEBUFS_DIR/tracing/current_tracer
if dmesg | grep -q 'ftrace bug'
then
break
fi
done
echo nop > $DEBUFS_DIR/tracing/current_tracer
if dmesg | grep -q 'ftrace bug'
then
echo "FAILED: Mapping kernel memory causes spurious faults" 1>&2
exit 1
else
echo "OK: Mapping kernel memory does not cause spurious faults"
exit 0
fi

View File

@@ -324,7 +324,7 @@ int compress_file(int argc, char **argv, void *handle)
fprintf(stderr, "error: cannot progress; ");
fprintf(stderr, "too many faults\n");
exit(-1);
};
}
}
fault_tries = NX_MAX_FAULTS; /* Reset for the next chunk */

View File

@@ -14,3 +14,4 @@ perf-hwbreak
core-pkey
ptrace-pkey
ptrace-syscall
ptrace-perf-hwbreak

View File

@@ -2,7 +2,7 @@
TEST_GEN_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \
perf-hwbreak ptrace-syscall
perf-hwbreak ptrace-syscall ptrace-perf-hwbreak
top_srcdir = ../../../../..
include ../../lib.mk

View File

@@ -21,8 +21,13 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/sysinfo.h>
#include <asm/ptrace.h>
#include <elf.h>
#include <pthread.h>
#include <sys/syscall.h>
@@ -30,32 +35,130 @@
#include <linux/hw_breakpoint.h>
#include "utils.h"
#ifndef PPC_DEBUG_FEATURE_DATA_BP_ARCH_31
#define PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 0x20
#endif
#define MAX_LOOPS 10000
#define DAWR_LENGTH_MAX ((0x3f + 1) * 8)
static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid,
int cpu, int group_fd,
unsigned long flags)
int nprocs;
static volatile int a = 10;
static volatile int b = 10;
static volatile char c[512 + 8] __attribute__((aligned(512)));
static void perf_event_attr_set(struct perf_event_attr *attr,
__u32 type, __u64 addr, __u64 len,
bool exclude_user)
{
attr->size = sizeof(*attr);
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
memset(attr, 0, sizeof(struct perf_event_attr));
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(struct perf_event_attr);
attr->bp_type = type;
attr->bp_addr = addr;
attr->bp_len = len;
attr->exclude_kernel = 1;
attr->exclude_hv = 1;
attr->exclude_guest = 1;
attr->exclude_user = exclude_user;
attr->disabled = 1;
}
static int
perf_process_event_open_exclude_user(__u32 type, __u64 addr, __u64 len, bool exclude_user)
{
struct perf_event_attr attr;
perf_event_attr_set(&attr, type, addr, len, exclude_user);
return syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
}
static int perf_process_event_open(__u32 type, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_event_attr_set(&attr, type, addr, len, 0);
return syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
}
static int perf_cpu_event_open(long cpu, __u32 type, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_event_attr_set(&attr, type, addr, len, 0);
return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
}
static void close_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
close(fd[i]);
}
static unsigned long read_fds(int *fd, int n)
{
int i;
unsigned long c = 0;
unsigned long count = 0;
size_t res;
for (i = 0; i < n; i++) {
res = read(fd[i], &c, sizeof(c));
assert(res == sizeof(unsigned long long));
count += c;
}
return count;
}
static void reset_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
ioctl(fd[i], PERF_EVENT_IOC_RESET);
}
static void enable_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
ioctl(fd[i], PERF_EVENT_IOC_ENABLE);
}
static void disable_fds(int *fd, int n)
{
int i;
for (i = 0; i < n; i++)
ioctl(fd[i], PERF_EVENT_IOC_DISABLE);
}
static int perf_systemwide_event_open(int *fd, __u32 type, __u64 addr, __u64 len)
{
int i = 0;
/* Assume online processors are 0 to nprocs for simplisity */
for (i = 0; i < nprocs; i++) {
fd[i] = perf_cpu_event_open(i, type, addr, len);
if (fd[i] < 0) {
close_fds(fd, i);
return fd[i];
}
}
return 0;
}
static inline bool breakpoint_test(int len)
{
struct perf_event_attr attr;
int fd;
/* setup counters */
memset(&attr, 0, sizeof(attr));
attr.disabled = 1;
attr.type = PERF_TYPE_BREAKPOINT;
attr.bp_type = HW_BREAKPOINT_R;
/* bp_addr can point anywhere but needs to be aligned */
attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800;
attr.bp_len = len;
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
fd = perf_process_event_open(HW_BREAKPOINT_R, (__u64)(&fd) & 0xfffffffffffff800, len);
if (fd < 0)
return false;
close(fd);
@@ -75,7 +178,6 @@ static inline bool dawr_supported(void)
static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
{
int i,j;
struct perf_event_attr attr;
size_t res;
unsigned long long breaks, needed;
int readint;
@@ -85,6 +187,7 @@ static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
int break_fd;
int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */
volatile int *k;
__u64 len;
/* align to 0x400 boundary as required by DAWR */
readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) &
@@ -94,19 +197,11 @@ static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
if (arraytest)
ptr = &readintalign[0];
/* setup counters */
memset(&attr, 0, sizeof(attr));
attr.disabled = 1;
attr.type = PERF_TYPE_BREAKPOINT;
attr.bp_type = readwriteflag;
attr.bp_addr = (__u64)ptr;
attr.bp_len = sizeof(int);
if (arraytest)
attr.bp_len = DAWR_LENGTH_MAX;
attr.exclude_user = exclude_user;
break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
len = arraytest ? DAWR_LENGTH_MAX : sizeof(int);
break_fd = perf_process_event_open_exclude_user(readwriteflag, (__u64)ptr,
len, exclude_user);
if (break_fd < 0) {
perror("sys_perf_event_open");
perror("perf_process_event_open_exclude_user");
exit(1);
}
@@ -153,7 +248,6 @@ static int runtest_dar_outside(void)
void *target;
volatile __u16 temp16;
volatile __u64 temp64;
struct perf_event_attr attr;
int break_fd;
unsigned long long breaks;
int fail = 0;
@@ -165,21 +259,11 @@ static int runtest_dar_outside(void)
exit(EXIT_FAILURE);
}
/* setup counters */
memset(&attr, 0, sizeof(attr));
attr.disabled = 1;
attr.type = PERF_TYPE_BREAKPOINT;
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.exclude_guest = 1;
attr.bp_type = HW_BREAKPOINT_RW;
/* watch middle half of target array */
attr.bp_addr = (__u64)(target + 2);
attr.bp_len = 4;
break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
break_fd = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)(target + 2), 4);
if (break_fd < 0) {
free(target);
perror("sys_perf_event_open");
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
@@ -263,11 +347,467 @@ static int runtest_dar_outside(void)
return fail;
}
static void multi_dawr_workload(void)
{
a += 10;
b += 10;
c[512 + 1] += 'a';
}
static int test_process_multi_diff_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, diff addr";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&b, (__u64)sizeof(b));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_process_multi_same_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, same addr";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_process_multi_diff_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, diff addr, one is RO, other is WO";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_R, (__u64)&b, (__u64)sizeof(b));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_process_multi_same_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int fd1, fd2;
char *desc = "Process specific, Two events, same addr, one is RO, other is WO";
size_t res;
fd1 = perf_process_event_open(HW_BREAKPOINT_R, (__u64)&a, (__u64)sizeof(a));
if (fd1 < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
fd2 = perf_process_event_open(HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (fd2 < 0) {
close(fd1);
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd1, PERF_EVENT_IOC_RESET);
ioctl(fd2, PERF_EVENT_IOC_RESET);
ioctl(fd1, PERF_EVENT_IOC_ENABLE);
ioctl(fd2, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd1, PERF_EVENT_IOC_DISABLE);
ioctl(fd2, PERF_EVENT_IOC_DISABLE);
res = read(fd1, &breaks1, sizeof(breaks1));
assert(res == sizeof(unsigned long long));
res = read(fd2, &breaks2, sizeof(breaks2));
assert(res == sizeof(unsigned long long));
close(fd1);
close(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_diff_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, diff addr";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (ret) {
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_RW, (__u64)&b, (__u64)sizeof(b));
if (ret) {
close_fds(fd1, nprocs);
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_same_addr(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, same addr";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (ret) {
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_RW, (__u64)&a, (__u64)sizeof(a));
if (ret) {
close_fds(fd1, nprocs);
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 2 || breaks2 != 2) {
printf("FAILED: %s: %lld != 2 || %lld != 2\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_diff_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, diff addr, one is RO, other is WO";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (ret) {
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_R, (__u64)&b, (__u64)sizeof(b));
if (ret) {
close_fds(fd1, nprocs);
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int test_syswide_multi_same_addr_ro_wo(void)
{
unsigned long long breaks1 = 0, breaks2 = 0;
int *fd1 = malloc(nprocs * sizeof(int));
int *fd2 = malloc(nprocs * sizeof(int));
char *desc = "Systemwide, Two events, same addr, one is RO, other is WO";
int ret;
ret = perf_systemwide_event_open(fd1, HW_BREAKPOINT_W, (__u64)&a, (__u64)sizeof(a));
if (ret) {
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
ret = perf_systemwide_event_open(fd2, HW_BREAKPOINT_R, (__u64)&a, (__u64)sizeof(a));
if (ret) {
close_fds(fd1, nprocs);
perror("perf_systemwide_event_open");
exit(EXIT_FAILURE);
}
reset_fds(fd1, nprocs);
reset_fds(fd2, nprocs);
enable_fds(fd1, nprocs);
enable_fds(fd2, nprocs);
multi_dawr_workload();
disable_fds(fd1, nprocs);
disable_fds(fd2, nprocs);
breaks1 = read_fds(fd1, nprocs);
breaks2 = read_fds(fd2, nprocs);
close_fds(fd1, nprocs);
close_fds(fd2, nprocs);
free(fd1);
free(fd2);
if (breaks1 != 1 || breaks2 != 1) {
printf("FAILED: %s: %lld != 1 || %lld != 1\n", desc, breaks1, breaks2);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
static int runtest_multi_dawr(void)
{
int ret = 0;
ret |= test_process_multi_diff_addr();
ret |= test_process_multi_same_addr();
ret |= test_process_multi_diff_addr_ro_wo();
ret |= test_process_multi_same_addr_ro_wo();
ret |= test_syswide_multi_diff_addr();
ret |= test_syswide_multi_same_addr();
ret |= test_syswide_multi_diff_addr_ro_wo();
ret |= test_syswide_multi_same_addr_ro_wo();
return ret;
}
static int runtest_unaligned_512bytes(void)
{
unsigned long long breaks = 0;
int fd;
char *desc = "Process specific, 512 bytes, unaligned";
__u64 addr = (__u64)&c + 8;
size_t res;
fd = perf_process_event_open(HW_BREAKPOINT_RW, addr, 512);
if (fd < 0) {
perror("perf_process_event_open");
exit(EXIT_FAILURE);
}
ioctl(fd, PERF_EVENT_IOC_RESET);
ioctl(fd, PERF_EVENT_IOC_ENABLE);
multi_dawr_workload();
ioctl(fd, PERF_EVENT_IOC_DISABLE);
res = read(fd, &breaks, sizeof(breaks));
assert(res == sizeof(unsigned long long));
close(fd);
if (breaks != 2) {
printf("FAILED: %s: %lld != 2\n", desc, breaks);
return 1;
}
printf("TESTED: %s\n", desc);
return 0;
}
/* There is no perf api to find number of available watchpoints. Use ptrace. */
static int get_nr_wps(bool *arch_31)
{
struct ppc_debug_info dbginfo;
int child_pid;
child_pid = fork();
if (!child_pid) {
int ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
if (ret) {
perror("PTRACE_TRACEME failed\n");
exit(EXIT_FAILURE);
}
kill(getpid(), SIGUSR1);
sleep(1);
exit(EXIT_SUCCESS);
}
wait(NULL);
if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo)) {
perror("Can't get breakpoint info");
exit(EXIT_FAILURE);
}
*arch_31 = !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_ARCH_31);
return dbginfo.num_data_bps;
}
static int runtest(void)
{
int rwflag;
int exclude_user;
int ret;
bool dawr = dawr_supported();
bool arch_31 = false;
int nr_wps = get_nr_wps(&arch_31);
/*
* perf defines rwflag as two bits read and write and at least
@@ -280,7 +820,7 @@ static int runtest(void)
return ret;
/* if we have the dawr, we can do an array test */
if (!dawr_supported())
if (!dawr)
continue;
ret = runtestsingle(rwflag, exclude_user, 1);
if (ret)
@@ -289,6 +829,19 @@ static int runtest(void)
}
ret = runtest_dar_outside();
if (ret)
return ret;
if (dawr && nr_wps > 1) {
nprocs = get_nprocs();
ret = runtest_multi_dawr();
if (ret)
return ret;
}
if (dawr && arch_31)
ret = runtest_unaligned_512bytes();
return ret;
}

View File

@@ -194,6 +194,18 @@ static void test_workload(void)
big_var[rand() % DAWR_MAX_LEN] = 'a';
else
cvar = big_var[rand() % DAWR_MAX_LEN];
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
gstruct.a[rand() % A_LEN] = 'a';
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
cvar = gstruct.b[rand() % B_LEN];
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
gstruct.a[rand() % A_LEN] = 'a';
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
cvar = gstruct.a[rand() % A_LEN];
}
static void check_success(pid_t child_pid, const char *name, const char *type,
@@ -417,6 +429,69 @@ static void test_sethwdebug_range_aligned(pid_t child_pid)
ptrace_delhwdebug(child_pid, wh);
}
static void test_multi_sethwdebug_range(pid_t child_pid)
{
struct ppc_hw_breakpoint info1, info2;
unsigned long wp_addr1, wp_addr2;
char *name1 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED";
char *name2 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED";
int len1, len2;
int wh1, wh2;
wp_addr1 = (unsigned long)&gstruct.a;
wp_addr2 = (unsigned long)&gstruct.b;
len1 = A_LEN;
len2 = B_LEN;
get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
wh1 = ptrace_sethwdebug(child_pid, &info1);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
wh2 = ptrace_sethwdebug(child_pid, &info2);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name1, "WO", wp_addr1, len1);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name2, "RO", wp_addr2, len2);
ptrace_delhwdebug(child_pid, wh1);
ptrace_delhwdebug(child_pid, wh2);
}
static void test_multi_sethwdebug_range_dawr_overlap(pid_t child_pid)
{
struct ppc_hw_breakpoint info1, info2;
unsigned long wp_addr1, wp_addr2;
char *name = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap";
int len1, len2;
int wh1, wh2;
wp_addr1 = (unsigned long)&gstruct.a;
wp_addr2 = (unsigned long)&gstruct.a;
len1 = A_LEN;
len2 = A_LEN;
get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
wh1 = ptrace_sethwdebug(child_pid, &info1);
/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
wh2 = ptrace_sethwdebug(child_pid, &info2);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "WO", wp_addr1, len1);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RO", wp_addr2, len2);
ptrace_delhwdebug(child_pid, wh1);
ptrace_delhwdebug(child_pid, wh2);
}
static void test_sethwdebug_range_unaligned(pid_t child_pid)
{
struct ppc_hw_breakpoint info;
@@ -504,6 +579,10 @@ run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
test_sethwdebug_range_unaligned(child_pid);
test_sethwdebug_range_unaligned_dar(child_pid);
test_sethwdebug_dawr_max_range(child_pid);
if (dbginfo->num_data_bps > 1) {
test_multi_sethwdebug_range(child_pid);
test_multi_sethwdebug_range_dawr_overlap(child_pid);
}
}
}
}

View File

@@ -0,0 +1,659 @@
// SPDX-License-Identifier: GPL-2.0+
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <linux/hw_breakpoint.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include "ptrace.h"
char data[16];
/* Overlapping address range */
volatile __u64 *ptrace_data1 = (__u64 *)&data[0];
volatile __u64 *perf_data1 = (__u64 *)&data[4];
/* Non-overlapping address range */
volatile __u64 *ptrace_data2 = (__u64 *)&data[0];
volatile __u64 *perf_data2 = (__u64 *)&data[8];
static unsigned long pid_max_addr(void)
{
FILE *fp;
char *line, *c;
char addr[100];
size_t len = 0;
fp = fopen("/proc/kallsyms", "r");
if (!fp) {
printf("Failed to read /proc/kallsyms. Exiting..\n");
exit(EXIT_FAILURE);
}
while (getline(&line, &len, fp) != -1) {
if (!strstr(line, "pid_max") || strstr(line, "pid_max_max") ||
strstr(line, "pid_max_min"))
continue;
strncpy(addr, line, len < 100 ? len : 100);
c = strchr(addr, ' ');
*c = '\0';
return strtoul(addr, &c, 16);
}
fclose(fp);
printf("Could not find pix_max. Exiting..\n");
exit(EXIT_FAILURE);
return -1;
}
static void perf_user_event_attr_set(struct perf_event_attr *attr, __u64 addr, __u64 len)
{
memset(attr, 0, sizeof(struct perf_event_attr));
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(struct perf_event_attr);
attr->bp_type = HW_BREAKPOINT_R;
attr->bp_addr = addr;
attr->bp_len = len;
attr->exclude_kernel = 1;
attr->exclude_hv = 1;
}
static void perf_kernel_event_attr_set(struct perf_event_attr *attr)
{
memset(attr, 0, sizeof(struct perf_event_attr));
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(struct perf_event_attr);
attr->bp_type = HW_BREAKPOINT_R;
attr->bp_addr = pid_max_addr();
attr->bp_len = sizeof(unsigned long);
attr->exclude_user = 1;
attr->exclude_hv = 1;
}
static int perf_cpu_event_open(int cpu, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_user_event_attr_set(&attr, addr, len);
return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
}
static int perf_thread_event_open(pid_t child_pid, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_user_event_attr_set(&attr, addr, len);
return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0);
}
static int perf_thread_cpu_event_open(pid_t child_pid, int cpu, __u64 addr, __u64 len)
{
struct perf_event_attr attr;
perf_user_event_attr_set(&attr, addr, len);
return syscall(__NR_perf_event_open, &attr, child_pid, cpu, -1, 0);
}
static int perf_thread_kernel_event_open(pid_t child_pid)
{
struct perf_event_attr attr;
perf_kernel_event_attr_set(&attr);
return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0);
}
static int perf_cpu_kernel_event_open(int cpu)
{
struct perf_event_attr attr;
perf_kernel_event_attr_set(&attr);
return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
}
static int child(void)
{
int ret;
ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
if (ret) {
printf("Error: PTRACE_TRACEME failed\n");
return 0;
}
kill(getpid(), SIGUSR1); /* --> parent (SIGUSR1) */
return 0;
}
static void ptrace_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
__u64 addr, int len)
{
info->version = 1;
info->trigger_type = type;
info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
info->addr = addr;
info->addr2 = addr + len;
info->condition_value = 0;
if (!len)
info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
else
info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
}
static int ptrace_open(pid_t child_pid, __u64 wp_addr, int len)
{
struct ppc_hw_breakpoint info;
ptrace_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
return ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
}
static int test1(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread event by ptrace)
* if (existing cpu event by perf)
* if (addr range overlaps)
* fail;
*/
perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
if (perf_fd < 0)
return -1;
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd > 0 || errno != ENOSPC)
ret = -1;
close(perf_fd);
return ret;
}
static int test2(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread event by ptrace)
* if (existing cpu event by perf)
* if (addr range does not overlaps)
* allow;
*/
perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2));
if (perf_fd < 0)
return -1;
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
if (ptrace_fd < 0) {
ret = -1;
goto perf_close;
}
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
perf_close:
close(perf_fd);
return ret;
}
static int test3(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread event by ptrace)
* if (existing thread event by perf on the same thread)
* if (addr range overlaps)
* fail;
*/
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1,
sizeof(*perf_data1));
if (perf_fd < 0)
return -1;
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd > 0 || errno != ENOSPC)
ret = -1;
close(perf_fd);
return ret;
}
static int test4(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread event by ptrace)
* if (existing thread event by perf on the same thread)
* if (addr range does not overlaps)
* fail;
*/
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2,
sizeof(*perf_data2));
if (perf_fd < 0)
return -1;
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
if (ptrace_fd < 0) {
ret = -1;
goto perf_close;
}
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
perf_close:
close(perf_fd);
return ret;
}
static int test5(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int cpid;
int ret = 0;
/* Test:
* if (new per thread event by ptrace)
* if (existing thread event by perf on the different thread)
* allow;
*/
cpid = fork();
if (!cpid) {
/* Temporary Child */
pause();
exit(EXIT_SUCCESS);
}
perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1));
if (perf_fd < 0) {
ret = -1;
goto kill_child;
}
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0) {
ret = -1;
goto perf_close;
}
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
perf_close:
close(perf_fd);
kill_child:
kill(cpid, SIGINT);
return ret;
}
static int test6(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread kernel event by perf)
* if (existing thread event by ptrace on the same thread)
* allow;
* -- OR --
* if (new per cpu kernel event by perf)
* if (existing thread event by ptrace)
* allow;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_thread_kernel_event_open(child_pid);
if (perf_fd < 0) {
ret = -1;
goto ptrace_close;
}
close(perf_fd);
perf_fd = perf_cpu_kernel_event_open(0);
if (perf_fd < 0) {
ret = -1;
goto ptrace_close;
}
close(perf_fd);
ptrace_close:
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test7(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread event by perf)
* if (existing thread event by ptrace on the same thread)
* if (addr range overlaps)
* fail;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1,
sizeof(*perf_data1));
if (perf_fd > 0 || errno != ENOSPC)
ret = -1;
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test8(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread event by perf)
* if (existing thread event by ptrace on the same thread)
* if (addr range does not overlaps)
* allow;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2,
sizeof(*perf_data2));
if (perf_fd < 0) {
ret = -1;
goto ptrace_close;
}
close(perf_fd);
ptrace_close:
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test9(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int cpid;
int ret = 0;
/* Test:
* if (new per thread event by perf)
* if (existing thread event by ptrace on the other thread)
* allow;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0)
return -1;
cpid = fork();
if (!cpid) {
/* Temporary Child */
pause();
exit(EXIT_SUCCESS);
}
perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1));
if (perf_fd < 0) {
ret = -1;
goto kill_child;
}
close(perf_fd);
kill_child:
kill(cpid, SIGINT);
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test10(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per cpu event by perf)
* if (existing thread event by ptrace on the same thread)
* if (addr range overlaps)
* fail;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
if (perf_fd > 0 || errno != ENOSPC)
ret = -1;
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test11(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per cpu event by perf)
* if (existing thread event by ptrace on the same thread)
* if (addr range does not overlap)
* allow;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2));
if (perf_fd < 0) {
ret = -1;
goto ptrace_close;
}
close(perf_fd);
ptrace_close:
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test12(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread and per cpu event by perf)
* if (existing thread event by ptrace on the same thread)
* if (addr range overlaps)
* fail;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data1, sizeof(*perf_data1));
if (perf_fd > 0 || errno != ENOSPC)
ret = -1;
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test13(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int ret = 0;
/* Test:
* if (new per thread and per cpu event by perf)
* if (existing thread event by ptrace on the same thread)
* if (addr range does not overlap)
* allow;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
if (ptrace_fd < 0)
return -1;
perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data2, sizeof(*perf_data2));
if (perf_fd < 0) {
ret = -1;
goto ptrace_close;
}
close(perf_fd);
ptrace_close:
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int test14(pid_t child_pid)
{
int perf_fd;
int ptrace_fd;
int cpid;
int ret = 0;
/* Test:
* if (new per thread and per cpu event by perf)
* if (existing thread event by ptrace on the other thread)
* allow;
*/
ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
if (ptrace_fd < 0)
return -1;
cpid = fork();
if (!cpid) {
/* Temporary Child */
pause();
exit(EXIT_SUCCESS);
}
perf_fd = perf_thread_cpu_event_open(cpid, 0, (__u64)perf_data1,
sizeof(*perf_data1));
if (perf_fd < 0) {
ret = -1;
goto kill_child;
}
close(perf_fd);
kill_child:
kill(cpid, SIGINT);
ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
return ret;
}
static int do_test(const char *msg, int (*fun)(pid_t arg), pid_t arg)
{
int ret;
ret = fun(arg);
if (ret)
printf("%s: Error\n", msg);
else
printf("%s: Ok\n", msg);
return ret;
}
char *desc[14] = {
"perf cpu event -> ptrace thread event (Overlapping)",
"perf cpu event -> ptrace thread event (Non-overlapping)",
"perf thread event -> ptrace same thread event (Overlapping)",
"perf thread event -> ptrace same thread event (Non-overlapping)",
"perf thread event -> ptrace other thread event",
"ptrace thread event -> perf kernel event",
"ptrace thread event -> perf same thread event (Overlapping)",
"ptrace thread event -> perf same thread event (Non-overlapping)",
"ptrace thread event -> perf other thread event",
"ptrace thread event -> perf cpu event (Overlapping)",
"ptrace thread event -> perf cpu event (Non-overlapping)",
"ptrace thread event -> perf same thread & cpu event (Overlapping)",
"ptrace thread event -> perf same thread & cpu event (Non-overlapping)",
"ptrace thread event -> perf other thread & cpu event",
};
static int test(pid_t child_pid)
{
int ret = TEST_PASS;
ret |= do_test(desc[0], test1, child_pid);
ret |= do_test(desc[1], test2, child_pid);
ret |= do_test(desc[2], test3, child_pid);
ret |= do_test(desc[3], test4, child_pid);
ret |= do_test(desc[4], test5, child_pid);
ret |= do_test(desc[5], test6, child_pid);
ret |= do_test(desc[6], test7, child_pid);
ret |= do_test(desc[7], test8, child_pid);
ret |= do_test(desc[8], test9, child_pid);
ret |= do_test(desc[9], test10, child_pid);
ret |= do_test(desc[10], test11, child_pid);
ret |= do_test(desc[11], test12, child_pid);
ret |= do_test(desc[12], test13, child_pid);
ret |= do_test(desc[13], test14, child_pid);
return ret;
}
static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
{
if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
perror("Can't get breakpoint info");
exit(-1);
}
}
static int ptrace_perf_hwbreak(void)
{
int ret;
pid_t child_pid;
struct ppc_debug_info dbginfo;
child_pid = fork();
if (!child_pid)
return child();
/* parent */
wait(NULL); /* <-- child (SIGUSR1) */
get_dbginfo(child_pid, &dbginfo);
SKIP_IF(dbginfo.num_data_bps <= 1);
ret = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
SKIP_IF(ret < 0);
close(ret);
ret = test(child_pid);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
return ret;
}
int main(int argc, char *argv[])
{
return test_harness(ptrace_perf_hwbreak, "ptrace-perf-hwbreak");
}

View File

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
TEST_GEN_PROGS := rfi_flush entry_flush spectre_v2
TEST_GEN_PROGS := rfi_flush entry_flush uaccess_flush spectre_v2
top_srcdir = ../../../../..
CFLAGS += -I../../../../../usr/include
@@ -13,3 +13,4 @@ $(OUTPUT)/spectre_v2: CFLAGS += -m64
$(OUTPUT)/spectre_v2: ../pmu/event.c branch_loops.S
$(OUTPUT)/rfi_flush: flush_utils.c
$(OUTPUT)/entry_flush: flush_utils.c
$(OUTPUT)/uaccess_flush: flush_utils.c

View File

@@ -53,7 +53,7 @@ int entry_flush_test(void)
entry_flush = entry_flush_orig;
fd = perf_event_open_counter(PERF_TYPE_RAW, /* L1d miss */ 0x400f0, -1);
fd = perf_event_open_counter(PERF_TYPE_HW_CACHE, PERF_L1D_READ_MISS_CONFIG, -1);
FAIL_IF(fd < 0);
p = (char *)memalign(zero_size, CACHELINE_SIZE);

View File

@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/utsname.h>
#include "utils.h"
#include "flush_utils.h"
@@ -35,6 +36,18 @@ void syscall_loop(char *p, unsigned long iterations,
}
}
void syscall_loop_uaccess(char *p, unsigned long iterations,
unsigned long zero_size)
{
struct utsname utsname;
for (unsigned long i = 0; i < iterations; i++) {
for (unsigned long j = 0; j < zero_size; j += CACHELINE_SIZE)
load(p + j);
uname(&utsname);
}
}
static void sigill_handler(int signr, siginfo_t *info, void *unused)
{
static int warned;

View File

@@ -9,9 +9,16 @@
#define CACHELINE_SIZE 128
#define PERF_L1D_READ_MISS_CONFIG ((PERF_COUNT_HW_CACHE_L1D) | \
(PERF_COUNT_HW_CACHE_OP_READ << 8) | \
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
void syscall_loop(char *p, unsigned long iterations,
unsigned long zero_size);
void syscall_loop_uaccess(char *p, unsigned long iterations,
unsigned long zero_size);
void set_dscr(unsigned long val);
#endif /* _SELFTESTS_POWERPC_SECURITY_FLUSH_UTILS_H */

View File

@@ -54,7 +54,7 @@ int rfi_flush_test(void)
rfi_flush = rfi_flush_orig;
fd = perf_event_open_counter(PERF_TYPE_RAW, /* L1d miss */ 0x400f0, -1);
fd = perf_event_open_counter(PERF_TYPE_HW_CACHE, PERF_L1D_READ_MISS_CONFIG, -1);
FAIL_IF(fd < 0);
p = (char *)memalign(zero_size, CACHELINE_SIZE);

View File

@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 IBM Corporation.
* Copyright 2020 Canonical Ltd.
*/
#define __SANE_USERSPACE_TYPES__
#include <sys/types.h>
#include <stdint.h>
#include <malloc.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "utils.h"
#include "flush_utils.h"
int uaccess_flush_test(void)
{
char *p;
int repetitions = 10;
int fd, passes = 0, iter, rc = 0;
struct perf_event_read v;
__u64 l1d_misses_total = 0;
unsigned long iterations = 100000, zero_size = 24 * 1024;
unsigned long l1d_misses_expected;
int rfi_flush_orig;
int entry_flush_orig;
int uaccess_flush, uaccess_flush_orig;
SKIP_IF(geteuid() != 0);
// The PMU event we use only works on Power7 or later
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
perror("Unable to read powerpc/rfi_flush debugfs file");
SKIP_IF(1);
}
if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
perror("Unable to read powerpc/entry_flush debugfs file");
SKIP_IF(1);
}
if (read_debugfs_file("powerpc/uaccess_flush", &uaccess_flush_orig) < 0) {
perror("Unable to read powerpc/entry_flush debugfs file");
SKIP_IF(1);
}
if (rfi_flush_orig != 0) {
if (write_debugfs_file("powerpc/rfi_flush", 0) < 0) {
perror("error writing to powerpc/rfi_flush debugfs file");
FAIL_IF(1);
}
}
if (entry_flush_orig != 0) {
if (write_debugfs_file("powerpc/entry_flush", 0) < 0) {
perror("error writing to powerpc/entry_flush debugfs file");
FAIL_IF(1);
}
}
uaccess_flush = uaccess_flush_orig;
fd = perf_event_open_counter(PERF_TYPE_HW_CACHE, PERF_L1D_READ_MISS_CONFIG, -1);
FAIL_IF(fd < 0);
p = (char *)memalign(zero_size, CACHELINE_SIZE);
FAIL_IF(perf_event_enable(fd));
// disable L1 prefetching
set_dscr(1);
iter = repetitions;
/*
* We expect to see l1d miss for each cacheline access when entry_flush
* is set. Allow a small variation on this.
*/
l1d_misses_expected = iterations * (zero_size / CACHELINE_SIZE - 2);
again:
FAIL_IF(perf_event_reset(fd));
syscall_loop_uaccess(p, iterations, zero_size);
FAIL_IF(read(fd, &v, sizeof(v)) != sizeof(v));
if (uaccess_flush && v.l1d_misses >= l1d_misses_expected)
passes++;
else if (!uaccess_flush && v.l1d_misses < (l1d_misses_expected / 2))
passes++;
l1d_misses_total += v.l1d_misses;
while (--iter)
goto again;
if (passes < repetitions) {
printf("FAIL (L1D misses with uaccess_flush=%d: %llu %c %lu) [%d/%d failures]\n",
uaccess_flush, l1d_misses_total, uaccess_flush ? '<' : '>',
uaccess_flush ? repetitions * l1d_misses_expected :
repetitions * l1d_misses_expected / 2,
repetitions - passes, repetitions);
rc = 1;
} else {
printf("PASS (L1D misses with uaccess_flush=%d: %llu %c %lu) [%d/%d pass]\n",
uaccess_flush, l1d_misses_total, uaccess_flush ? '>' : '<',
uaccess_flush ? repetitions * l1d_misses_expected :
repetitions * l1d_misses_expected / 2,
passes, repetitions);
}
if (uaccess_flush == uaccess_flush_orig) {
uaccess_flush = !uaccess_flush_orig;
if (write_debugfs_file("powerpc/uaccess_flush", uaccess_flush) < 0) {
perror("error writing to powerpc/uaccess_flush debugfs file");
return 1;
}
iter = repetitions;
l1d_misses_total = 0;
passes = 0;
goto again;
}
perf_event_disable(fd);
close(fd);
set_dscr(0);
if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
perror("unable to restore original value of powerpc/rfi_flush debugfs file");
return 1;
}
if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
perror("unable to restore original value of powerpc/entry_flush debugfs file");
return 1;
}
if (write_debugfs_file("powerpc/uaccess_flush", uaccess_flush_orig) < 0) {
perror("unable to restore original value of powerpc/uaccess_flush debugfs file");
return 1;
}
return rc;
}
int main(int argc, char *argv[])
{
return test_harness(uaccess_flush_test, "uaccess_flush_test");
}

View File

@@ -66,7 +66,7 @@ void trap_signal_handler(int signo, siginfo_t *si, void *uc)
/* Get thread endianness: extract bit LE from MSR */
thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR];
/***
/*
* Little-Endian Machine
*/
@@ -126,7 +126,7 @@ void trap_signal_handler(int signo, siginfo_t *si, void *uc)
}
}
/***
/*
* Big-Endian Machine
*/

View File

@@ -25,12 +25,20 @@ static void fill_function_pointers(void)
if (!vdso)
vdso = dlopen("linux-gate.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
vdso = dlopen("linux-vdso32.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
vdso = dlopen("linux-vdso64.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso) {
pr_err("[WARN]\tfailed to find vDSO\n");
return;
}
vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
if (!vdso_clock_gettime)
vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__kernel_clock_gettime");
if (!vdso_clock_gettime)
pr_err("Warning: failed to find clock_gettime in vDSO\n");