Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,39 @@
# Makefile for powerpc selftests
# ARCH can be overridden by the user for cross compiling
ARCH ?= $(shell uname -m)
ARCH := $(shell echo $(ARCH) | sed -e s/ppc.*/powerpc/)
ifeq ($(ARCH),powerpc)
GIT_VERSION = $(shell git describe --always --long --dirty || echo "unknown")
CC := $(CROSS_COMPILE)$(CC)
CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CURDIR) $(CFLAGS)
export CC CFLAGS
TARGETS = pmu copyloops mm tm primitives
endif
all: $(TARGETS)
$(TARGETS):
$(MAKE) -k -C $@ all
run_tests: all
@for TARGET in $(TARGETS); do \
$(MAKE) -C $$TARGET run_tests; \
done;
clean:
@for TARGET in $(TARGETS); do \
$(MAKE) -C $$TARGET clean; \
done;
rm -f tags
tags:
find . -name '*.c' -o -name '*.h' | xargs ctags
.PHONY: all run_tests clean tags $(TARGETS)

View file

@ -0,0 +1,29 @@
# The loops are all 64-bit code
CFLAGS += -m64
CFLAGS += -I$(CURDIR)
CFLAGS += -D SELFTEST
# Use our CFLAGS for the implicit .S rule
ASFLAGS = $(CFLAGS)
PROGS := copyuser_64 copyuser_power7 memcpy_64 memcpy_power7
EXTRA_SOURCES := validate.c ../harness.c
all: $(PROGS)
copyuser_64: CPPFLAGS += -D COPY_LOOP=test___copy_tofrom_user_base
copyuser_power7: CPPFLAGS += -D COPY_LOOP=test___copy_tofrom_user_power7
memcpy_64: CPPFLAGS += -D COPY_LOOP=test_memcpy
memcpy_power7: CPPFLAGS += -D COPY_LOOP=test_memcpy_power7
$(PROGS): $(EXTRA_SOURCES)
run_tests: all
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
clean:
rm -f $(PROGS) *.o
.PHONY: all run_tests clean

View file

@ -0,0 +1,89 @@
#include <ppc-asm.h>
#define CONFIG_ALTIVEC
#define r1 1
#define vr0 0
#define vr1 1
#define vr2 2
#define vr3 3
#define vr4 4
#define vr5 5
#define vr6 6
#define vr7 7
#define vr8 8
#define vr9 9
#define vr10 10
#define vr11 11
#define vr12 12
#define vr13 13
#define vr14 14
#define vr15 15
#define vr16 16
#define vr17 17
#define vr18 18
#define vr19 19
#define vr20 20
#define vr21 21
#define vr22 22
#define vr23 23
#define vr24 24
#define vr25 25
#define vr26 26
#define vr27 27
#define vr28 28
#define vr29 29
#define vr30 30
#define vr31 31
#define R14 r14
#define R15 r15
#define R16 r16
#define R17 r17
#define R18 r18
#define R19 r19
#define R20 r20
#define R21 r21
#define R22 r22
#define R29 r29
#define R30 r30
#define R31 r31
#define STACKFRAMESIZE 256
#define STK_REG(i) (112 + ((i)-14)*8)
#define _GLOBAL(A) FUNC_START(test_ ## A)
#define _GLOBAL_TOC(A) _GLOBAL(A)
#define PPC_MTOCRF(A, B) mtocrf A, B
FUNC_START(enter_vmx_usercopy)
li r3,1
blr
FUNC_START(exit_vmx_usercopy)
li r3,0
blr
FUNC_START(enter_vmx_copy)
li r3,1
blr
FUNC_START(exit_vmx_copy)
blr
FUNC_START(memcpy_power7)
blr
FUNC_START(__copy_tofrom_user_power7)
blr
FUNC_START(__copy_tofrom_user_base)
blr
#define BEGIN_FTR_SECTION
#define FTR_SECTION_ELSE
#define ALT_FTR_SECTION_END_IFCLR(x)
#define ALT_FTR_SECTION_END(x, y)
#define END_FTR_SECTION_IFCLR(x)

View file

@ -0,0 +1 @@
../../../../../arch/powerpc/lib/copyuser_64.S

View file

@ -0,0 +1 @@
../../../../../arch/powerpc/lib/copyuser_power7.S

View file

@ -0,0 +1 @@
../../../../../arch/powerpc/lib/memcpy_64.S

View file

@ -0,0 +1 @@
../../../../../arch/powerpc/lib/memcpy_power7.S

View file

@ -0,0 +1,99 @@
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "../utils.h"
#define MAX_LEN 8192
#define MAX_OFFSET 16
#define MIN_REDZONE 128
#define BUFLEN (MAX_LEN+MAX_OFFSET+2*MIN_REDZONE)
#define POISON 0xa5
unsigned long COPY_LOOP(void *to, const void *from, unsigned long size);
static void do_one(char *src, char *dst, unsigned long src_off,
unsigned long dst_off, unsigned long len, void *redzone,
void *fill)
{
char *srcp, *dstp;
unsigned long ret;
unsigned long i;
srcp = src + MIN_REDZONE + src_off;
dstp = dst + MIN_REDZONE + dst_off;
memset(src, POISON, BUFLEN);
memset(dst, POISON, BUFLEN);
memcpy(srcp, fill, len);
ret = COPY_LOOP(dstp, srcp, len);
if (ret && ret != (unsigned long)dstp) {
printf("(%p,%p,%ld) returned %ld\n", dstp, srcp, len, ret);
abort();
}
if (memcmp(dstp, srcp, len)) {
printf("(%p,%p,%ld) miscompare\n", dstp, srcp, len);
printf("src: ");
for (i = 0; i < len; i++)
printf("%02x ", srcp[i]);
printf("\ndst: ");
for (i = 0; i < len; i++)
printf("%02x ", dstp[i]);
printf("\n");
abort();
}
if (memcmp(dst, redzone, dstp - dst)) {
printf("(%p,%p,%ld) redzone before corrupted\n",
dstp, srcp, len);
abort();
}
if (memcmp(dstp+len, redzone, dst+BUFLEN-(dstp+len))) {
printf("(%p,%p,%ld) redzone after corrupted\n",
dstp, srcp, len);
abort();
}
}
int test_copy_loop(void)
{
char *src, *dst, *redzone, *fill;
unsigned long len, src_off, dst_off;
unsigned long i;
src = memalign(BUFLEN, BUFLEN);
dst = memalign(BUFLEN, BUFLEN);
redzone = malloc(BUFLEN);
fill = malloc(BUFLEN);
if (!src || !dst || !redzone || !fill) {
fprintf(stderr, "malloc failed\n");
exit(1);
}
memset(redzone, POISON, BUFLEN);
/* Fill with sequential bytes */
for (i = 0; i < BUFLEN; i++)
fill[i] = i & 0xff;
for (len = 1; len < MAX_LEN; len++) {
for (src_off = 0; src_off < MAX_OFFSET; src_off++) {
for (dst_off = 0; dst_off < MAX_OFFSET; dst_off++) {
do_one(src, dst, src_off, dst_off, len,
redzone, fill);
}
}
}
return 0;
}
int main(void)
{
return test_harness(test_copy_loop, str(COPY_LOOP));
}

View file

@ -0,0 +1,114 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "subunit.h"
#include "utils.h"
#define TIMEOUT 120
#define KILL_TIMEOUT 5
int run_test(int (test_function)(void), char *name)
{
bool terminated;
int rc, status;
pid_t pid;
/* Make sure output is flushed before forking */
fflush(stdout);
pid = fork();
if (pid == 0) {
setpgid(0, 0);
exit(test_function());
} else if (pid == -1) {
perror("fork");
return 1;
}
setpgid(pid, pid);
/* Wake us up in timeout seconds */
alarm(TIMEOUT);
terminated = false;
wait:
rc = waitpid(pid, &status, 0);
if (rc == -1) {
if (errno != EINTR) {
printf("unknown error from waitpid\n");
return 1;
}
if (terminated) {
printf("!! force killing %s\n", name);
kill(-pid, SIGKILL);
return 1;
} else {
printf("!! killing %s\n", name);
kill(-pid, SIGTERM);
terminated = true;
alarm(KILL_TIMEOUT);
goto wait;
}
}
/* Kill anything else in the process group that is still running */
kill(-pid, SIGTERM);
if (WIFEXITED(status))
status = WEXITSTATUS(status);
else {
if (WIFSIGNALED(status))
printf("!! child died by signal %d\n", WTERMSIG(status));
else
printf("!! child died by unknown cause\n");
status = 1; /* Signal or other */
}
return status;
}
static void alarm_handler(int signum)
{
/* Jut wake us up from waitpid */
}
static struct sigaction alarm_action = {
.sa_handler = alarm_handler,
};
int test_harness(int (test_function)(void), char *name)
{
int rc;
test_start(name);
test_set_git_version(GIT_VERSION);
if (sigaction(SIGALRM, &alarm_action, NULL)) {
perror("sigaction");
test_error(name);
return 1;
}
rc = run_test(test_function, name);
if (rc == MAGIC_SKIP_RETURN_VALUE)
test_skip(name);
else
test_finish(name, rc);
return rc;
}

View file

@ -0,0 +1,18 @@
noarg:
$(MAKE) -C ../
PROGS := hugetlb_vs_thp_test
all: $(PROGS)
$(PROGS): ../harness.c
run_tests: all
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
clean:
rm -f $(PROGS)
.PHONY: all run_tests clean

View file

@ -0,0 +1,72 @@
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include "utils.h"
/* This must match the huge page & THP size */
#define SIZE (16 * 1024 * 1024)
static int test_body(void)
{
void *addr;
char *p;
addr = (void *)0xa0000000;
p = mmap(addr, SIZE, PROT_READ | PROT_WRITE,
MAP_HUGETLB | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (p != MAP_FAILED) {
/*
* Typically the mmap will fail because no huge pages are
* allocated on the system. But if there are huge pages
* allocated the mmap will succeed. That's fine too, we just
* munmap here before continuing.
*/
munmap(addr, SIZE);
}
p = mmap(addr, SIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED) {
printf("Mapping failed @ %p\n", addr);
perror("mmap");
return 1;
}
/*
* Either a user or kernel access is sufficient to trigger the bug.
* A kernel access is easier to spot & debug, as it will trigger the
* softlockup or RCU stall detectors, and when the system is kicked
* into xmon we get a backtrace in the kernel.
*
* A good option is:
* getcwd(p, SIZE);
*
* For the purposes of this testcase it's preferable to spin in
* userspace, so the harness can kill us if we get stuck. That way we
* see a test failure rather than a dead system.
*/
*p = 0xf;
munmap(addr, SIZE);
return 0;
}
static int test_main(void)
{
int i;
/* 10,000 because it's a "bunch", and completes reasonably quickly */
for (i = 0; i < 10000; i++)
if (test_body())
return 1;
return 0;
}
int main(void)
{
return test_harness(test_main, "hugetlb_vs_thp");
}

View file

@ -0,0 +1,38 @@
noarg:
$(MAKE) -C ../
PROGS := count_instructions l3_bank_test per_event_excludes
EXTRA_SOURCES := ../harness.c event.c lib.c
SUB_TARGETS = ebb
all: $(PROGS) $(SUB_TARGETS)
$(PROGS): $(EXTRA_SOURCES)
# loop.S can only be built 64-bit
count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
$(CC) $(CFLAGS) -m64 -o $@ $^
run_tests: all sub_run_tests
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
clean: sub_clean
rm -f $(PROGS) loop.o
$(SUB_TARGETS):
$(MAKE) -k -C $@ all
sub_run_tests: all
@for TARGET in $(SUB_TARGETS); do \
$(MAKE) -C $$TARGET run_tests; \
done;
sub_clean:
@for TARGET in $(SUB_TARGETS); do \
$(MAKE) -C $$TARGET clean; \
done;
.PHONY: all run_tests clean sub_run_tests sub_clean $(SUB_TARGETS)

View file

@ -0,0 +1,147 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "event.h"
#include "utils.h"
#include "lib.h"
extern void thirty_two_instruction_loop(u64 loops);
static void setup_event(struct event *e, u64 config, char *name)
{
event_init_opts(e, config, PERF_TYPE_HARDWARE, name);
e->attr.disabled = 1;
e->attr.exclude_kernel = 1;
e->attr.exclude_hv = 1;
e->attr.exclude_idle = 1;
}
static int do_count_loop(struct event *events, u64 instructions,
u64 overhead, bool report)
{
s64 difference, expected;
double percentage;
prctl(PR_TASK_PERF_EVENTS_ENABLE);
/* Run for 1M instructions */
thirty_two_instruction_loop(instructions >> 5);
prctl(PR_TASK_PERF_EVENTS_DISABLE);
event_read(&events[0]);
event_read(&events[1]);
expected = instructions + overhead;
difference = events[0].result.value - expected;
percentage = (double)difference / events[0].result.value * 100;
if (report) {
event_report(&events[0]);
event_report(&events[1]);
printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);
printf("Expected %llu\n", expected);
printf("Actual %llu\n", events[0].result.value);
printf("Delta %lld, %f%%\n", difference, percentage);
}
event_reset(&events[0]);
event_reset(&events[1]);
if (difference < 0)
difference = -difference;
/* Tolerate a difference below 0.0001 % */
difference *= 10000 * 100;
if (difference / events[0].result.value)
return -1;
return 0;
}
/* Count how many instructions it takes to do a null loop */
static u64 determine_overhead(struct event *events)
{
u64 current, overhead;
int i;
do_count_loop(events, 0, 0, false);
overhead = events[0].result.value;
for (i = 0; i < 100; i++) {
do_count_loop(events, 0, 0, false);
current = events[0].result.value;
if (current < overhead) {
printf("Replacing overhead %llu with %llu\n", overhead, current);
overhead = current;
}
}
return overhead;
}
static int test_body(void)
{
struct event events[2];
u64 overhead;
setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions");
setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles");
if (event_open(&events[0])) {
perror("perf_event_open");
return -1;
}
if (event_open_with_group(&events[1], events[0].fd)) {
perror("perf_event_open");
return -1;
}
overhead = determine_overhead(events);
printf("Overhead of null loop: %llu instructions\n", overhead);
/* Run for 1Mi instructions */
FAIL_IF(do_count_loop(events, 1000000, overhead, true));
/* Run for 10Mi instructions */
FAIL_IF(do_count_loop(events, 10000000, overhead, true));
/* Run for 100Mi instructions */
FAIL_IF(do_count_loop(events, 100000000, overhead, true));
/* Run for 1Bi instructions */
FAIL_IF(do_count_loop(events, 1000000000, overhead, true));
/* Run for 16Bi instructions */
FAIL_IF(do_count_loop(events, 16000000000, overhead, true));
/* Run for 64Bi instructions */
FAIL_IF(do_count_loop(events, 64000000000, overhead, true));
event_close(&events[0]);
event_close(&events[1]);
return 0;
}
static int count_instructions(void)
{
return eat_cpu(test_body);
}
int main(void)
{
return test_harness(count_instructions, "count_instructions");
}

View file

@ -0,0 +1,33 @@
noarg:
$(MAKE) -C ../../
# The EBB handler is 64-bit code and everything links against it
CFLAGS += -m64
PROGS := reg_access_test event_attributes_test cycles_test \
cycles_with_freeze_test pmc56_overflow_test \
ebb_vs_cpu_event_test cpu_event_vs_ebb_test \
cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test \
task_event_pinned_vs_ebb_test multi_ebb_procs_test \
multi_counter_test pmae_handling_test \
close_clears_pmcc_test instruction_count_test \
fork_cleanup_test ebb_on_child_test \
ebb_on_willing_child_test back_to_back_ebbs_test \
lost_exception_test no_handler_test \
cycles_with_mmcr2_test
all: $(PROGS)
$(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c busy_loop.S
instruction_count_test: ../loop.S
lost_exception_test: ../lib.c
run_tests: all
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
clean:
rm -f $(PROGS)

View file

@ -0,0 +1,106 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
#define NUMBER_OF_EBBS 50
/*
* Test that if we overflow the counter while in the EBB handler, we take
* another EBB on exiting from the handler.
*
* We do this by counting with a stupidly low sample period, causing us to
* overflow the PMU while we're still in the EBB handler, leading to another
* EBB.
*
* We get out of what would otherwise be an infinite loop by leaving the
* counter frozen once we've taken enough EBBs.
*/
static void ebb_callee(void)
{
uint64_t siar, val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
/* Resets the PMC */
count_pmc(1, sample_period);
out:
if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS)
/* Reset but leave counters frozen */
reset_ebb_with_clear_mask(MMCR0_PMAO);
else
/* Unfreezes */
reset_ebb();
/* Do some stuff to chew some cycles and pop the counter */
siar = mfspr(SPRN_SIAR);
trace_log_reg(ebb_state.trace, SPRN_SIAR, siar);
val = mfspr(SPRN_PMC1);
trace_log_reg(ebb_state.trace, SPRN_PMC1, val);
val = mfspr(SPRN_MMCR0);
trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
}
int back_to_back_ebbs(void)
{
struct event event;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(ebb_callee);
FAIL_IF(ebb_event_enable(&event));
sample_period = 5;
ebb_freeze_pmcs();
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
ebb_global_enable();
ebb_unfreeze_pmcs();
while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS)
FAIL_IF(core_busy_loop());
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS);
return 0;
}
int main(void)
{
return test_harness(back_to_back_ebbs, "back_to_back_ebbs");
}

View file

@ -0,0 +1,271 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <ppc-asm.h>
.text
FUNC_START(core_busy_loop)
stdu %r1, -168(%r1)
std r14, 160(%r1)
std r15, 152(%r1)
std r16, 144(%r1)
std r17, 136(%r1)
std r18, 128(%r1)
std r19, 120(%r1)
std r20, 112(%r1)
std r21, 104(%r1)
std r22, 96(%r1)
std r23, 88(%r1)
std r24, 80(%r1)
std r25, 72(%r1)
std r26, 64(%r1)
std r27, 56(%r1)
std r28, 48(%r1)
std r29, 40(%r1)
std r30, 32(%r1)
std r31, 24(%r1)
li r3, 0x3030
std r3, -96(%r1)
li r4, 0x4040
std r4, -104(%r1)
li r5, 0x5050
std r5, -112(%r1)
li r6, 0x6060
std r6, -120(%r1)
li r7, 0x7070
std r7, -128(%r1)
li r8, 0x0808
std r8, -136(%r1)
li r9, 0x0909
std r9, -144(%r1)
li r10, 0x1010
std r10, -152(%r1)
li r11, 0x1111
std r11, -160(%r1)
li r14, 0x1414
std r14, -168(%r1)
li r15, 0x1515
std r15, -176(%r1)
li r16, 0x1616
std r16, -184(%r1)
li r17, 0x1717
std r17, -192(%r1)
li r18, 0x1818
std r18, -200(%r1)
li r19, 0x1919
std r19, -208(%r1)
li r20, 0x2020
std r20, -216(%r1)
li r21, 0x2121
std r21, -224(%r1)
li r22, 0x2222
std r22, -232(%r1)
li r23, 0x2323
std r23, -240(%r1)
li r24, 0x2424
std r24, -248(%r1)
li r25, 0x2525
std r25, -256(%r1)
li r26, 0x2626
std r26, -264(%r1)
li r27, 0x2727
std r27, -272(%r1)
li r28, 0x2828
std r28, -280(%r1)
li r29, 0x2929
std r29, -288(%r1)
li r30, 0x3030
li r31, 0x3131
li r3, 0
0: addi r3, r3, 1
cmpwi r3, 100
blt 0b
/* Return 1 (fail) unless we get through all the checks */
li r3, 1
/* Check none of our registers have been corrupted */
cmpwi r4, 0x4040
bne 1f
cmpwi r5, 0x5050
bne 1f
cmpwi r6, 0x6060
bne 1f
cmpwi r7, 0x7070
bne 1f
cmpwi r8, 0x0808
bne 1f
cmpwi r9, 0x0909
bne 1f
cmpwi r10, 0x1010
bne 1f
cmpwi r11, 0x1111
bne 1f
cmpwi r14, 0x1414
bne 1f
cmpwi r15, 0x1515
bne 1f
cmpwi r16, 0x1616
bne 1f
cmpwi r17, 0x1717
bne 1f
cmpwi r18, 0x1818
bne 1f
cmpwi r19, 0x1919
bne 1f
cmpwi r20, 0x2020
bne 1f
cmpwi r21, 0x2121
bne 1f
cmpwi r22, 0x2222
bne 1f
cmpwi r23, 0x2323
bne 1f
cmpwi r24, 0x2424
bne 1f
cmpwi r25, 0x2525
bne 1f
cmpwi r26, 0x2626
bne 1f
cmpwi r27, 0x2727
bne 1f
cmpwi r28, 0x2828
bne 1f
cmpwi r29, 0x2929
bne 1f
cmpwi r30, 0x3030
bne 1f
cmpwi r31, 0x3131
bne 1f
/* Load junk into all our registers before we reload them from the stack. */
li r3, 0xde
li r4, 0xad
li r5, 0xbe
li r6, 0xef
li r7, 0xde
li r8, 0xad
li r9, 0xbe
li r10, 0xef
li r11, 0xde
li r14, 0xad
li r15, 0xbe
li r16, 0xef
li r17, 0xde
li r18, 0xad
li r19, 0xbe
li r20, 0xef
li r21, 0xde
li r22, 0xad
li r23, 0xbe
li r24, 0xef
li r25, 0xde
li r26, 0xad
li r27, 0xbe
li r28, 0xef
li r29, 0xdd
ld r3, -96(%r1)
cmpwi r3, 0x3030
bne 1f
ld r4, -104(%r1)
cmpwi r4, 0x4040
bne 1f
ld r5, -112(%r1)
cmpwi r5, 0x5050
bne 1f
ld r6, -120(%r1)
cmpwi r6, 0x6060
bne 1f
ld r7, -128(%r1)
cmpwi r7, 0x7070
bne 1f
ld r8, -136(%r1)
cmpwi r8, 0x0808
bne 1f
ld r9, -144(%r1)
cmpwi r9, 0x0909
bne 1f
ld r10, -152(%r1)
cmpwi r10, 0x1010
bne 1f
ld r11, -160(%r1)
cmpwi r11, 0x1111
bne 1f
ld r14, -168(%r1)
cmpwi r14, 0x1414
bne 1f
ld r15, -176(%r1)
cmpwi r15, 0x1515
bne 1f
ld r16, -184(%r1)
cmpwi r16, 0x1616
bne 1f
ld r17, -192(%r1)
cmpwi r17, 0x1717
bne 1f
ld r18, -200(%r1)
cmpwi r18, 0x1818
bne 1f
ld r19, -208(%r1)
cmpwi r19, 0x1919
bne 1f
ld r20, -216(%r1)
cmpwi r20, 0x2020
bne 1f
ld r21, -224(%r1)
cmpwi r21, 0x2121
bne 1f
ld r22, -232(%r1)
cmpwi r22, 0x2222
bne 1f
ld r23, -240(%r1)
cmpwi r23, 0x2323
bne 1f
ld r24, -248(%r1)
cmpwi r24, 0x2424
bne 1f
ld r25, -256(%r1)
cmpwi r25, 0x2525
bne 1f
ld r26, -264(%r1)
cmpwi r26, 0x2626
bne 1f
ld r27, -272(%r1)
cmpwi r27, 0x2727
bne 1f
ld r28, -280(%r1)
cmpwi r28, 0x2828
bne 1f
ld r29, -288(%r1)
cmpwi r29, 0x2929
bne 1f
/* Load 0 (success) to return */
li r3, 0
1: ld r14, 160(%r1)
ld r15, 152(%r1)
ld r16, 144(%r1)
ld r17, 136(%r1)
ld r18, 128(%r1)
ld r19, 120(%r1)
ld r20, 112(%r1)
ld r21, 104(%r1)
ld r22, 96(%r1)
ld r23, 88(%r1)
ld r24, 80(%r1)
ld r25, 72(%r1)
ld r26, 64(%r1)
ld r27, 56(%r1)
ld r28, 48(%r1)
ld r29, 40(%r1)
ld r30, 32(%r1)
ld r31, 24(%r1)
addi %r1, %r1, 168
blr

View file

@ -0,0 +1,59 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include "ebb.h"
/*
* Test that closing the EBB event clears MMCR0_PMCC, preventing further access
* by userspace to the PMU hardware.
*/
int close_clears_pmcc(void)
{
struct event event;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 1)
FAIL_IF(core_busy_loop());
ebb_global_disable();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
/* The real test is here, do we take a SIGILL when writing PMU regs now
* that we have closed the event. We expect that we will. */
FAIL_IF(catch_sigill(write_pmc1));
/* We should still be able to read EBB regs though */
mfspr(SPRN_EBBHR);
mfspr(SPRN_EBBRR);
mfspr(SPRN_BESCR);
return 0;
}
int main(void)
{
return test_harness(close_clears_pmcc, "close_clears_pmcc");
}

View file

@ -0,0 +1,93 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event
* should remain and the EBB event should fail to enable.
*/
static int setup_cpu_event(struct event *event, int cpu)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.pinned = 1;
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(event, cpu));
FAIL_IF(event_enable(event));
return 0;
}
int cpu_event_pinned_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
int cpu, rc;
pid_t pid;
cpu = pick_online_cpu();
FAIL_IF(cpu < 0);
FAIL_IF(bind_to_cpu(cpu));
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the cpu event first */
rc = setup_cpu_event(&event, cpu);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* We expect it to fail to read the event */
FAIL_IF(wait_for_child(pid) != 2);
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The cpu event should have run */
FAIL_IF(event.result.value == 0);
FAIL_IF(event.result.enabled != event.result.running);
return 0;
}
int main(void)
{
return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb");
}

View file

@ -0,0 +1,89 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a cpu event vs an EBB - in that order. The EBB should force the cpu
* event off the PMU.
*/
static int setup_cpu_event(struct event *event, int cpu)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(event, cpu));
FAIL_IF(event_enable(event));
return 0;
}
int cpu_event_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
int cpu, rc;
pid_t pid;
cpu = pick_online_cpu();
FAIL_IF(cpu < 0);
FAIL_IF(bind_to_cpu(cpu));
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the cpu event first */
rc = setup_cpu_event(&event, cpu);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* We expect the child to succeed */
FAIL_IF(wait_for_child(pid));
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The cpu event may have run */
return 0;
}
int main(void)
{
return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb");
}

View file

@ -0,0 +1,58 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Basic test that counts user cycles and takes EBBs.
*/
int cycles(void)
{
struct event event;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 10) {
FAIL_IF(core_busy_loop());
FAIL_IF(ebb_check_mmcr0());
}
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
FAIL_IF(!ebb_check_count(1, sample_period, 100));
return 0;
}
int main(void)
{
return test_harness(cycles, "cycles");
}

View file

@ -0,0 +1,117 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ebb.h"
/*
* Test of counting cycles while using MMCR0_FC (freeze counters) to only count
* parts of the code. This is complicated by the fact that FC is set by the
* hardware when the event overflows. We may take the EBB after we have set FC,
* so we have to be careful about whether we clear FC at the end of the EBB
* handler or not.
*/
static bool counters_frozen = false;
static int ebbs_while_frozen = 0;
static void ebb_callee(void)
{
uint64_t mask, val;
mask = MMCR0_PMAO | MMCR0_FC;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
val = mfspr(SPRN_MMCR0);
trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
if (counters_frozen) {
trace_log_string(ebb_state.trace, "frozen");
ebbs_while_frozen++;
mask &= ~MMCR0_FC;
}
count_pmc(1, sample_period);
out:
reset_ebb_with_clear_mask(mask);
}
int cycles_with_freeze(void)
{
struct event event;
uint64_t val;
bool fc_cleared;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
fc_cleared = false;
/* Make sure we loop until we take at least one EBB */
while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) ||
ebb_state.stats.ebb_count < 1)
{
counters_frozen = false;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
FAIL_IF(core_busy_loop());
counters_frozen = true;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
val = mfspr(SPRN_MMCR0);
if (! (val & MMCR0_FC)) {
printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val);
fc_cleared = true;
}
}
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
printf("EBBs while frozen %d\n", ebbs_while_frozen);
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
FAIL_IF(fc_cleared);
return 0;
}
int main(void)
{
return test_harness(cycles_with_freeze, "cycles_with_freeze");
}

View file

@ -0,0 +1,91 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ebb.h"
/*
* Test of counting cycles while manipulating the user accessible bits in MMCR2.
*/
/* We use two values because the first freezes PMC1 and so we would get no EBBs */
#define MMCR2_EXPECTED_1 0x4020100804020000UL /* (FC1P|FC2P|FC3P|FC4P|FC5P|FC6P) */
#define MMCR2_EXPECTED_2 0x0020100804020000UL /* ( FC2P|FC3P|FC4P|FC5P|FC6P) */
int cycles_with_mmcr2(void)
{
struct event event;
uint64_t val, expected[2], actual;
int i;
bool bad_mmcr2;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
/* XXX Set of MMCR2 must be after enable */
expected[0] = MMCR2_EXPECTED_1;
expected[1] = MMCR2_EXPECTED_2;
i = 0;
bad_mmcr2 = false;
/* Make sure we loop until we take at least one EBB */
while ((ebb_state.stats.ebb_count < 20 && !bad_mmcr2) ||
ebb_state.stats.ebb_count < 1)
{
mtspr(SPRN_MMCR2, expected[i % 2]);
FAIL_IF(core_busy_loop());
val = mfspr(SPRN_MMCR2);
if (val != expected[i % 2]) {
bad_mmcr2 = true;
actual = val;
}
i++;
}
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
if (bad_mmcr2)
printf("Bad MMCR2 value seen is 0x%lx\n", actual);
FAIL_IF(bad_mmcr2);
return 0;
}
int main(void)
{
return test_harness(cycles_with_mmcr2, "cycles_with_mmcr2");
}

View file

@ -0,0 +1,478 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
#include <sched.h>
#include <sys/wait.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "trace.h"
#include "reg.h"
#include "ebb.h"
void (*ebb_user_func)(void);
void ebb_hook(void)
{
if (ebb_user_func)
ebb_user_func();
}
struct ebb_state ebb_state;
u64 sample_period = 0x40000000ull;
void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask)
{
u64 val;
/* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */
/* 3) set MMCR0[PMAE] - docs say BESCR[PME] should do this */
val = mfspr(SPRN_MMCR0);
mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE);
/* 4) clear BESCR[PMEO] */
mtspr(SPRN_BESCRR, BESCR_PMEO);
/* 5) set BESCR[PME] */
mtspr(SPRN_BESCRS, BESCR_PME);
/* 6) rfebb 1 - done in our caller */
}
void reset_ebb(void)
{
reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC);
}
/* Called outside of the EBB handler to check MMCR0 is sane */
int ebb_check_mmcr0(void)
{
u64 val;
val = mfspr(SPRN_MMCR0);
if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) {
/* It's OK if we see FC & PMAO, but not FC by itself */
printf("Outside of loop, only FC set 0x%llx\n", val);
return 1;
}
return 0;
}
bool ebb_check_count(int pmc, u64 sample_period, int fudge)
{
u64 count, upper, lower;
count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)];
lower = ebb_state.stats.ebb_count * (sample_period - fudge);
if (count < lower) {
printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n",
pmc, count, lower, lower - count);
return false;
}
upper = ebb_state.stats.ebb_count * (sample_period + fudge);
if (count > upper) {
printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n",
pmc, count, upper, count - upper);
return false;
}
printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n",
pmc, count, lower, upper, count - lower, upper - count);
return true;
}
void standard_ebb_callee(void)
{
int found, i;
u64 val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
val = mfspr(SPRN_MMCR0);
trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
found = 0;
for (i = 1; i <= 6; i++) {
if (ebb_state.pmc_enable[PMC_INDEX(i)])
found += count_pmc(i, sample_period);
}
if (!found)
ebb_state.stats.no_overflow++;
out:
reset_ebb();
}
extern void ebb_handler(void);
void setup_ebb_handler(void (*callee)(void))
{
u64 entry;
#if defined(_CALL_ELF) && _CALL_ELF == 2
entry = (u64)ebb_handler;
#else
struct opd
{
u64 entry;
u64 toc;
} *opd;
opd = (struct opd *)ebb_handler;
entry = opd->entry;
#endif
printf("EBB Handler is at %#llx\n", entry);
ebb_user_func = callee;
/* Ensure ebb_user_func is set before we set the handler */
mb();
mtspr(SPRN_EBBHR, entry);
/* Make sure the handler is set before we return */
mb();
}
void clear_ebb_stats(void)
{
memset(&ebb_state.stats, 0, sizeof(ebb_state.stats));
}
void dump_summary_ebb_state(void)
{
printf("ebb_state:\n" \
" ebb_count = %d\n" \
" spurious = %d\n" \
" negative = %d\n" \
" no_overflow = %d\n" \
" pmc[1] count = 0x%llx\n" \
" pmc[2] count = 0x%llx\n" \
" pmc[3] count = 0x%llx\n" \
" pmc[4] count = 0x%llx\n" \
" pmc[5] count = 0x%llx\n" \
" pmc[6] count = 0x%llx\n",
ebb_state.stats.ebb_count, ebb_state.stats.spurious,
ebb_state.stats.negative, ebb_state.stats.no_overflow,
ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1],
ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3],
ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]);
}
static char *decode_mmcr0(u32 value)
{
static char buf[16];
buf[0] = '\0';
if (value & (1 << 31))
strcat(buf, "FC ");
if (value & (1 << 26))
strcat(buf, "PMAE ");
if (value & (1 << 7))
strcat(buf, "PMAO ");
return buf;
}
static char *decode_bescr(u64 value)
{
static char buf[16];
buf[0] = '\0';
if (value & (1ull << 63))
strcat(buf, "GE ");
if (value & (1ull << 32))
strcat(buf, "PMAE ");
if (value & 1)
strcat(buf, "PMAO ");
return buf;
}
void dump_ebb_hw_state(void)
{
u64 bescr;
u32 mmcr0;
mmcr0 = mfspr(SPRN_MMCR0);
bescr = mfspr(SPRN_BESCR);
printf("HW state:\n" \
"MMCR0 0x%016x %s\n" \
"MMCR2 0x%016lx\n" \
"EBBHR 0x%016lx\n" \
"BESCR 0x%016llx %s\n" \
"PMC1 0x%016lx\n" \
"PMC2 0x%016lx\n" \
"PMC3 0x%016lx\n" \
"PMC4 0x%016lx\n" \
"PMC5 0x%016lx\n" \
"PMC6 0x%016lx\n" \
"SIAR 0x%016lx\n",
mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_MMCR2),
mfspr(SPRN_EBBHR), bescr, decode_bescr(bescr),
mfspr(SPRN_PMC1), mfspr(SPRN_PMC2), mfspr(SPRN_PMC3),
mfspr(SPRN_PMC4), mfspr(SPRN_PMC5), mfspr(SPRN_PMC6),
mfspr(SPRN_SIAR));
}
void dump_ebb_state(void)
{
dump_summary_ebb_state();
dump_ebb_hw_state();
trace_buffer_print(ebb_state.trace);
}
int count_pmc(int pmc, uint32_t sample_period)
{
uint32_t start_value;
u64 val;
/* 0) Read PMC */
start_value = pmc_sample_period(sample_period);
val = read_pmc(pmc);
if (val < start_value)
ebb_state.stats.negative++;
else
ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value;
trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val);
/* 1) Reset PMC */
write_pmc(pmc, start_value);
/* Report if we overflowed */
return val >= COUNTER_OVERFLOW;
}
int ebb_event_enable(struct event *e)
{
int rc;
/* Ensure any SPR writes are ordered vs us */
mb();
rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
if (rc)
return rc;
rc = event_read(e);
/* Ditto */
mb();
return rc;
}
void ebb_freeze_pmcs(void)
{
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
mb();
}
void ebb_unfreeze_pmcs(void)
{
/* Unfreeze counters */
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
mb();
}
void ebb_global_enable(void)
{
/* Enable EBBs globally and PMU EBBs */
mtspr(SPRN_BESCR, 0x8000000100000000ull);
mb();
}
void ebb_global_disable(void)
{
/* Disable EBBs & freeze counters, events are still scheduled */
mtspr(SPRN_BESCRR, BESCR_PME);
mb();
}
void event_ebb_init(struct event *e)
{
e->attr.config |= (1ull << 63);
}
void event_bhrb_init(struct event *e, unsigned ifm)
{
e->attr.config |= (1ull << 62) | ((u64)ifm << 60);
}
void event_leader_ebb_init(struct event *e)
{
event_ebb_init(e);
e->attr.exclusive = 1;
e->attr.pinned = 1;
}
int ebb_child(union pipe read_pipe, union pipe write_pipe)
{
struct event event;
uint64_t val;
FAIL_IF(wait_for_parent(read_pipe));
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(event_enable(&event));
if (event_read(&event)) {
/*
* Some tests expect to fail here, so don't report an error on
* this line, and return a distinguisable error code. Tell the
* parent an error happened.
*/
notify_parent_of_error(write_pipe);
return 2;
}
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
FAIL_IF(notify_parent(write_pipe));
FAIL_IF(wait_for_parent(read_pipe));
FAIL_IF(notify_parent(write_pipe));
while (ebb_state.stats.ebb_count < 20) {
FAIL_IF(core_busy_loop());
/* To try and hit SIGILL case */
val = mfspr(SPRN_MMCRA);
val |= mfspr(SPRN_MMCR2);
val |= mfspr(SPRN_MMCR0);
}
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
static jmp_buf setjmp_env;
static void sigill_handler(int signal)
{
printf("Took sigill\n");
longjmp(setjmp_env, 1);
}
static struct sigaction sigill_action = {
.sa_handler = sigill_handler,
};
int catch_sigill(void (*func)(void))
{
if (sigaction(SIGILL, &sigill_action, NULL)) {
perror("sigaction");
return 1;
}
if (setjmp(setjmp_env) == 0) {
func();
return 1;
}
return 0;
}
void write_pmc1(void)
{
mtspr(SPRN_PMC1, 0);
}
void write_pmc(int pmc, u64 value)
{
switch (pmc) {
case 1: mtspr(SPRN_PMC1, value); break;
case 2: mtspr(SPRN_PMC2, value); break;
case 3: mtspr(SPRN_PMC3, value); break;
case 4: mtspr(SPRN_PMC4, value); break;
case 5: mtspr(SPRN_PMC5, value); break;
case 6: mtspr(SPRN_PMC6, value); break;
}
}
u64 read_pmc(int pmc)
{
switch (pmc) {
case 1: return mfspr(SPRN_PMC1);
case 2: return mfspr(SPRN_PMC2);
case 3: return mfspr(SPRN_PMC3);
case 4: return mfspr(SPRN_PMC4);
case 5: return mfspr(SPRN_PMC5);
case 6: return mfspr(SPRN_PMC6);
}
return 0;
}
static void term_handler(int signal)
{
dump_summary_ebb_state();
dump_ebb_hw_state();
abort();
}
struct sigaction term_action = {
.sa_handler = term_handler,
};
static void __attribute__((constructor)) ebb_init(void)
{
clear_ebb_stats();
if (sigaction(SIGTERM, &term_action, NULL))
perror("sigaction");
ebb_state.trace = trace_buffer_allocate(1 * 1024 * 1024);
}

View file

@ -0,0 +1,77 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H
#define _SELFTESTS_POWERPC_PMU_EBB_EBB_H
#include "../event.h"
#include "../lib.h"
#include "trace.h"
#include "reg.h"
#define PMC_INDEX(pmc) ((pmc)-1)
#define NUM_PMC_VALUES 128
struct ebb_state
{
struct {
u64 pmc_count[6];
volatile int ebb_count;
int spurious;
int negative;
int no_overflow;
} stats;
bool pmc_enable[6];
struct trace_buffer *trace;
};
extern struct ebb_state ebb_state;
#define COUNTER_OVERFLOW 0x80000000ull
static inline uint32_t pmc_sample_period(uint32_t value)
{
return COUNTER_OVERFLOW - value;
}
static inline void ebb_enable_pmc_counting(int pmc)
{
ebb_state.pmc_enable[PMC_INDEX(pmc)] = true;
}
bool ebb_check_count(int pmc, u64 sample_period, int fudge);
void event_leader_ebb_init(struct event *e);
void event_ebb_init(struct event *e);
void event_bhrb_init(struct event *e, unsigned ifm);
void setup_ebb_handler(void (*callee)(void));
void standard_ebb_callee(void);
int ebb_event_enable(struct event *e);
void ebb_global_enable(void);
void ebb_global_disable(void);
void ebb_freeze_pmcs(void);
void ebb_unfreeze_pmcs(void);
void event_ebb_init(struct event *e);
void event_leader_ebb_init(struct event *e);
int count_pmc(int pmc, uint32_t sample_period);
void dump_ebb_state(void);
void dump_summary_ebb_state(void);
void dump_ebb_hw_state(void);
void clear_ebb_stats(void);
void write_pmc(int pmc, u64 value);
u64 read_pmc(int pmc);
void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask);
void reset_ebb(void);
int ebb_check_mmcr0(void);
extern u64 sample_period;
int core_busy_loop(void);
int ebb_child(union pipe read_pipe, union pipe write_pipe);
int catch_sigill(void (*func)(void));
void write_pmc1(void);
#endif /* _SELFTESTS_POWERPC_PMU_EBB_EBB_H */

View file

@ -0,0 +1,365 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <ppc-asm.h>
#include "reg.h"
/* ppc-asm.h defines most of the reg aliases, but not r1/r2. */
#define r1 1
#define r2 2
#define RFEBB .long 0x4c000924
/* Stack layout:
*
* ^
* User stack |
* Back chain ------+ <- r1 <-------+
* ... |
* Red zone / ABI Gap |
* ... |
* vr63 <+ |
* vr0 | |
* VSCR | |
* FSCR | |
* r31 | Save area |
* r0 | |
* XER | |
* CTR | |
* LR | |
* CCR <+ |
* ... <+ |
* LR | Caller frame |
* CCR | |
* Back chain <+ <- updated r1 --------+
*
*/
#if defined(_CALL_ELF) && _CALL_ELF == 2
#define ABIGAP 512
#else
#define ABIGAP 288
#endif
#define NR_GPR 32
#define NR_SPR 6
#define NR_VSR 64
#define SAVE_AREA ((NR_GPR + NR_SPR) * 8 + (NR_VSR * 16))
#define CALLER_FRAME 112
#define STACK_FRAME (ABIGAP + SAVE_AREA + CALLER_FRAME)
#define CCR_SAVE (CALLER_FRAME)
#define LR_SAVE (CCR_SAVE + 8)
#define CTR_SAVE (LR_SAVE + 8)
#define XER_SAVE (CTR_SAVE + 8)
#define GPR_SAVE(n) (XER_SAVE + 8 + (8 * n))
#define FSCR_SAVE (GPR_SAVE(31) + 8)
#define VSCR_SAVE (FSCR_SAVE + 8)
#define VSR_SAVE(n) (VSCR_SAVE + 8 + (16 * n))
#define SAVE_GPR(n) std n,GPR_SAVE(n)(r1)
#define REST_GPR(n) ld n,GPR_SAVE(n)(r1)
#define TRASH_GPR(n) lis n,0xaaaa
#define SAVE_VSR(n, b) li b, VSR_SAVE(n); stxvd2x n,b,r1
#define LOAD_VSR(n, b) li b, VSR_SAVE(n); lxvd2x n,b,r1
#define LOAD_REG_IMMEDIATE(reg,expr) \
lis reg,(expr)@highest; \
ori reg,reg,(expr)@higher; \
rldicr reg,reg,32,31; \
oris reg,reg,(expr)@h; \
ori reg,reg,(expr)@l;
#if defined(_CALL_ELF) && _CALL_ELF == 2
#define ENTRY_POINT(name) \
.type FUNC_NAME(name),@function; \
.globl FUNC_NAME(name); \
FUNC_NAME(name):
#define RESTORE_TOC(name) \
/* Restore our TOC pointer using our entry point */ \
LOAD_REG_IMMEDIATE(r12, name) \
0: addis r2,r12,(.TOC.-0b)@ha; \
addi r2,r2,(.TOC.-0b)@l;
#else
#define ENTRY_POINT(name) FUNC_START(name)
#define RESTORE_TOC(name) \
/* Restore our TOC pointer via our opd entry */ \
LOAD_REG_IMMEDIATE(r2, name) \
ld r2,8(r2);
#endif
.text
ENTRY_POINT(ebb_handler)
stdu r1,-STACK_FRAME(r1)
SAVE_GPR(0)
mflr r0
std r0,LR_SAVE(r1)
mfcr r0
std r0,CCR_SAVE(r1)
mfctr r0
std r0,CTR_SAVE(r1)
mfxer r0
std r0,XER_SAVE(r1)
SAVE_GPR(2)
SAVE_GPR(3)
SAVE_GPR(4)
SAVE_GPR(5)
SAVE_GPR(6)
SAVE_GPR(7)
SAVE_GPR(8)
SAVE_GPR(9)
SAVE_GPR(10)
SAVE_GPR(11)
SAVE_GPR(12)
SAVE_GPR(13)
SAVE_GPR(14)
SAVE_GPR(15)
SAVE_GPR(16)
SAVE_GPR(17)
SAVE_GPR(18)
SAVE_GPR(19)
SAVE_GPR(20)
SAVE_GPR(21)
SAVE_GPR(22)
SAVE_GPR(23)
SAVE_GPR(24)
SAVE_GPR(25)
SAVE_GPR(26)
SAVE_GPR(27)
SAVE_GPR(28)
SAVE_GPR(29)
SAVE_GPR(30)
SAVE_GPR(31)
SAVE_VSR(0, r3)
mffs f0
stfd f0, FSCR_SAVE(r1)
mfvscr f0
stfd f0, VSCR_SAVE(r1)
SAVE_VSR(1, r3)
SAVE_VSR(2, r3)
SAVE_VSR(3, r3)
SAVE_VSR(4, r3)
SAVE_VSR(5, r3)
SAVE_VSR(6, r3)
SAVE_VSR(7, r3)
SAVE_VSR(8, r3)
SAVE_VSR(9, r3)
SAVE_VSR(10, r3)
SAVE_VSR(11, r3)
SAVE_VSR(12, r3)
SAVE_VSR(13, r3)
SAVE_VSR(14, r3)
SAVE_VSR(15, r3)
SAVE_VSR(16, r3)
SAVE_VSR(17, r3)
SAVE_VSR(18, r3)
SAVE_VSR(19, r3)
SAVE_VSR(20, r3)
SAVE_VSR(21, r3)
SAVE_VSR(22, r3)
SAVE_VSR(23, r3)
SAVE_VSR(24, r3)
SAVE_VSR(25, r3)
SAVE_VSR(26, r3)
SAVE_VSR(27, r3)
SAVE_VSR(28, r3)
SAVE_VSR(29, r3)
SAVE_VSR(30, r3)
SAVE_VSR(31, r3)
SAVE_VSR(32, r3)
SAVE_VSR(33, r3)
SAVE_VSR(34, r3)
SAVE_VSR(35, r3)
SAVE_VSR(36, r3)
SAVE_VSR(37, r3)
SAVE_VSR(38, r3)
SAVE_VSR(39, r3)
SAVE_VSR(40, r3)
SAVE_VSR(41, r3)
SAVE_VSR(42, r3)
SAVE_VSR(43, r3)
SAVE_VSR(44, r3)
SAVE_VSR(45, r3)
SAVE_VSR(46, r3)
SAVE_VSR(47, r3)
SAVE_VSR(48, r3)
SAVE_VSR(49, r3)
SAVE_VSR(50, r3)
SAVE_VSR(51, r3)
SAVE_VSR(52, r3)
SAVE_VSR(53, r3)
SAVE_VSR(54, r3)
SAVE_VSR(55, r3)
SAVE_VSR(56, r3)
SAVE_VSR(57, r3)
SAVE_VSR(58, r3)
SAVE_VSR(59, r3)
SAVE_VSR(60, r3)
SAVE_VSR(61, r3)
SAVE_VSR(62, r3)
SAVE_VSR(63, r3)
TRASH_GPR(2)
TRASH_GPR(3)
TRASH_GPR(4)
TRASH_GPR(5)
TRASH_GPR(6)
TRASH_GPR(7)
TRASH_GPR(8)
TRASH_GPR(9)
TRASH_GPR(10)
TRASH_GPR(11)
TRASH_GPR(12)
TRASH_GPR(14)
TRASH_GPR(15)
TRASH_GPR(16)
TRASH_GPR(17)
TRASH_GPR(18)
TRASH_GPR(19)
TRASH_GPR(20)
TRASH_GPR(21)
TRASH_GPR(22)
TRASH_GPR(23)
TRASH_GPR(24)
TRASH_GPR(25)
TRASH_GPR(26)
TRASH_GPR(27)
TRASH_GPR(28)
TRASH_GPR(29)
TRASH_GPR(30)
TRASH_GPR(31)
RESTORE_TOC(ebb_handler)
/*
* r13 is our TLS pointer. We leave whatever value was in there when the
* EBB fired. That seems to be OK because once set the TLS pointer is not
* changed - but presumably that could change in future.
*/
bl ebb_hook
nop
/* r2 may be changed here but we don't care */
lfd f0, FSCR_SAVE(r1)
mtfsf 0xff,f0
lfd f0, VSCR_SAVE(r1)
mtvscr f0
LOAD_VSR(0, r3)
LOAD_VSR(1, r3)
LOAD_VSR(2, r3)
LOAD_VSR(3, r3)
LOAD_VSR(4, r3)
LOAD_VSR(5, r3)
LOAD_VSR(6, r3)
LOAD_VSR(7, r3)
LOAD_VSR(8, r3)
LOAD_VSR(9, r3)
LOAD_VSR(10, r3)
LOAD_VSR(11, r3)
LOAD_VSR(12, r3)
LOAD_VSR(13, r3)
LOAD_VSR(14, r3)
LOAD_VSR(15, r3)
LOAD_VSR(16, r3)
LOAD_VSR(17, r3)
LOAD_VSR(18, r3)
LOAD_VSR(19, r3)
LOAD_VSR(20, r3)
LOAD_VSR(21, r3)
LOAD_VSR(22, r3)
LOAD_VSR(23, r3)
LOAD_VSR(24, r3)
LOAD_VSR(25, r3)
LOAD_VSR(26, r3)
LOAD_VSR(27, r3)
LOAD_VSR(28, r3)
LOAD_VSR(29, r3)
LOAD_VSR(30, r3)
LOAD_VSR(31, r3)
LOAD_VSR(32, r3)
LOAD_VSR(33, r3)
LOAD_VSR(34, r3)
LOAD_VSR(35, r3)
LOAD_VSR(36, r3)
LOAD_VSR(37, r3)
LOAD_VSR(38, r3)
LOAD_VSR(39, r3)
LOAD_VSR(40, r3)
LOAD_VSR(41, r3)
LOAD_VSR(42, r3)
LOAD_VSR(43, r3)
LOAD_VSR(44, r3)
LOAD_VSR(45, r3)
LOAD_VSR(46, r3)
LOAD_VSR(47, r3)
LOAD_VSR(48, r3)
LOAD_VSR(49, r3)
LOAD_VSR(50, r3)
LOAD_VSR(51, r3)
LOAD_VSR(52, r3)
LOAD_VSR(53, r3)
LOAD_VSR(54, r3)
LOAD_VSR(55, r3)
LOAD_VSR(56, r3)
LOAD_VSR(57, r3)
LOAD_VSR(58, r3)
LOAD_VSR(59, r3)
LOAD_VSR(60, r3)
LOAD_VSR(61, r3)
LOAD_VSR(62, r3)
LOAD_VSR(63, r3)
ld r0,XER_SAVE(r1)
mtxer r0
ld r0,CTR_SAVE(r1)
mtctr r0
ld r0,LR_SAVE(r1)
mtlr r0
ld r0,CCR_SAVE(r1)
mtcr r0
REST_GPR(0)
REST_GPR(2)
REST_GPR(3)
REST_GPR(4)
REST_GPR(5)
REST_GPR(6)
REST_GPR(7)
REST_GPR(8)
REST_GPR(9)
REST_GPR(10)
REST_GPR(11)
REST_GPR(12)
REST_GPR(13)
REST_GPR(14)
REST_GPR(15)
REST_GPR(16)
REST_GPR(17)
REST_GPR(18)
REST_GPR(19)
REST_GPR(20)
REST_GPR(21)
REST_GPR(22)
REST_GPR(23)
REST_GPR(24)
REST_GPR(25)
REST_GPR(26)
REST_GPR(27)
REST_GPR(28)
REST_GPR(29)
REST_GPR(30)
REST_GPR(31)
addi r1,r1,STACK_FRAME
RFEBB
FUNC_END(ebb_handler)

View file

@ -0,0 +1,86 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests we can setup an EBB on our child. Nothing interesting happens, because
* even though the event is enabled and running the child hasn't enabled the
* actual delivery of the EBBs.
*/
static int victim_child(union pipe read_pipe, union pipe write_pipe)
{
int i;
FAIL_IF(wait_for_parent(read_pipe));
FAIL_IF(notify_parent(write_pipe));
/* Parent creates EBB event */
FAIL_IF(wait_for_parent(read_pipe));
FAIL_IF(notify_parent(write_pipe));
/* Check the EBB is enabled by writing PMC1 */
write_pmc1();
/* EBB event is enabled here */
for (i = 0; i < 1000000; i++) ;
return 0;
}
int ebb_on_child(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(victim_child(write_pipe, read_pipe));
}
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Child is running now */
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(&event, pid));
FAIL_IF(ebb_event_enable(&event));
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Child should just exit happily */
FAIL_IF(wait_for_child(pid));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(ebb_on_child, "ebb_on_child");
}

View file

@ -0,0 +1,92 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests we can setup an EBB on our child. The child expects this and enables
* EBBs, which are then delivered to the child, even though the event is
* created by the parent.
*/
static int victim_child(union pipe read_pipe, union pipe write_pipe)
{
FAIL_IF(wait_for_parent(read_pipe));
/* Setup our EBB handler, before the EBB event is created */
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(notify_parent(write_pipe));
while (ebb_state.stats.ebb_count < 20) {
FAIL_IF(core_busy_loop());
}
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
/* Tests we can setup an EBB on our child - if it's expecting it */
int ebb_on_willing_child(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(victim_child(write_pipe, read_pipe));
}
/* Signal the child to setup its EBB handler */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Child is running now */
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(&event, pid));
FAIL_IF(ebb_event_enable(&event));
/* Child show now take EBBs and then exit */
FAIL_IF(wait_for_child(pid));
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(ebb_on_willing_child, "ebb_on_willing_child");
}

View file

@ -0,0 +1,86 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests an EBB vs a cpu event - in that order. The EBB should force the cpu
* event off the PMU.
*/
static int setup_cpu_event(struct event *event, int cpu)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(event, cpu));
FAIL_IF(event_enable(event));
return 0;
}
int ebb_vs_cpu_event(void)
{
union pipe read_pipe, write_pipe;
struct event event;
int cpu, rc;
pid_t pid;
cpu = pick_online_cpu();
FAIL_IF(cpu < 0);
FAIL_IF(bind_to_cpu(cpu));
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* Signal the child to install its EBB event and wait */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* Now try to install our CPU event */
rc = setup_cpu_event(&event, cpu);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
/* .. and wait for it to complete */
FAIL_IF(wait_for_child(pid));
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The cpu event may have run, but we don't expect 100% */
FAIL_IF(event.result.enabled >= event.result.running);
return 0;
}
int main(void)
{
return test_harness(ebb_vs_cpu_event, "ebb_vs_cpu_event");
}

View file

@ -0,0 +1,131 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test various attributes of the EBB event are enforced.
*/
int event_attributes(void)
{
struct event event, leader;
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
/* Expected to succeed */
FAIL_IF(event_open(&event));
event_close(&event);
event_init(&event, 0x001e); /* CYCLES - no PMC specified */
event_leader_ebb_init(&event);
/* Expected to fail, no PMC specified */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x2001e);
event_leader_ebb_init(&event);
event.attr.exclusive = 0;
/* Expected to fail, not exclusive */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x3001e);
event_leader_ebb_init(&event);
event.attr.freq = 1;
/* Expected to fail, sets freq */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x4001e);
event_leader_ebb_init(&event);
event.attr.sample_period = 1;
/* Expected to fail, sets sample_period */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
event.attr.enable_on_exec = 1;
/* Expected to fail, sets enable_on_exec */
FAIL_IF(event_open(&event) == 0);
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
event.attr.inherit = 1;
/* Expected to fail, sets inherit */
FAIL_IF(event_open(&event) == 0);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
FAIL_IF(event_open(&leader));
event_init(&event, 0x20002);
event_ebb_init(&event);
/* Expected to succeed */
FAIL_IF(event_open_with_group(&event, leader.fd));
event_close(&leader);
event_close(&event);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
FAIL_IF(event_open(&leader));
event_init(&event, 0x20002);
/* Expected to fail, event doesn't request EBB, leader does */
FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
event_close(&leader);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
/* Clear the EBB flag */
leader.attr.config &= ~(1ull << 63);
FAIL_IF(event_open(&leader));
event_init(&event, 0x20002);
event_ebb_init(&event);
/* Expected to fail, leader doesn't request EBB */
FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
event_close(&leader);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
leader.attr.exclusive = 0;
/* Expected to fail, leader isn't exclusive */
FAIL_IF(event_open(&leader) == 0);
event_init(&leader, 0x1001e);
event_leader_ebb_init(&leader);
leader.attr.pinned = 0;
/* Expected to fail, leader isn't pinned */
FAIL_IF(event_open(&leader) == 0);
event_init(&event, 0x1001e);
event_leader_ebb_init(&event);
/* Expected to fail, not a task event */
SKIP_IF(require_paranoia_below(1));
FAIL_IF(event_open_with_cpu(&event, 0) == 0);
return 0;
}
int main(void)
{
return test_harness(event_attributes, "event_attributes");
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <ppc-asm.h>
.text
FUNC_START(thirty_two_instruction_loop)
cmpwi r3,0
beqlr
addi r4,r3,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1 # 28 addi's
subi r3,r3,1
b FUNC_NAME(thirty_two_instruction_loop)
FUNC_END(thirty_two_instruction_loop)

View file

@ -0,0 +1,79 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include "ebb.h"
/*
* Test that a fork clears the PMU state of the child. eg. BESCR/EBBHR/EBBRR
* are cleared, and MMCR0_PMCC is reset, preventing the child from accessing
* the PMU.
*/
static struct event event;
static int child(void)
{
/* Even though we have EBE=0 we can still see the EBB regs */
FAIL_IF(mfspr(SPRN_BESCR) != 0);
FAIL_IF(mfspr(SPRN_EBBHR) != 0);
FAIL_IF(mfspr(SPRN_EBBRR) != 0);
FAIL_IF(catch_sigill(write_pmc1));
/* We can still read from the event, though it is on our parent */
FAIL_IF(event_read(&event));
return 0;
}
/* Tests that fork clears EBB state */
int fork_cleanup(void)
{
pid_t pid;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_MMCR0, MMCR0_FC);
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
/* Don't need to actually take any EBBs */
pid = fork();
if (pid == 0)
exit(child());
/* Child does the actual testing */
FAIL_IF(wait_for_child(pid));
/* After fork */
event_close(&event);
return 0;
}
int main(void)
{
return test_harness(fork_cleanup, "fork_cleanup");
}

View file

@ -0,0 +1,164 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "ebb.h"
/*
* Run a calibrated instruction loop and count instructions executed using
* EBBs. Make sure the counts look right.
*/
extern void thirty_two_instruction_loop(uint64_t loops);
static bool counters_frozen = true;
static int do_count_loop(struct event *event, uint64_t instructions,
uint64_t overhead, bool report)
{
int64_t difference, expected;
double percentage;
clear_ebb_stats();
counters_frozen = false;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
thirty_two_instruction_loop(instructions >> 5);
counters_frozen = true;
mb();
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
count_pmc(4, sample_period);
event->result.value = ebb_state.stats.pmc_count[4-1];
expected = instructions + overhead;
difference = event->result.value - expected;
percentage = (double)difference / event->result.value * 100;
if (report) {
printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
printf("Expected %lu\n", expected);
printf("Actual %llu\n", event->result.value);
printf("Error %ld, %f%%\n", difference, percentage);
printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
}
if (difference < 0)
difference = -difference;
/* Tolerate a difference of up to 0.0001 % */
difference *= 10000 * 100;
if (difference / event->result.value)
return -1;
return 0;
}
/* Count how many instructions it takes to do a null loop */
static uint64_t determine_overhead(struct event *event)
{
uint64_t current, overhead;
int i;
do_count_loop(event, 0, 0, false);
overhead = event->result.value;
for (i = 0; i < 100; i++) {
do_count_loop(event, 0, 0, false);
current = event->result.value;
if (current < overhead) {
printf("Replacing overhead %lu with %lu\n", overhead, current);
overhead = current;
}
}
return overhead;
}
static void pmc4_ebb_callee(void)
{
uint64_t val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
count_pmc(4, sample_period);
out:
if (counters_frozen)
reset_ebb_with_clear_mask(MMCR0_PMAO);
else
reset_ebb();
}
int instruction_count(void)
{
struct event event;
uint64_t overhead;
event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
FAIL_IF(ebb_event_enable(&event));
sample_period = COUNTER_OVERFLOW;
setup_ebb_handler(pmc4_ebb_callee);
mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
ebb_global_enable();
overhead = determine_overhead(&event);
printf("Overhead of null loop: %lu instructions\n", overhead);
/* Run for 1M instructions */
FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
/* Run for 10M instructions */
FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
/* Run for 100M instructions */
FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
/* Run for 1G instructions */
FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
/* Run for 16G instructions */
FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
/* Run for 64G instructions */
FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
/* Run for 128G instructions */
FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
ebb_global_disable();
event_close(&event);
printf("Finished OK\n");
return 0;
}
int main(void)
{
return test_harness(instruction_count, "instruction_count");
}

View file

@ -0,0 +1,100 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "ebb.h"
/*
* Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect
* where an exception triggers but we context switch before it is delivered and
* lose the exception.
*/
static int test_body(void)
{
int i, orig_period, max_period;
struct event event;
/* We use PMC4 to make sure the kernel switches all counters correctly */
event_init_named(&event, 0x40002, "instructions");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(4);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
/*
* We want a low sample period, but we also want to get out of the EBB
* handler without tripping up again.
*
* This value picked after much experimentation.
*/
orig_period = max_period = sample_period = 400;
mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 1000000) {
/*
* We are trying to get the EBB exception to race exactly with
* us entering the kernel to do the syscall. We then need the
* kernel to decide our timeslice is up and context switch to
* the other thread. When we come back our EBB will have been
* lost and we'll spin in this while loop forever.
*/
for (i = 0; i < 100000; i++)
sched_yield();
/* Change the sample period slightly to try and hit the race */
if (sample_period >= (orig_period + 200))
sample_period = orig_period;
else
sample_period++;
if (sample_period > max_period)
max_period = sample_period;
}
ebb_freeze_pmcs();
ebb_global_disable();
count_pmc(4, sample_period);
mtspr(SPRN_PMC4, 0xdead);
dump_summary_ebb_state();
dump_ebb_hw_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
/* We vary our sample period so we need extra fudge here */
FAIL_IF(!ebb_check_count(4, orig_period, 2 * (max_period - orig_period)));
return 0;
}
static int lost_exception(void)
{
return eat_cpu(test_body);
}
int main(void)
{
return test_harness(lost_exception, "lost_exception");
}

View file

@ -0,0 +1,91 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "ebb.h"
/*
* Test counting multiple events using EBBs.
*/
int multi_counter(void)
{
struct event events[6];
int i, group_fd;
event_init_named(&events[0], 0x1001C, "PM_CMPLU_STALL_THRD");
event_init_named(&events[1], 0x2D016, "PM_CMPLU_STALL_FXU");
event_init_named(&events[2], 0x30006, "PM_CMPLU_STALL_OTHER_CMPL");
event_init_named(&events[3], 0x4000A, "PM_CMPLU_STALL");
event_init_named(&events[4], 0x600f4, "PM_RUN_CYC");
event_init_named(&events[5], 0x500fa, "PM_RUN_INST_CMPL");
event_leader_ebb_init(&events[0]);
for (i = 1; i < 6; i++)
event_ebb_init(&events[i]);
group_fd = -1;
for (i = 0; i < 6; i++) {
events[i].attr.exclude_kernel = 1;
events[i].attr.exclude_hv = 1;
events[i].attr.exclude_idle = 1;
FAIL_IF(event_open_with_group(&events[i], group_fd));
if (group_fd == -1)
group_fd = events[0].fd;
}
ebb_enable_pmc_counting(1);
ebb_enable_pmc_counting(2);
ebb_enable_pmc_counting(3);
ebb_enable_pmc_counting(4);
ebb_enable_pmc_counting(5);
ebb_enable_pmc_counting(6);
setup_ebb_handler(standard_ebb_callee);
FAIL_IF(ioctl(events[0].fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP));
FAIL_IF(event_read(&events[0]));
ebb_global_enable();
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
mtspr(SPRN_PMC3, pmc_sample_period(sample_period));
mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
mtspr(SPRN_PMC5, pmc_sample_period(sample_period));
mtspr(SPRN_PMC6, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 50) {
FAIL_IF(core_busy_loop());
FAIL_IF(ebb_check_mmcr0());
}
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++)
event_close(&events[i]);
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
int main(void)
{
return test_harness(multi_counter, "multi_counter");
}

View file

@ -0,0 +1,109 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "ebb.h"
/*
* Test running multiple EBB using processes at once on a single CPU. They
* should all run happily without interfering with each other.
*/
static bool child_should_exit;
static void sigint_handler(int signal)
{
child_should_exit = true;
}
struct sigaction sigint_action = {
.sa_handler = sigint_handler,
};
static int cycles_child(void)
{
struct event event;
if (sigaction(SIGINT, &sigint_action, NULL)) {
perror("sigaction");
return 1;
}
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
ebb_enable_pmc_counting(1);
setup_ebb_handler(standard_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (!child_should_exit) {
FAIL_IF(core_busy_loop());
FAIL_IF(ebb_check_mmcr0());
}
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_summary_ebb_state();
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
return 0;
}
#define NR_CHILDREN 4
int multi_ebb_procs(void)
{
pid_t pids[NR_CHILDREN];
int cpu, rc, i;
cpu = pick_online_cpu();
FAIL_IF(cpu < 0);
FAIL_IF(bind_to_cpu(cpu));
for (i = 0; i < NR_CHILDREN; i++) {
pids[i] = fork();
if (pids[i] == 0)
exit(cycles_child());
}
/* Have them all run for "a while" */
sleep(10);
rc = 0;
for (i = 0; i < NR_CHILDREN; i++) {
/* Tell them to stop */
kill(pids[i], SIGINT);
/* And wait */
rc |= wait_for_child(pids[i]);
}
return rc;
}
int main(void)
{
return test_harness(multi_ebb_procs, "multi_ebb_procs");
}

View file

@ -0,0 +1,61 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include "ebb.h"
/* Test that things work sanely if we have no handler */
static int no_handler_test(void)
{
struct event event;
u64 val;
int i;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
FAIL_IF(ebb_event_enable(&event));
val = mfspr(SPRN_EBBHR);
FAIL_IF(val != 0);
/* Make sure it overflows quickly */
sample_period = 1000;
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
/* Spin to make sure the event has time to overflow */
for (i = 0; i < 1000; i++)
mb();
dump_ebb_state();
/* We expect to see the PMU frozen & PMAO set */
val = mfspr(SPRN_MMCR0);
FAIL_IF(val != 0x0000000080000080);
event_close(&event);
dump_ebb_state();
/* The real test is that we never took an EBB at 0x0 */
return 0;
}
int main(void)
{
return test_harness(no_handler_test,"no_handler_test");
}

View file

@ -0,0 +1,106 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test that the kernel properly handles PMAE across context switches.
*
* We test this by calling into the kernel inside our EBB handler, where PMAE
* is clear. A cpu eater companion thread is running on the same CPU as us to
* encourage the scheduler to switch us.
*
* The kernel must make sure that when it context switches us back in, it
* honours the fact that we had PMAE clear.
*
* Observed to hit the failing case on the first EBB with a broken kernel.
*/
static bool mmcr0_mismatch;
static uint64_t before, after;
static void syscall_ebb_callee(void)
{
uint64_t val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
count_pmc(1, sample_period);
before = mfspr(SPRN_MMCR0);
/* Try and get ourselves scheduled, to force a PMU context switch */
sched_yield();
after = mfspr(SPRN_MMCR0);
if (before != after)
mmcr0_mismatch = true;
out:
reset_ebb();
}
static int test_body(void)
{
struct event event;
event_init_named(&event, 0x1001e, "cycles");
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(syscall_ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
while (ebb_state.stats.ebb_count < 20 && !mmcr0_mismatch)
FAIL_IF(core_busy_loop());
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(1, sample_period);
dump_ebb_state();
if (mmcr0_mismatch)
printf("Saw MMCR0 before 0x%lx after 0x%lx\n", before, after);
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0);
FAIL_IF(mmcr0_mismatch);
return 0;
}
int pmae_handling(void)
{
return eat_cpu(test_body);
}
int main(void)
{
return test_harness(pmae_handling, "pmae_handling");
}

View file

@ -0,0 +1,93 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
/*
* Test that PMC5 & 6 are frozen (ie. don't overflow) when they are not being
* used. Tests the MMCR0_FC56 logic in the kernel.
*/
static int pmc56_overflowed;
static void ebb_callee(void)
{
uint64_t val;
val = mfspr(SPRN_BESCR);
if (!(val & BESCR_PMEO)) {
ebb_state.stats.spurious++;
goto out;
}
ebb_state.stats.ebb_count++;
count_pmc(2, sample_period);
val = mfspr(SPRN_PMC5);
if (val >= COUNTER_OVERFLOW)
pmc56_overflowed++;
count_pmc(5, COUNTER_OVERFLOW);
val = mfspr(SPRN_PMC6);
if (val >= COUNTER_OVERFLOW)
pmc56_overflowed++;
count_pmc(6, COUNTER_OVERFLOW);
out:
reset_ebb();
}
int pmc56_overflow(void)
{
struct event event;
/* Use PMC2 so we set PMCjCE, which enables PMC5/6 */
event_init(&event, 0x2001e);
event_leader_ebb_init(&event);
event.attr.exclude_kernel = 1;
event.attr.exclude_hv = 1;
event.attr.exclude_idle = 1;
FAIL_IF(event_open(&event));
setup_ebb_handler(ebb_callee);
ebb_global_enable();
FAIL_IF(ebb_event_enable(&event));
mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
mtspr(SPRN_PMC5, 0);
mtspr(SPRN_PMC6, 0);
while (ebb_state.stats.ebb_count < 10)
FAIL_IF(core_busy_loop());
ebb_global_disable();
ebb_freeze_pmcs();
count_pmc(2, sample_period);
dump_ebb_state();
printf("PMC5/6 overflow %d\n", pmc56_overflowed);
event_close(&event);
FAIL_IF(ebb_state.stats.ebb_count == 0 || pmc56_overflowed != 0);
return 0;
}
int main(void)
{
return test_harness(pmc56_overflow, "pmc56_overflow");
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_REG_H
#define _SELFTESTS_POWERPC_REG_H
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval; })
#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : \
: "r" ((unsigned long)(v)) \
: "memory")
#define mb() asm volatile("sync" : : : "memory");
#define SPRN_MMCR2 769
#define SPRN_MMCRA 770
#define SPRN_MMCR0 779
#define MMCR0_PMAO 0x00000080
#define MMCR0_PMAE 0x04000000
#define MMCR0_FC 0x80000000
#define SPRN_EBBHR 804
#define SPRN_EBBRR 805
#define SPRN_BESCR 806 /* Branch event status & control register */
#define SPRN_BESCRS 800 /* Branch event status & control set (1 bits set to 1) */
#define SPRN_BESCRSU 801 /* Branch event status & control set upper */
#define SPRN_BESCRR 802 /* Branch event status & control REset (1 bits set to 0) */
#define SPRN_BESCRRU 803 /* Branch event status & control REset upper */
#define BESCR_PMEO 0x1 /* PMU Event-based exception Occurred */
#define BESCR_PME (0x1ul << 32) /* PMU Event-based exception Enable */
#define SPRN_PMC1 771
#define SPRN_PMC2 772
#define SPRN_PMC3 773
#define SPRN_PMC4 774
#define SPRN_PMC5 775
#define SPRN_PMC6 776
#define SPRN_SIAR 780
#define SPRN_SDAR 781
#define SPRN_SIER 768
#endif /* _SELFTESTS_POWERPC_REG_H */

View file

@ -0,0 +1,39 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include "ebb.h"
#include "reg.h"
/*
* Test basic access to the EBB regs, they should be user accessible with no
* kernel interaction required.
*/
int reg_access(void)
{
uint64_t val, expected;
expected = 0x8000000100000000ull;
mtspr(SPRN_BESCR, expected);
val = mfspr(SPRN_BESCR);
FAIL_IF(val != expected);
expected = 0x0000000001000000ull;
mtspr(SPRN_EBBHR, expected);
val = mfspr(SPRN_EBBHR);
FAIL_IF(val != expected);
return 0;
}
int main(void)
{
return test_harness(reg_access, "reg_access");
}

View file

@ -0,0 +1,91 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a pinned per-task event vs an EBB - in that order. The pinned per-task
* event should prevent the EBB event from being enabled.
*/
static int setup_child_event(struct event *event, pid_t child_pid)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.pinned = 1;
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(event, child_pid));
FAIL_IF(event_enable(event));
return 0;
}
int task_event_pinned_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
int rc;
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the task event first */
rc = setup_child_event(&event, pid);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* We expect it to fail to read the event */
FAIL_IF(wait_for_child(pid) != 2);
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
FAIL_IF(event.result.value == 0);
/*
* For reasons I don't understand enabled is usually just slightly
* lower than running. Would be good to confirm why.
*/
FAIL_IF(event.result.enabled == 0);
FAIL_IF(event.result.running == 0);
return 0;
}
int main(void)
{
return test_harness(task_event_pinned_vs_ebb, "task_event_pinned_vs_ebb");
}

View file

@ -0,0 +1,83 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ebb.h"
/*
* Tests a per-task event vs an EBB - in that order. The EBB should push the
* per-task event off the PMU.
*/
static int setup_child_event(struct event *event, pid_t child_pid)
{
event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
event->attr.exclude_kernel = 1;
event->attr.exclude_hv = 1;
event->attr.exclude_idle = 1;
FAIL_IF(event_open_with_pid(event, child_pid));
FAIL_IF(event_enable(event));
return 0;
}
int task_event_vs_ebb(void)
{
union pipe read_pipe, write_pipe;
struct event event;
pid_t pid;
int rc;
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
pid = fork();
if (pid == 0) {
/* NB order of pipes looks reversed */
exit(ebb_child(write_pipe, read_pipe));
}
/* We setup the task event first */
rc = setup_child_event(&event, pid);
if (rc) {
kill_child_and_wait(pid);
return rc;
}
/* Signal the child to install its EBB event and wait */
if (sync_with_child(read_pipe, write_pipe))
/* If it fails, wait for it to exit */
goto wait;
/* Signal the child to run */
FAIL_IF(sync_with_child(read_pipe, write_pipe));
wait:
/* The EBB event should push the task event off so the child should succeed */
FAIL_IF(wait_for_child(pid));
FAIL_IF(event_disable(&event));
FAIL_IF(event_read(&event));
event_report(&event);
/* The task event may have run, or not so we can't assert anything about it */
return 0;
}
int main(void)
{
return test_harness(task_event_vs_ebb, "task_event_vs_ebb");
}

View file

@ -0,0 +1,300 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include "trace.h"
struct trace_buffer *trace_buffer_allocate(u64 size)
{
struct trace_buffer *tb;
if (size < sizeof(*tb)) {
fprintf(stderr, "Error: trace buffer too small\n");
return NULL;
}
tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (tb == MAP_FAILED) {
perror("mmap");
return NULL;
}
tb->size = size;
tb->tail = tb->data;
tb->overflow = false;
return tb;
}
static bool trace_check_bounds(struct trace_buffer *tb, void *p)
{
return p < ((void *)tb + tb->size);
}
static bool trace_check_alloc(struct trace_buffer *tb, void *p)
{
/*
* If we ever overflowed don't allow any more input. This prevents us
* from dropping a large item and then later logging a small one. The
* buffer should just stop when overflow happened, not be patchy. If
* you're overflowing, make your buffer bigger.
*/
if (tb->overflow)
return false;
if (!trace_check_bounds(tb, p)) {
tb->overflow = true;
return false;
}
return true;
}
static void *trace_alloc(struct trace_buffer *tb, int bytes)
{
void *p, *newtail;
p = tb->tail;
newtail = tb->tail + bytes;
if (!trace_check_alloc(tb, newtail))
return NULL;
tb->tail = newtail;
return p;
}
static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
{
struct trace_entry *e;
e = trace_alloc(tb, sizeof(*e) + payload_size);
if (e)
e->length = payload_size;
return e;
}
int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
{
struct trace_entry *e;
u64 *p;
e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_REG;
p = (u64 *)e->data;
*p++ = reg;
*p++ = value;
return 0;
}
int trace_log_counter(struct trace_buffer *tb, u64 value)
{
struct trace_entry *e;
u64 *p;
e = trace_alloc_entry(tb, sizeof(value));
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_COUNTER;
p = (u64 *)e->data;
*p++ = value;
return 0;
}
int trace_log_string(struct trace_buffer *tb, char *str)
{
struct trace_entry *e;
char *p;
int len;
len = strlen(str);
/* We NULL terminate to make printing easier */
e = trace_alloc_entry(tb, len + 1);
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_STRING;
p = (char *)e->data;
memcpy(p, str, len);
p += len;
*p = '\0';
return 0;
}
int trace_log_indent(struct trace_buffer *tb)
{
struct trace_entry *e;
e = trace_alloc_entry(tb, 0);
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_INDENT;
return 0;
}
int trace_log_outdent(struct trace_buffer *tb)
{
struct trace_entry *e;
e = trace_alloc_entry(tb, 0);
if (!e)
return -ENOSPC;
e->type = TRACE_TYPE_OUTDENT;
return 0;
}
static void trace_print_header(int seq, int prefix)
{
printf("%*s[%d]: ", prefix, "", seq);
}
static char *trace_decode_reg(int reg)
{
switch (reg) {
case 769: return "SPRN_MMCR2"; break;
case 770: return "SPRN_MMCRA"; break;
case 779: return "SPRN_MMCR0"; break;
case 804: return "SPRN_EBBHR"; break;
case 805: return "SPRN_EBBRR"; break;
case 806: return "SPRN_BESCR"; break;
case 800: return "SPRN_BESCRS"; break;
case 801: return "SPRN_BESCRSU"; break;
case 802: return "SPRN_BESCRR"; break;
case 803: return "SPRN_BESCRRU"; break;
case 771: return "SPRN_PMC1"; break;
case 772: return "SPRN_PMC2"; break;
case 773: return "SPRN_PMC3"; break;
case 774: return "SPRN_PMC4"; break;
case 775: return "SPRN_PMC5"; break;
case 776: return "SPRN_PMC6"; break;
case 780: return "SPRN_SIAR"; break;
case 781: return "SPRN_SDAR"; break;
case 768: return "SPRN_SIER"; break;
}
return NULL;
}
static void trace_print_reg(struct trace_entry *e)
{
u64 *p, *reg, *value;
char *name;
p = (u64 *)e->data;
reg = p++;
value = p;
name = trace_decode_reg(*reg);
if (name)
printf("register %-10s = 0x%016llx\n", name, *value);
else
printf("register %lld = 0x%016llx\n", *reg, *value);
}
static void trace_print_counter(struct trace_entry *e)
{
u64 *value;
value = (u64 *)e->data;
printf("counter = %lld\n", *value);
}
static void trace_print_string(struct trace_entry *e)
{
char *str;
str = (char *)e->data;
puts(str);
}
#define BASE_PREFIX 2
#define PREFIX_DELTA 8
static void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
{
switch (e->type) {
case TRACE_TYPE_REG:
trace_print_header(seq, *prefix);
trace_print_reg(e);
break;
case TRACE_TYPE_COUNTER:
trace_print_header(seq, *prefix);
trace_print_counter(e);
break;
case TRACE_TYPE_STRING:
trace_print_header(seq, *prefix);
trace_print_string(e);
break;
case TRACE_TYPE_INDENT:
trace_print_header(seq, *prefix);
puts("{");
*prefix += PREFIX_DELTA;
break;
case TRACE_TYPE_OUTDENT:
*prefix -= PREFIX_DELTA;
if (*prefix < BASE_PREFIX)
*prefix = BASE_PREFIX;
trace_print_header(seq, *prefix);
puts("}");
break;
default:
trace_print_header(seq, *prefix);
printf("entry @ %p type %d\n", e, e->type);
break;
}
}
void trace_buffer_print(struct trace_buffer *tb)
{
struct trace_entry *e;
int i, prefix;
void *p;
printf("Trace buffer dump:\n");
printf(" address %p \n", tb);
printf(" tail %p\n", tb->tail);
printf(" size %llu\n", tb->size);
printf(" overflow %s\n", tb->overflow ? "TRUE" : "false");
printf(" Content:\n");
p = tb->data;
i = 0;
prefix = BASE_PREFIX;
while (trace_check_bounds(tb, p) && p < tb->tail) {
e = p;
trace_print_entry(e, i, &prefix);
i++;
p = (void *)e + sizeof(*e) + e->length;
}
}
void trace_print_location(struct trace_buffer *tb)
{
printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
#define _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
#include "utils.h"
#define TRACE_TYPE_REG 1
#define TRACE_TYPE_COUNTER 2
#define TRACE_TYPE_STRING 3
#define TRACE_TYPE_INDENT 4
#define TRACE_TYPE_OUTDENT 5
struct trace_entry
{
u8 type;
u8 length;
u8 data[0];
};
struct trace_buffer
{
u64 size;
bool overflow;
void *tail;
u8 data[0];
};
struct trace_buffer *trace_buffer_allocate(u64 size);
int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value);
int trace_log_counter(struct trace_buffer *tb, u64 value);
int trace_log_string(struct trace_buffer *tb, char *str);
int trace_log_indent(struct trace_buffer *tb);
int trace_log_outdent(struct trace_buffer *tb);
void trace_buffer_print(struct trace_buffer *tb);
void trace_print_location(struct trace_buffer *tb);
#endif /* _SELFTESTS_POWERPC_PMU_EBB_TRACE_H */

View file

@ -0,0 +1,131 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include "event.h"
int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, attr, pid, cpu,
group_fd, flags);
}
void event_init_opts(struct event *e, u64 config, int type, char *name)
{
memset(e, 0, sizeof(*e));
e->name = name;
e->attr.type = type;
e->attr.config = config;
e->attr.size = sizeof(e->attr);
/* This has to match the structure layout in the header */
e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \
PERF_FORMAT_TOTAL_TIME_RUNNING;
}
void event_init_named(struct event *e, u64 config, char *name)
{
event_init_opts(e, config, PERF_TYPE_RAW, name);
}
void event_init(struct event *e, u64 config)
{
event_init_opts(e, config, PERF_TYPE_RAW, "event");
}
#define PERF_CURRENT_PID 0
#define PERF_NO_PID -1
#define PERF_NO_CPU -1
#define PERF_NO_GROUP -1
int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd)
{
e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0);
if (e->fd == -1) {
perror("perf_event_open");
return -1;
}
return 0;
}
int event_open_with_group(struct event *e, int group_fd)
{
return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
}
int event_open_with_pid(struct event *e, pid_t pid)
{
return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP);
}
int event_open_with_cpu(struct event *e, int cpu)
{
return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP);
}
int event_open(struct event *e)
{
return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
}
void event_close(struct event *e)
{
close(e->fd);
}
int event_enable(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
}
int event_disable(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_DISABLE);
}
int event_reset(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_RESET);
}
int event_read(struct event *e)
{
int rc;
rc = read(e->fd, &e->result, sizeof(e->result));
if (rc != sizeof(e->result)) {
fprintf(stderr, "read error on event %p!\n", e);
return -1;
}
return 0;
}
void event_report_justified(struct event *e, int name_width, int result_width)
{
printf("%*s: result %*llu ", name_width, e->name, result_width,
e->result.value);
if (e->result.running == e->result.enabled)
printf("running/enabled %llu\n", e->result.running);
else
printf("running %llu enabled %llu\n", e->result.running,
e->result.enabled);
}
void event_report(struct event *e)
{
event_report_justified(e, 0, 0);
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EVENT_H
#define _SELFTESTS_POWERPC_PMU_EVENT_H
#include <unistd.h>
#include <linux/perf_event.h>
#include "utils.h"
struct event {
struct perf_event_attr attr;
char *name;
int fd;
/* This must match the read_format we use */
struct {
u64 value;
u64 running;
u64 enabled;
} result;
};
void event_init(struct event *e, u64 config);
void event_init_named(struct event *e, u64 config, char *name);
void event_init_opts(struct event *e, u64 config, int type, char *name);
int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd);
int event_open_with_group(struct event *e, int group_fd);
int event_open_with_pid(struct event *e, pid_t pid);
int event_open_with_cpu(struct event *e, int cpu);
int event_open(struct event *e);
void event_close(struct event *e);
int event_enable(struct event *e);
int event_disable(struct event *e);
int event_reset(struct event *e);
int event_read(struct event *e);
void event_report_justified(struct event *e, int name_width, int result_width);
void event_report(struct event *e);
#endif /* _SELFTESTS_POWERPC_PMU_EVENT_H */

View file

@ -0,0 +1,48 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include "event.h"
#include "utils.h"
#define MALLOC_SIZE (0x10000 * 10) /* Ought to be enough .. */
/*
* Tests that the L3 bank handling is correct. We fixed it in commit e9aaac1.
*/
static int l3_bank_test(void)
{
struct event event;
char *p;
int i;
p = malloc(MALLOC_SIZE);
FAIL_IF(!p);
event_init(&event, 0x84918F);
FAIL_IF(event_open(&event));
for (i = 0; i < MALLOC_SIZE; i += 0x10000)
p[i] = i;
event_read(&event);
event_report(&event);
FAIL_IF(event.result.running == 0);
FAIL_IF(event.result.enabled == 0);
event_close(&event);
free(p);
return 0;
}
int main(void)
{
return test_harness(l3_bank_test, "l3_bank_test");
}

View file

@ -0,0 +1,300 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#include <sched.h>
#include <setjmp.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "utils.h"
#include "lib.h"
int pick_online_cpu(void)
{
cpu_set_t mask;
int cpu;
CPU_ZERO(&mask);
if (sched_getaffinity(0, sizeof(mask), &mask)) {
perror("sched_getaffinity");
return -1;
}
/* We prefer a primary thread, but skip 0 */
for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8)
if (CPU_ISSET(cpu, &mask))
return cpu;
/* Search for anything, but in reverse */
for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--)
if (CPU_ISSET(cpu, &mask))
return cpu;
printf("No cpus in affinity mask?!\n");
return -1;
}
int bind_to_cpu(int cpu)
{
cpu_set_t mask;
printf("Binding to cpu %d\n", cpu);
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
return sched_setaffinity(0, sizeof(mask), &mask);
}
#define PARENT_TOKEN 0xAA
#define CHILD_TOKEN 0x55
int sync_with_child(union pipe read_pipe, union pipe write_pipe)
{
char c = PARENT_TOKEN;
FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
if (c != CHILD_TOKEN) /* sometimes expected */
return 1;
return 0;
}
int wait_for_parent(union pipe read_pipe)
{
char c;
FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
FAIL_IF(c != PARENT_TOKEN);
return 0;
}
int notify_parent(union pipe write_pipe)
{
char c = CHILD_TOKEN;
FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
return 0;
}
int notify_parent_of_error(union pipe write_pipe)
{
char c = ~CHILD_TOKEN;
FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
return 0;
}
int wait_for_child(pid_t child_pid)
{
int rc;
if (waitpid(child_pid, &rc, 0) == -1) {
perror("waitpid");
return 1;
}
if (WIFEXITED(rc))
rc = WEXITSTATUS(rc);
else
rc = 1; /* Signal or other */
return rc;
}
int kill_child_and_wait(pid_t child_pid)
{
kill(child_pid, SIGTERM);
return wait_for_child(child_pid);
}
static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
{
volatile int i = 0;
/*
* We are just here to eat cpu and die. So make sure we can be killed,
* and also don't do any custom SIGTERM handling.
*/
signal(SIGTERM, SIG_DFL);
notify_parent(write_pipe);
wait_for_parent(read_pipe);
/* Soak up cpu forever */
while (1) i++;
return 0;
}
pid_t eat_cpu(int (test_function)(void))
{
union pipe read_pipe, write_pipe;
int cpu, rc;
pid_t pid;
cpu = pick_online_cpu();
FAIL_IF(cpu < 0);
FAIL_IF(bind_to_cpu(cpu));
if (pipe(read_pipe.fds) == -1)
return -1;
if (pipe(write_pipe.fds) == -1)
return -1;
pid = fork();
if (pid == 0)
exit(eat_cpu_child(write_pipe, read_pipe));
if (sync_with_child(read_pipe, write_pipe)) {
rc = -1;
goto out;
}
printf("main test running as pid %d\n", getpid());
rc = test_function();
out:
kill(pid, SIGKILL);
return rc;
}
struct addr_range libc, vdso;
int parse_proc_maps(void)
{
unsigned long start, end;
char execute, name[128];
FILE *f;
int rc;
f = fopen("/proc/self/maps", "r");
if (!f) {
perror("fopen");
return -1;
}
do {
/* This skips line with no executable which is what we want */
rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
&start, &end, &execute, name);
if (rc <= 0)
break;
if (execute != 'x')
continue;
if (strstr(name, "libc")) {
libc.first = start;
libc.last = end - 1;
} else if (strstr(name, "[vdso]")) {
vdso.first = start;
vdso.last = end - 1;
}
} while(1);
fclose(f);
return 0;
}
#define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid"
bool require_paranoia_below(int level)
{
unsigned long current;
char *end, buf[16];
FILE *f;
int rc;
rc = -1;
f = fopen(PARANOID_PATH, "r");
if (!f) {
perror("fopen");
goto out;
}
if (!fgets(buf, sizeof(buf), f)) {
printf("Couldn't read " PARANOID_PATH "?\n");
goto out_close;
}
current = strtoul(buf, &end, 10);
if (end == buf) {
printf("Couldn't parse " PARANOID_PATH "?\n");
goto out_close;
}
if (current >= level)
goto out;
rc = 0;
out_close:
fclose(f);
out:
return rc;
}
static char auxv[4096];
void *get_auxv_entry(int type)
{
ElfW(auxv_t) *p;
void *result;
ssize_t num;
int fd;
fd = open("/proc/self/auxv", O_RDONLY);
if (fd == -1) {
perror("open");
return NULL;
}
result = NULL;
num = read(fd, auxv, sizeof(auxv));
if (num < 0) {
perror("read");
goto out;
}
if (num > sizeof(auxv)) {
printf("Overflowed auxv buffer\n");
goto out;
}
p = (ElfW(auxv_t) *)auxv;
while (p->a_type != AT_NULL) {
if (p->a_type == type) {
result = (void *)p->a_un.a_val;
break;
}
p++;
}
out:
close(fd);
return result;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
#define __SELFTESTS_POWERPC_PMU_LIB_H
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
union pipe {
struct {
int read_fd;
int write_fd;
};
int fds[2];
};
extern int pick_online_cpu(void);
extern int bind_to_cpu(int cpu);
extern int kill_child_and_wait(pid_t child_pid);
extern int wait_for_child(pid_t child_pid);
extern int sync_with_child(union pipe read_pipe, union pipe write_pipe);
extern int wait_for_parent(union pipe read_pipe);
extern int notify_parent(union pipe write_pipe);
extern int notify_parent_of_error(union pipe write_pipe);
extern pid_t eat_cpu(int (test_function)(void));
extern bool require_paranoia_below(int level);
extern void *get_auxv_entry(int type);
struct addr_range {
uint64_t first, last;
};
extern struct addr_range libc, vdso;
int parse_proc_maps(void);
#endif /* __SELFTESTS_POWERPC_PMU_LIB_H */

View file

@ -0,0 +1,43 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#include <ppc-asm.h>
.text
FUNC_START(thirty_two_instruction_loop)
cmpdi r3,0
beqlr
addi r4,r3,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1
addi r4,r4,1 # 28 addi's
subi r3,r3,1
b FUNC_NAME(thirty_two_instruction_loop)
FUNC_END(thirty_two_instruction_loop)

View file

@ -0,0 +1,114 @@
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#define _GNU_SOURCE
#include <elf.h>
#include <limits.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "event.h"
#include "lib.h"
#include "utils.h"
/*
* Test that per-event excludes work.
*/
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);
/*
* We need to create the events disabled, otherwise the running/enabled
* counts don't match up.
*/
e = &events[0];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions");
e->attr.disabled = 1;
e = &events[1];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions(k)");
e->attr.disabled = 1;
e->attr.exclude_user = 1;
e->attr.exclude_hv = 1;
e = &events[2];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions(h)");
e->attr.disabled = 1;
e->attr.exclude_user = 1;
e->attr.exclude_kernel = 1;
e = &events[3];
event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
PERF_TYPE_HARDWARE, "instructions(u)");
e->attr.disabled = 1;
e->attr.exclude_hv = 1;
e->attr.exclude_kernel = 1;
FAIL_IF(event_open(&events[0]));
/*
* The open here will fail if we don't have per event exclude support,
* because the second event has an incompatible set of exclude settings
* and we're asking for the events to be in a group.
*/
for (i = 1; i < 4; i++)
FAIL_IF(event_open_with_group(&events[i], events[0].fd));
/*
* Even though the above will fail without per-event excludes we keep
* testing in order to be thorough.
*/
prctl(PR_TASK_PERF_EVENTS_ENABLE);
/* Spin for a while */
for (i = 0; i < INT_MAX; i++)
asm volatile("" : : : "memory");
prctl(PR_TASK_PERF_EVENTS_DISABLE);
for (i = 0; i < 4; i++) {
FAIL_IF(event_read(&events[i]));
event_report(&events[i]);
}
/*
* We should see that all events have enabled == running. That
* shows that they were all on the PMU at once.
*/
for (i = 0; i < 4; i++)
FAIL_IF(events[i].result.running != events[i].result.enabled);
/*
* We can also check that the result for instructions is >= all the
* other counts. That's because it is counting all instructions while
* the others are counting a subset.
*/
for (i = 1; i < 4; i++)
FAIL_IF(events[0].result.value < events[i].result.value);
for (i = 0; i < 4; i++)
event_close(&events[i]);
return 0;
}
int main(void)
{
return test_harness(per_event_excludes, "per_event_excludes");
}

View file

@ -0,0 +1,17 @@
CFLAGS += -I$(CURDIR)
PROGS := load_unaligned_zeropad
all: $(PROGS)
$(PROGS): ../harness.c
run_tests: all
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
clean:
rm -f $(PROGS) *.o
.PHONY: all run_tests clean

View file

@ -0,0 +1 @@
../.././../../../../arch/powerpc/include/asm/asm-compat.h

View file

@ -0,0 +1,147 @@
/*
* Userspace test harness for load_unaligned_zeropad. Creates two
* pages and uses mprotect to prevent access to the second page and
* a SEGV handler that walks the exception tables and runs the fixup
* routine.
*
* The results are compared against a normal load that is that is
* performed while access to the second page is enabled via mprotect.
*
* Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#define FIXUP_SECTION ".ex_fixup"
#include "word-at-a-time.h"
#include "utils.h"
static int page_size;
static char *mem_region;
static int protect_region(void)
{
if (mprotect(mem_region + page_size, page_size, PROT_NONE)) {
perror("mprotect");
return 1;
}
return 0;
}
static int unprotect_region(void)
{
if (mprotect(mem_region + page_size, page_size, PROT_READ|PROT_WRITE)) {
perror("mprotect");
return 1;
}
return 0;
}
extern char __start___ex_table[];
extern char __stop___ex_table[];
#if defined(__powerpc64__)
#define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP]
#elif defined(__powerpc__)
#define UCONTEXT_NIA(UC) (UC)->uc_mcontext.uc_regs->gregs[PT_NIP]
#else
#error implement UCONTEXT_NIA
#endif
static int segv_error;
static void segv_handler(int signr, siginfo_t *info, void *ptr)
{
ucontext_t *uc = (ucontext_t *)ptr;
unsigned long addr = (unsigned long)info->si_addr;
unsigned long *ip = &UCONTEXT_NIA(uc);
unsigned long *ex_p = (unsigned long *)__start___ex_table;
while (ex_p < (unsigned long *)__stop___ex_table) {
unsigned long insn, fixup;
insn = *ex_p++;
fixup = *ex_p++;
if (insn == *ip) {
*ip = fixup;
return;
}
}
printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr);
segv_error++;
}
static void setup_segv_handler(void)
{
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_sigaction = segv_handler;
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, NULL);
}
static int do_one_test(char *p, int page_offset)
{
unsigned long should;
unsigned long got;
FAIL_IF(unprotect_region());
should = *(unsigned long *)p;
FAIL_IF(protect_region());
got = load_unaligned_zeropad(p);
if (should != got)
printf("offset %u load_unaligned_zeropad returned 0x%lx, should be 0x%lx\n", page_offset, got, should);
return 0;
}
static int test_body(void)
{
unsigned long i;
page_size = getpagesize();
mem_region = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
FAIL_IF(mem_region == MAP_FAILED);
for (i = 0; i < page_size; i++)
mem_region[i] = i;
memset(mem_region+page_size, 0, page_size);
setup_segv_handler();
for (i = 0; i < page_size; i++)
FAIL_IF(do_one_test(mem_region+i, i));
FAIL_IF(segv_error);
return 0;
}
int main(void)
{
return test_harness(test_body, "load_unaligned_zeropad");
}

View file

@ -0,0 +1 @@
../../../../../arch/powerpc/include/asm/word-at-a-time.h

View file

@ -0,0 +1,52 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_SUBUNIT_H
#define _SELFTESTS_POWERPC_SUBUNIT_H
static inline void test_start(char *name)
{
printf("test: %s\n", name);
}
static inline void test_failure_detail(char *name, char *detail)
{
printf("failure: %s [%s]\n", name, detail);
}
static inline void test_failure(char *name)
{
printf("failure: %s\n", name);
}
static inline void test_error(char *name)
{
printf("error: %s\n", name);
}
static inline void test_skip(char *name)
{
printf("skip: %s\n", name);
}
static inline void test_success(char *name)
{
printf("success: %s\n", name);
}
static inline void test_finish(char *name, int status)
{
if (status)
test_failure(name);
else
test_success(name);
}
static inline void test_set_git_version(char *value)
{
printf("tags: git_version:%s\n", value);
}
#endif /* _SELFTESTS_POWERPC_SUBUNIT_H */

View file

@ -0,0 +1,15 @@
PROGS := tm-resched-dscr
all: $(PROGS)
$(PROGS): ../harness.c
run_tests: all
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
clean:
rm -f $(PROGS) *.o
.PHONY: all run_tests clean

View file

@ -0,0 +1,98 @@
/* Test context switching to see if the DSCR SPR is correctly preserved
* when within a transaction.
*
* Note: We assume that the DSCR has been left at the default value (0)
* for all CPUs.
*
* Method:
*
* Set a value into the DSCR.
*
* Start a transaction, and suspend it (*).
*
* Hard loop checking to see if the transaction has become doomed.
*
* Now that we *may* have been preempted, record the DSCR and TEXASR SPRS.
*
* If the abort was because of a context switch, check the DSCR value.
* Otherwise, try again.
*
* (*) If the transaction is not suspended we can't see the problem because
* the transaction abort handler will restore the DSCR to it's checkpointed
* value before we regain control.
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <asm/tm.h>
#include "utils.h"
#define TBEGIN ".long 0x7C00051D ;"
#define TEND ".long 0x7C00055D ;"
#define TCHECK ".long 0x7C00059C ;"
#define TSUSPEND ".long 0x7C0005DD ;"
#define TRESUME ".long 0x7C2005DD ;"
#define SPRN_TEXASR 0x82
#define SPRN_DSCR 0x03
int test_body(void)
{
uint64_t rv, dscr1 = 1, dscr2, texasr;
printf("Check DSCR TM context switch: ");
fflush(stdout);
for (;;) {
rv = 1;
asm __volatile__ (
/* set a known value into the DSCR */
"ld 3, %[dscr1];"
"mtspr %[sprn_dscr], 3;"
/* start and suspend a transaction */
TBEGIN
"beq 1f;"
TSUSPEND
/* hard loop until the transaction becomes doomed */
"2: ;"
TCHECK
"bc 4, 0, 2b;"
/* record DSCR and TEXASR */
"mfspr 3, %[sprn_dscr];"
"std 3, %[dscr2];"
"mfspr 3, %[sprn_texasr];"
"std 3, %[texasr];"
TRESUME
TEND
"li %[rv], 0;"
"1: ;"
: [rv]"=r"(rv), [dscr2]"=m"(dscr2), [texasr]"=m"(texasr)
: [dscr1]"m"(dscr1)
, [sprn_dscr]"i"(SPRN_DSCR), [sprn_texasr]"i"(SPRN_TEXASR)
: "memory", "r3"
);
assert(rv); /* make sure the transaction aborted */
if ((texasr >> 56) != TM_CAUSE_RESCHED) {
putchar('.');
fflush(stdout);
continue;
}
if (dscr2 != dscr1) {
printf(" FAIL\n");
return 1;
} else {
printf(" OK\n");
return 0;
}
}
}
int main(void)
{
return test_harness(test_body, "tm_resched_dscr");
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
* Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_UTILS_H
#define _SELFTESTS_POWERPC_UTILS_H
#include <stdint.h>
#include <stdbool.h>
/* Avoid headaches with PRI?64 - just use %ll? always */
typedef unsigned long long u64;
typedef signed long long s64;
/* Just for familiarity */
typedef uint32_t u32;
typedef uint8_t u8;
int test_harness(int (test_function)(void), char *name);
/* Yes, this is evil */
#define FAIL_IF(x) \
do { \
if ((x)) { \
fprintf(stderr, \
"[FAIL] Test FAILED on line %d\n", __LINE__); \
return 1; \
} \
} while (0)
/* The test harness uses this, yes it's gross */
#define MAGIC_SKIP_RETURN_VALUE 99
#define SKIP_IF(x) \
do { \
if ((x)) { \
fprintf(stderr, \
"[SKIP] Test skipped on line %d\n", __LINE__); \
return MAGIC_SKIP_RETURN_VALUE; \
} \
} while (0)
#define _str(s) #s
#define str(s) _str(s)
#endif /* _SELFTESTS_POWERPC_UTILS_H */