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

Pull powerpc updates from Michael Ellerman:
 "Highlights:

   - Infrastructure for secure boot on some bare metal Power9 machines.
     The firmware support is still in development, so the code here
     won't actually activate secure boot on any existing systems.

   - A change to xmon (our crash handler / pseudo-debugger) to restrict
     it to read-only mode when the kernel is lockdown'ed, otherwise it's
     trivial to drop into xmon and modify kernel data, such as the
     lockdown state.

   - Support for KASLR on 32-bit BookE machines (Freescale / NXP).

   - Fixes for our flush_icache_range() and __kernel_sync_dicache()
     (VDSO) to work with memory ranges >4GB.

   - Some reworks of the pseries CMM (Cooperative Memory Management)
     driver to make it behave more like other balloon drivers and enable
     some cleanups of generic mm code.

   - A series of fixes to our hardware breakpoint support to properly
     handle unaligned watchpoint addresses.

  Plus a bunch of other smaller improvements, fixes and cleanups.

  Thanks to: Alastair D'Silva, Andrew Donnellan, Aneesh Kumar K.V,
  Anthony Steinhauser, Cédric Le Goater, Chris Packham, Chris Smart,
  Christophe Leroy, Christopher M. Riedl, Christoph Hellwig, Claudio
  Carvalho, Daniel Axtens, David Hildenbrand, Deb McLemore, Diana
  Craciun, Eric Richter, Geert Uytterhoeven, Greg Kroah-Hartman, Greg
  Kurz, Gustavo L. F. Walbon, Hari Bathini, Harish, Jason Yan, Krzysztof
  Kozlowski, Leonardo Bras, Mathieu Malaterre, Mauro S. M. Rodrigues,
  Michal Suchanek, Mimi Zohar, Nathan Chancellor, Nathan Lynch, Nayna
  Jain, Nick Desaulniers, Oliver O'Halloran, Qian Cai, Rasmus Villemoes,
  Ravi Bangoria, Sam Bobroff, Santosh Sivaraj, Scott Wood, Thomas Huth,
  Tyrel Datwyler, Vaibhav Jain, Valentin Longchamp, YueHaibing"

* tag 'powerpc-5.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (144 commits)
  powerpc/fixmap: fix crash with HIGHMEM
  x86/efi: remove unused variables
  powerpc: Define arch_is_kernel_initmem_freed() for lockdep
  powerpc/prom_init: Use -ffreestanding to avoid a reference to bcmp
  powerpc: Avoid clang warnings around setjmp and longjmp
  powerpc: Don't add -mabi= flags when building with Clang
  powerpc: Fix Kconfig indentation
  powerpc/fixmap: don't clear fixmap area in paging_init()
  selftests/powerpc: spectre_v2 test must be built 64-bit
  powerpc/powernv: Disable native PCIe port management
  powerpc/kexec: Move kexec files into a dedicated subdir.
  powerpc/32: Split kexec low level code out of misc_32.S
  powerpc/sysdev: drop simple gpio
  powerpc/83xx: map IMMR with a BAT.
  powerpc/32s: automatically allocate BAT in setbat()
  powerpc/ioremap: warn on early use of ioremap()
  powerpc: Add support for GENERIC_EARLY_IOREMAP
  powerpc/fixmap: Use __fix_to_virt() instead of fix_to_virt()
  powerpc/8xx: use the fixmapped IMMR in cpm_reset()
  powerpc/8xx: add __init to cpm1 init functions
  ...
This commit is contained in:
Linus Torvalds
2019-11-30 14:35:43 -08:00
کامیت 7794b1d418
215فایلهای تغییر یافته به همراه4574 افزوده شده و 2468 حذف شده

مشاهده پرونده

@@ -34,6 +34,7 @@ int pick_online_cpu(void);
int read_debugfs_file(char *debugfs_file, int *result);
int write_debugfs_file(char *debugfs_file, int result);
int read_sysfs_file(char *debugfs_file, char *result, size_t result_size);
void set_dscr(unsigned long val);
int perf_event_open_counter(unsigned int type,
unsigned long config, int group_fd);

مشاهده پرونده

@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
include ../../../../../../scripts/Kbuild.include
noarg:
$(MAKE) -C ../../
@@ -6,7 +8,10 @@ noarg:
CFLAGS += -m64
# Toolchains may build PIE by default which breaks the assembly
LDFLAGS += -no-pie
no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
$(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie)
LDFLAGS += $(no-pie-option)
TEST_GEN_PROGS := reg_access_test event_attributes_test cycles_test \
cycles_with_freeze_test pmc56_overflow_test \

مشاهده پرونده

@@ -148,6 +148,121 @@ static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
return 0;
}
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;
size_t res;
target = malloc(8);
if (!target) {
perror("malloc failed");
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);
if (break_fd < 0) {
free(target);
perror("sys_perf_event_open");
exit(EXIT_FAILURE);
}
/* Shouldn't hit. */
ioctl(break_fd, PERF_EVENT_IOC_RESET);
ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
temp16 = *((__u16 *)target);
*((__u16 *)target) = temp16;
ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
res = read(break_fd, &breaks, sizeof(unsigned long long));
assert(res == sizeof(unsigned long long));
if (breaks == 0) {
printf("TESTED: No overlap\n");
} else {
printf("FAILED: No overlap: %lld != 0\n", breaks);
fail = 1;
}
/* Hit */
ioctl(break_fd, PERF_EVENT_IOC_RESET);
ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
temp16 = *((__u16 *)(target + 1));
*((__u16 *)(target + 1)) = temp16;
ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
res = read(break_fd, &breaks, sizeof(unsigned long long));
assert(res == sizeof(unsigned long long));
if (breaks == 2) {
printf("TESTED: Partial overlap\n");
} else {
printf("FAILED: Partial overlap: %lld != 2\n", breaks);
fail = 1;
}
/* Hit */
ioctl(break_fd, PERF_EVENT_IOC_RESET);
ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
temp16 = *((__u16 *)(target + 5));
*((__u16 *)(target + 5)) = temp16;
ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
res = read(break_fd, &breaks, sizeof(unsigned long long));
assert(res == sizeof(unsigned long long));
if (breaks == 2) {
printf("TESTED: Partial overlap\n");
} else {
printf("FAILED: Partial overlap: %lld != 2\n", breaks);
fail = 1;
}
/* Shouldn't Hit */
ioctl(break_fd, PERF_EVENT_IOC_RESET);
ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
temp16 = *((__u16 *)(target + 6));
*((__u16 *)(target + 6)) = temp16;
ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
res = read(break_fd, &breaks, sizeof(unsigned long long));
assert(res == sizeof(unsigned long long));
if (breaks == 0) {
printf("TESTED: No overlap\n");
} else {
printf("FAILED: No overlap: %lld != 0\n", breaks);
fail = 1;
}
/* Hit */
ioctl(break_fd, PERF_EVENT_IOC_RESET);
ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
temp64 = *((__u64 *)target);
*((__u64 *)target) = temp64;
ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
res = read(break_fd, &breaks, sizeof(unsigned long long));
assert(res == sizeof(unsigned long long));
if (breaks == 2) {
printf("TESTED: Full overlap\n");
} else {
printf("FAILED: Full overlap: %lld != 2\n", breaks);
fail = 1;
}
free(target);
close(break_fd);
return fail;
}
static int runtest(void)
{
int rwflag;
@@ -172,7 +287,9 @@ static int runtest(void)
return ret;
}
}
return 0;
ret = runtest_dar_outside();
return ret;
}

مشاهده پرونده

@@ -22,321 +22,486 @@
#include <sys/wait.h>
#include "ptrace.h"
/* Breakpoint access modes */
enum {
BP_X = 1,
BP_RW = 2,
BP_W = 4,
#define SPRN_PVR 0x11F
#define PVR_8xx 0x00500000
bool is_8xx;
/*
* Use volatile on all global var so that compiler doesn't
* optimise their load/stores. Otherwise selftest can fail.
*/
static volatile __u64 glvar;
#define DAWR_MAX_LEN 512
static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
#define A_LEN 6
#define B_LEN 6
struct gstruct {
__u8 a[A_LEN]; /* double word aligned */
__u8 b[B_LEN]; /* double word unaligned */
};
static volatile struct gstruct gstruct __attribute__((aligned(512)));
static pid_t child_pid;
static struct ppc_debug_info dbginfo;
static void get_dbginfo(void)
static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
{
int ret;
ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
if (ret) {
perror("Can't get breakpoint info\n");
if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
perror("Can't get breakpoint info");
exit(-1);
}
}
static bool hwbreak_present(void)
static bool dawr_present(struct ppc_debug_info *dbginfo)
{
return (dbginfo.num_data_bps != 0);
return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
}
static bool dawr_present(void)
{
return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
}
static void set_breakpoint_addr(void *addr)
{
int ret;
ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr);
if (ret) {
perror("Can't set breakpoint addr\n");
exit(-1);
}
}
static int set_hwbreakpoint_addr(void *addr, int range)
{
int ret;
struct ppc_hw_breakpoint info;
info.version = 1;
info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW;
info.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
if (range > 0)
info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
info.addr = (__u64)addr;
info.addr2 = (__u64)addr + range;
info.condition_value = 0;
ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
if (ret < 0) {
perror("Can't set breakpoint\n");
exit(-1);
}
return ret;
}
static int del_hwbreakpoint_addr(int watchpoint_handle)
{
int ret;
ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle);
if (ret < 0) {
perror("Can't delete hw breakpoint\n");
exit(-1);
}
return ret;
}
#define DAWR_LENGTH_MAX 512
/* Dummy variables to test read/write accesses */
static unsigned long long
dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
__attribute__((aligned(512)));
static unsigned long long *dummy_var = dummy_array;
static void write_var(int len)
{
long long *plval;
char *pcval;
short *psval;
int *pival;
__u8 *pcvar;
__u16 *psvar;
__u32 *pivar;
__u64 *plvar;
switch (len) {
case 1:
pcval = (char *)dummy_var;
*pcval = 0xff;
pcvar = (__u8 *)&glvar;
*pcvar = 0xff;
break;
case 2:
psval = (short *)dummy_var;
*psval = 0xffff;
psvar = (__u16 *)&glvar;
*psvar = 0xffff;
break;
case 4:
pival = (int *)dummy_var;
*pival = 0xffffffff;
pivar = (__u32 *)&glvar;
*pivar = 0xffffffff;
break;
case 8:
plval = (long long *)dummy_var;
*plval = 0xffffffffffffffffLL;
plvar = (__u64 *)&glvar;
*plvar = 0xffffffffffffffffLL;
break;
}
}
static void read_var(int len)
{
char cval __attribute__((unused));
short sval __attribute__((unused));
int ival __attribute__((unused));
long long lval __attribute__((unused));
__u8 cvar __attribute__((unused));
__u16 svar __attribute__((unused));
__u32 ivar __attribute__((unused));
__u64 lvar __attribute__((unused));
switch (len) {
case 1:
cval = *(char *)dummy_var;
cvar = (__u8)glvar;
break;
case 2:
sval = *(short *)dummy_var;
svar = (__u16)glvar;
break;
case 4:
ival = *(int *)dummy_var;
ivar = (__u32)glvar;
break;
case 8:
lval = *(long long *)dummy_var;
lvar = (__u64)glvar;
break;
}
}
/*
* Do the r/w accesses to trigger the breakpoints. And run
* the usual traps.
*/
static void trigger_tests(void)
static void test_workload(void)
{
int len, ret;
__u8 cvar __attribute__((unused));
__u32 ivar __attribute__((unused));
int len = 0;
ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
if (ret) {
perror("Can't be traced?\n");
return;
if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
perror("Child can't be traced?");
exit(-1);
}
/* Wake up father so that it sets up the first test */
kill(getpid(), SIGUSR1);
/* Test write watchpoints */
for (len = 1; len <= sizeof(long); len <<= 1)
/* PTRACE_SET_DEBUGREG, WO test */
for (len = 1; len <= sizeof(glvar); len <<= 1)
write_var(len);
/* Test read/write watchpoints (on read accesses) */
for (len = 1; len <= sizeof(long); len <<= 1)
/* PTRACE_SET_DEBUGREG, RO test */
for (len = 1; len <= sizeof(glvar); len <<= 1)
read_var(len);
/* Test when breakpoint is unset */
/* PTRACE_SET_DEBUGREG, RW test */
for (len = 1; len <= sizeof(glvar); len <<= 1) {
if (rand() % 2)
read_var(len);
else
write_var(len);
}
/* Test write watchpoints */
for (len = 1; len <= sizeof(long); len <<= 1)
write_var(len);
/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
write_var(1);
/* Test read/write watchpoints (on read accesses) */
for (len = 1; len <= sizeof(long); len <<= 1)
read_var(len);
/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
read_var(1);
/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
if (rand() % 2)
write_var(1);
else
read_var(1);
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
gstruct.a[rand() % A_LEN] = 'a';
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
cvar = gstruct.a[rand() % A_LEN];
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
if (rand() % 2)
gstruct.a[rand() % A_LEN] = 'a';
else
cvar = gstruct.a[rand() % A_LEN];
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
gstruct.b[rand() % B_LEN] = 'b';
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
cvar = gstruct.b[rand() % B_LEN];
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
if (rand() % 2)
gstruct.b[rand() % B_LEN] = 'b';
else
cvar = gstruct.b[rand() % B_LEN];
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
if (rand() % 2)
*((int *)(gstruct.a + 4)) = 10;
else
ivar = *((int *)(gstruct.a + 4));
/* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
if (rand() % 2)
big_var[rand() % DAWR_MAX_LEN] = 'a';
else
cvar = big_var[rand() % DAWR_MAX_LEN];
}
static void check_success(const char *msg)
static void check_success(pid_t child_pid, const char *name, const char *type,
unsigned long saddr, int len)
{
const char *msg2;
int status;
siginfo_t siginfo;
unsigned long eaddr = (saddr + len - 1) | 0x7;
saddr &= ~0x7;
/* Wait for the child to SIGTRAP */
wait(&status);
msg2 = "Failed";
ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
msg2 = "Child process hit the breakpoint";
if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
(unsigned long)siginfo.si_addr < saddr ||
(unsigned long)siginfo.si_addr > eaddr) {
printf("%s, %s, len: %d: Fail\n", name, type, len);
exit(-1);
}
printf("%s Result: [%s]\n", msg, msg2);
printf("%s, %s, len: %d: Ok\n", name, type, len);
if (!is_8xx) {
/*
* For ptrace registered watchpoint, signal is generated
* before executing load/store. Singlestep the instruction
* and then continue the test.
*/
ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
wait(NULL);
}
}
static void launch_watchpoints(char *buf, int mode, int len,
struct ppc_debug_info *dbginfo, bool dawr)
static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
{
const char *mode_str;
unsigned long data = (unsigned long)(dummy_var);
int wh, range;
if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
perror("PTRACE_SET_DEBUGREG failed");
exit(-1);
}
}
data &= ~0x7UL;
static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
{
int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
if (mode == BP_W) {
data |= (1UL << 1);
mode_str = "write";
} else {
data |= (1UL << 0);
data |= (1UL << 1);
mode_str = "read";
if (wh <= 0) {
perror("PPC_PTRACE_SETHWDEBUG failed");
exit(-1);
}
return wh;
}
static void ptrace_delhwdebug(pid_t child_pid, int wh)
{
if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
perror("PPC_PTRACE_DELHWDEBUG failed");
exit(-1);
}
}
#define DABR_READ_SHIFT 0
#define DABR_WRITE_SHIFT 1
#define DABR_TRANSLATION_SHIFT 2
static int test_set_debugreg(pid_t child_pid)
{
unsigned long wp_addr = (unsigned long)&glvar;
char *name = "PTRACE_SET_DEBUGREG";
int len;
/* PTRACE_SET_DEBUGREG, WO test*/
wp_addr &= ~0x7UL;
wp_addr |= (1UL << DABR_WRITE_SHIFT);
wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
for (len = 1; len <= sizeof(glvar); len <<= 1) {
ptrace_set_debugreg(child_pid, wp_addr);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "WO", wp_addr, len);
}
/* Set DABR_TRANSLATION bit */
data |= (1UL << 2);
/* PTRACE_SET_DEBUGREG, RO test */
wp_addr &= ~0x7UL;
wp_addr |= (1UL << DABR_READ_SHIFT);
wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
for (len = 1; len <= sizeof(glvar); len <<= 1) {
ptrace_set_debugreg(child_pid, wp_addr);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RO", wp_addr, len);
}
/* use PTRACE_SET_DEBUGREG breakpoints */
set_breakpoint_addr((void *)data);
/* PTRACE_SET_DEBUGREG, RW test */
wp_addr &= ~0x7UL;
wp_addr |= (1Ul << DABR_READ_SHIFT);
wp_addr |= (1UL << DABR_WRITE_SHIFT);
wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
for (len = 1; len <= sizeof(glvar); len <<= 1) {
ptrace_set_debugreg(child_pid, wp_addr);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RW", wp_addr, len);
}
ptrace_set_debugreg(child_pid, 0);
return 0;
}
static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
unsigned long addr, int len)
{
info->version = 1;
info->trigger_type = type;
info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
info->addr = (__u64)addr;
info->addr2 = (__u64)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 void test_sethwdebug_exact(pid_t child_pid)
{
struct ppc_hw_breakpoint info;
unsigned long wp_addr = (unsigned long)&glvar;
char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
int len = 1; /* hardcoded in kernel */
int wh;
/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
check_success(buf);
/* Unregister hw brkpoint */
set_breakpoint_addr(NULL);
check_success(child_pid, name, "WO", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
data = (data & ~7); /* remove dabr control bits */
/* use PPC_PTRACE_SETHWDEBUG breakpoint */
if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
return; /* not supported */
wh = set_hwbreakpoint_addr((void *)data, 0);
/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
check_success(buf);
/* Unregister hw brkpoint */
del_hwbreakpoint_addr(wh);
check_success(child_pid, name, "RO", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
/* try a wider range */
range = 8;
if (dawr)
range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1));
wh = set_hwbreakpoint_addr((void *)data, range);
/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
check_success(buf);
/* Unregister hw brkpoint */
del_hwbreakpoint_addr(wh);
check_success(child_pid, name, "RW", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
}
static void test_sethwdebug_range_aligned(pid_t child_pid)
{
struct ppc_hw_breakpoint info;
unsigned long wp_addr;
char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
int len;
int wh;
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
wp_addr = (unsigned long)&gstruct.a;
len = A_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "WO", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
wp_addr = (unsigned long)&gstruct.a;
len = A_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RO", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
wp_addr = (unsigned long)&gstruct.a;
len = A_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RW", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
}
static void test_sethwdebug_range_unaligned(pid_t child_pid)
{
struct ppc_hw_breakpoint info;
unsigned long wp_addr;
char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
int len;
int wh;
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
wp_addr = (unsigned long)&gstruct.b;
len = B_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "WO", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
wp_addr = (unsigned long)&gstruct.b;
len = B_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RO", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
wp_addr = (unsigned long)&gstruct.b;
len = B_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RW", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
}
static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
{
struct ppc_hw_breakpoint info;
unsigned long wp_addr;
char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
int len;
int wh;
/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
wp_addr = (unsigned long)&gstruct.b;
len = B_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RW", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
}
static void test_sethwdebug_dawr_max_range(pid_t child_pid)
{
struct ppc_hw_breakpoint info;
unsigned long wp_addr;
char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
int len;
int wh;
/* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
wp_addr = (unsigned long)big_var;
len = DAWR_MAX_LEN;
get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
wh = ptrace_sethwdebug(child_pid, &info);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
check_success(child_pid, name, "RW", wp_addr, len);
ptrace_delhwdebug(child_pid, wh);
}
/* Set the breakpoints and check the child successfully trigger them */
static int launch_tests(bool dawr)
static void
run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
{
char buf[1024];
int len, i, status;
test_set_debugreg(child_pid);
if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
test_sethwdebug_exact(child_pid);
struct ppc_debug_info dbginfo;
i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
if (i) {
perror("Can't set breakpoint info\n");
exit(-1);
if (!is_8xx)
test_sethwdebug_range_aligned(child_pid);
if (dawr && !is_8xx) {
test_sethwdebug_range_unaligned(child_pid);
test_sethwdebug_range_unaligned_dar(child_pid);
test_sethwdebug_dawr_max_range(child_pid);
}
}
if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n");
/* Write watchpoint */
for (len = 1; len <= sizeof(long); len <<= 1)
launch_watchpoints(buf, BP_W, len, &dbginfo, dawr);
/* Read-Write watchpoint */
for (len = 1; len <= sizeof(long); len <<= 1)
launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
/*
* Now we have unregistered the breakpoint, access by child
* should not cause SIGTRAP.
*/
wait(&status);
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
printf("FAIL: Child process hit the breakpoint, which is not expected\n");
ptrace(PTRACE_CONT, child_pid, NULL, 0);
return TEST_FAIL;
}
if (WIFEXITED(status))
printf("Child exited normally\n");
return TEST_PASS;
}
static int ptrace_hwbreak(void)
{
pid_t pid;
int ret;
pid_t child_pid;
struct ppc_debug_info dbginfo;
bool dawr;
pid = fork();
if (!pid) {
trigger_tests();
child_pid = fork();
if (!child_pid) {
test_workload();
return 0;
}
wait(NULL);
child_pid = pid;
get_dbginfo(child_pid, &dbginfo);
SKIP_IF(dbginfo.num_data_bps == 0);
get_dbginfo();
SKIP_IF(!hwbreak_present());
dawr = dawr_present();
ret = launch_tests(dawr);
dawr = dawr_present(&dbginfo);
run_tests(child_pid, &dbginfo, dawr);
/* Let the child exit first. */
ptrace(PTRACE_CONT, child_pid, NULL, 0);
wait(NULL);
return ret;
/*
* Testcases exits immediately with -1 on any failure. If
* it has reached here, it means all tests were successful.
*/
return TEST_PASS;
}
int main(int argc, char **argv, char **envp)
{
int pvr = 0;
asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
if (pvr == PVR_8xx)
is_8xx = true;
return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
}

مشاهده پرونده

@@ -73,7 +73,7 @@ trans:
[sprn_texasr]"i"(SPRN_TEXASR), [tar_1]"i"(TAR_1),
[dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), [dscr_2]"i"(DSCR_2),
[tar_3]"i"(TAR_3), [dscr_3]"i"(DSCR_3)
: "memory", "r0", "r1", "r3", "r4", "r5", "r6"
: "memory", "r0", "r3", "r4", "r5", "r6", "lr"
);
/* TM failed, analyse */

مشاهده پرونده

@@ -74,8 +74,8 @@ trans:
"3: ;"
: [res] "=r" (result), [texasr] "=r" (texasr)
: [sprn_texasr] "i" (SPRN_TEXASR)
: "memory", "r0", "r1", "r3", "r4",
"r7", "r8", "r9", "r10", "r11"
: "memory", "r0", "r3", "r4",
"r7", "r8", "r9", "r10", "r11", "lr"
);
if (result) {

مشاهده پرونده

@@ -62,7 +62,7 @@ trans:
[sprn_ppr]"i"(SPRN_PPR), [sprn_texasr]"i"(SPRN_TEXASR),
[tar_1]"i"(TAR_1), [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2),
[dscr_2]"i"(DSCR_2), [cptr1] "b" (&cptr[1])
: "memory", "r0", "r1", "r3", "r4", "r5", "r6"
: "memory", "r0", "r3", "r4", "r5", "r6"
);
/* TM failed, analyse */

مشاهده پرونده

@@ -62,8 +62,8 @@ trans:
"3: ;"
: [res] "=r" (result), [texasr] "=r" (texasr)
: [sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "b" (&cptr[1])
: "memory", "r0", "r1", "r3", "r4",
"r7", "r8", "r9", "r10", "r11"
: "memory", "r0", "r3", "r4",
"r7", "r8", "r9", "r10", "r11", "lr"
);
if (result) {

مشاهده پرونده

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
TEST_GEN_PROGS := rfi_flush
TEST_GEN_PROGS := rfi_flush spectre_v2
top_srcdir = ../../../../..
CFLAGS += -I../../../../../usr/include
@@ -8,3 +8,6 @@ CFLAGS += -I../../../../../usr/include
include ../../lib.mk
$(TEST_GEN_PROGS): ../harness.c ../utils.c
$(OUTPUT)/spectre_v2: CFLAGS += -m64
$(OUTPUT)/spectre_v2: ../pmu/event.c branch_loops.S

مشاهده پرونده

@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2019, Michael Ellerman, IBM Corp.
*/
#include <ppc-asm.h>
.data
jump_table:
.long 0x0
.long (.Lstate_1 - .Lstate_0)
.long (.Lstate_2 - .Lstate_0)
.long (.Lstate_3 - .Lstate_0)
.long (.Lstate_4 - .Lstate_0)
.long (.Lstate_5 - .Lstate_0)
.long (.Lstate_6 - .Lstate_0)
.long (.Lstate_7 - .Lstate_0)
.text
#define ITER_SHIFT 31
.macro state number
.balign 32
.Lstate_\number:
.if \number==7
li r3, 0
.else
li r3, \number+1
.endif
b .Lloop
.endm
FUNC_START(pattern_cache_loop)
li r3, 0
li r4, 1
sldi r4, r4, ITER_SHIFT
.Lloop: cmpdi r4, 0
beqlr
addi r4, r4, -1
ld r6, jump_table@got(%r2)
sldi r5, r3, 2
lwax r6, r5, r6
ld r7, .Lstate_0@got(%r2)
add r6, r6, r7
mtctr r6
bctr
state 0
state 1
state 2
state 3
state 4
state 5
state 6
state 7
FUNC_END(pattern_cache_loop)
FUNC_START(indirect_branch_loop)
li r3, 1
sldi r3, r3, ITER_SHIFT
1: cmpdi r3, 0
beqlr
addi r3, r3, -1
ld r4, 2f@got(%r2)
mtctr r4
bctr
.balign 32
2: b 1b
FUNC_END(indirect_branch_loop)

مشاهده پرونده

@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018-2019 IBM Corporation.
*/
#define __SANE_USERSPACE_TYPES__
#include <sys/types.h>
#include <stdint.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/prctl.h>
#include "utils.h"
#include "../pmu/event.h"
extern void pattern_cache_loop(void);
extern void indirect_branch_loop(void);
static int do_count_loop(struct event *events, bool is_p9, s64 *miss_percent)
{
u64 pred, mpred;
prctl(PR_TASK_PERF_EVENTS_ENABLE);
if (is_p9)
pattern_cache_loop();
else
indirect_branch_loop();
prctl(PR_TASK_PERF_EVENTS_DISABLE);
event_read(&events[0]);
event_read(&events[1]);
// We could scale all the events by running/enabled but we're lazy
// As long as the PMU is uncontended they should all run
FAIL_IF(events[0].result.running != events[0].result.enabled);
FAIL_IF(events[1].result.running != events[1].result.enabled);
pred = events[0].result.value;
mpred = events[1].result.value;
if (is_p9) {
event_read(&events[2]);
event_read(&events[3]);
FAIL_IF(events[2].result.running != events[2].result.enabled);
FAIL_IF(events[3].result.running != events[3].result.enabled);
pred += events[2].result.value;
mpred += events[3].result.value;
}
*miss_percent = 100 * mpred / pred;
return 0;
}
static void setup_event(struct event *e, u64 config, char *name)
{
event_init_named(e, config, name);
e->attr.disabled = 1;
e->attr.exclude_kernel = 1;
e->attr.exclude_hv = 1;
e->attr.exclude_idle = 1;
}
enum spectre_v2_state {
VULNERABLE = 0,
UNKNOWN = 1, // Works with FAIL_IF()
NOT_AFFECTED,
BRANCH_SERIALISATION,
COUNT_CACHE_DISABLED,
COUNT_CACHE_FLUSH_SW,
COUNT_CACHE_FLUSH_HW,
BTB_FLUSH,
};
static enum spectre_v2_state get_sysfs_state(void)
{
enum spectre_v2_state state = UNKNOWN;
char buf[256];
int len;
memset(buf, 0, sizeof(buf));
FAIL_IF(read_sysfs_file("devices/system/cpu/vulnerabilities/spectre_v2", buf, sizeof(buf)));
// Make sure it's NULL terminated
buf[sizeof(buf) - 1] = '\0';
// Trim the trailing newline
len = strlen(buf);
FAIL_IF(len < 1);
buf[len - 1] = '\0';
printf("sysfs reports: '%s'\n", buf);
// Order matters
if (strstr(buf, "Vulnerable"))
state = VULNERABLE;
else if (strstr(buf, "Not affected"))
state = NOT_AFFECTED;
else if (strstr(buf, "Indirect branch serialisation (kernel only)"))
state = BRANCH_SERIALISATION;
else if (strstr(buf, "Indirect branch cache disabled"))
state = COUNT_CACHE_DISABLED;
else if (strstr(buf, "Software count cache flush (hardware accelerated)"))
state = COUNT_CACHE_FLUSH_HW;
else if (strstr(buf, "Software count cache flush"))
state = COUNT_CACHE_FLUSH_SW;
else if (strstr(buf, "Branch predictor state flush"))
state = BTB_FLUSH;
return state;
}
#define PM_BR_PRED_CCACHE 0x040a4 // P8 + P9
#define PM_BR_MPRED_CCACHE 0x040ac // P8 + P9
#define PM_BR_PRED_PCACHE 0x048a0 // P9 only
#define PM_BR_MPRED_PCACHE 0x048b0 // P9 only
#define SPRN_PVR 287
int spectre_v2_test(void)
{
enum spectre_v2_state state;
struct event events[4];
s64 miss_percent;
bool is_p9;
state = get_sysfs_state();
if (state == UNKNOWN) {
printf("Error: couldn't determine spectre_v2 mitigation state?\n");
return -1;
}
memset(events, 0, sizeof(events));
setup_event(&events[0], PM_BR_PRED_CCACHE, "PM_BR_PRED_CCACHE");
setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE");
FAIL_IF(event_open(&events[0]));
FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1);
is_p9 = ((mfspr(SPRN_PVR) >> 16) & 0xFFFF) == 0x4e;
if (is_p9) {
// Count pattern cache too
setup_event(&events[2], PM_BR_PRED_PCACHE, "PM_BR_PRED_PCACHE");
setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE");
FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1);
FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1);
}
FAIL_IF(do_count_loop(events, is_p9, &miss_percent));
event_report_justified(&events[0], 18, 10);
event_report_justified(&events[1], 18, 10);
event_close(&events[0]);
event_close(&events[1]);
if (is_p9) {
event_report_justified(&events[2], 18, 10);
event_report_justified(&events[3], 18, 10);
event_close(&events[2]);
event_close(&events[3]);
}
printf("Miss percent %lld %%\n", miss_percent);
switch (state) {
case VULNERABLE:
case NOT_AFFECTED:
case COUNT_CACHE_FLUSH_SW:
case COUNT_CACHE_FLUSH_HW:
// These should all not affect userspace branch prediction
if (miss_percent > 15) {
printf("Branch misses > 15%% unexpected in this configuration!\n");
printf("Possible mis-match between reported & actual mitigation\n");
return 1;
}
break;
case BRANCH_SERIALISATION:
// This seems to affect userspace branch prediction a bit?
if (miss_percent > 25) {
printf("Branch misses > 25%% unexpected in this configuration!\n");
printf("Possible mis-match between reported & actual mitigation\n");
return 1;
}
break;
case COUNT_CACHE_DISABLED:
if (miss_percent < 95) {
printf("Branch misses < 20%% unexpected in this configuration!\n");
printf("Possible mis-match between reported & actual mitigation\n");
return 1;
}
break;
case UNKNOWN:
case BTB_FLUSH:
printf("Not sure!\n");
return 1;
}
printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n");
return 0;
}
int main(int argc, char *argv[])
{
return test_harness(spectre_v2_test, "spectre_v2");
}

مشاهده پرونده

@@ -42,7 +42,7 @@
#include "utils.h"
/* Selftest defaults */
#define COUNT_MAX 4000 /* Number of interactions */
#define COUNT_MAX 600 /* Number of interactions */
#define THREADS 16 /* Number of threads */
/* Arguments options */

مشاهده پرونده

@@ -10,10 +10,12 @@
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "utils.h"
#include "tm.h"
void trap_signal_handler(int signo, siginfo_t *si, void *uc)
{
@@ -29,6 +31,8 @@ int tm_signal_sigreturn_nt(void)
{
struct sigaction trap_sa;
SKIP_IF(!have_htm());
trap_sa.sa_flags = SA_SIGINFO;
trap_sa.sa_sigaction = trap_signal_handler;

مشاهده پرونده

@@ -127,6 +127,26 @@ bool is_ppc64le(void)
return strcmp(uts.machine, "ppc64le") == 0;
}
int read_sysfs_file(char *fpath, char *result, size_t result_size)
{
char path[PATH_MAX] = "/sys/";
int rc = -1, fd;
strncat(path, fpath, PATH_MAX - strlen(path) - 1);
if ((fd = open(path, O_RDONLY)) < 0)
return rc;
rc = read(fd, result, result_size);
close(fd);
if (rc < 0)
return rc;
return 0;
}
int read_debugfs_file(char *debugfs_file, int *result)
{
int rc = -1, fd;