| /* | |
| * 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 <stdio.h> | |
| #include <unistd.h> | |
| #include <stdlib.h> | |
| #include "utils.h" | |
| #include <sched.h> | |
| #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"); | |
| } |