/* * Adapted from Anton Blanchard's context switch microbenchmark. * * Copyright 2009, Anton Blanchard, IBM Corporation. * Copyright 2016, Mikey Neuling, Chris Smart, IBM Corporation. * * 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. * * This program tests the copy paste abort functionality of a P9 * (or later) by setting up two processes on the same CPU, one * which executes the copy instruction and the other which * executes paste. * * The paste instruction should never succeed, as the cp_abort * instruction is called by the kernel during a context switch. * */ #define _GNU_SOURCE #include #include #include #include "utils.h" #include #define READ_FD 0 #define WRITE_FD 1 #define NUM_LOOPS 1000 /* This defines the "paste" instruction from Power ISA 3.0 Book II, section 4.4. */ #define PASTE(RA, RB, L, RC) \ .long (0x7c00070c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10) | (RC) << (31-31)) int paste(void *i) { int cr; asm volatile(str(PASTE(0, %1, 1, 1))";" "mfcr %0;" : "=r" (cr) : "b" (i) : "memory" ); return cr; } /* This defines the "copy" instruction from Power ISA 3.0 Book II, section 4.4. */ #define COPY(RA, RB, L) \ .long (0x7c00060c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10)) void copy(void *i) { asm volatile(str(COPY(0, %0, 1))";" : : "b" (i) : "memory" ); } int test_cp_abort(void) { /* 128 bytes for a full cache line */ char buf[128] __cacheline_aligned; cpu_set_t cpuset; int fd1[2], fd2[2], pid; char c; /* only run this test on a P9 or later */ SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); /* * Run both processes on the same CPU, so that copy is more likely * to leak into a paste. */ CPU_ZERO(&cpuset); CPU_SET(pick_online_cpu(), &cpuset); FAIL_IF(sched_setaffinity(0, sizeof(cpuset), &cpuset)); FAIL_IF(pipe(fd1) || pipe(fd2)); pid = fork(); FAIL_IF(pid < 0); if (!pid) { for (int i = 0; i < NUM_LOOPS; i++) { FAIL_IF((write(fd1[WRITE_FD], &c, 1)) != 1); FAIL_IF((read(fd2[READ_FD], &c, 1)) != 1); /* A paste succeeds if CR0 EQ bit is set */ FAIL_IF(paste(buf) & 0x20000000); } } else { for (int i = 0; i < NUM_LOOPS; i++) { FAIL_IF((read(fd1[READ_FD], &c, 1)) != 1); copy(buf); FAIL_IF((write(fd2[WRITE_FD], &c, 1) != 1)); } } return 0; } int main(int argc, char *argv[]) { return test_harness(test_cp_abort, "cp_abort"); }