cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rdpmc.c (3493B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <errno.h>
      3#include <unistd.h>
      4#include <stdlib.h>
      5#include <signal.h>
      6#include <sys/mman.h>
      7#include <sys/types.h>
      8#include <sys/wait.h>
      9#include <linux/string.h>
     10#include <linux/types.h>
     11#include "perf-sys.h"
     12#include "debug.h"
     13#include "tests/tests.h"
     14#include "cloexec.h"
     15#include "event.h"
     16#include <internal/lib.h> // page_size
     17#include "arch-tests.h"
     18
     19static u64 rdpmc(unsigned int counter)
     20{
     21	unsigned int low, high;
     22
     23	asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
     24
     25	return low | ((u64)high) << 32;
     26}
     27
     28static u64 rdtsc(void)
     29{
     30	unsigned int low, high;
     31
     32	asm volatile("rdtsc" : "=a" (low), "=d" (high));
     33
     34	return low | ((u64)high) << 32;
     35}
     36
     37static u64 mmap_read_self(void *addr)
     38{
     39	struct perf_event_mmap_page *pc = addr;
     40	u32 seq, idx, time_mult = 0, time_shift = 0;
     41	u64 count, cyc = 0, time_offset = 0, enabled, running, delta;
     42
     43	do {
     44		seq = pc->lock;
     45		barrier();
     46
     47		enabled = pc->time_enabled;
     48		running = pc->time_running;
     49
     50		if (enabled != running) {
     51			cyc = rdtsc();
     52			time_mult = pc->time_mult;
     53			time_shift = pc->time_shift;
     54			time_offset = pc->time_offset;
     55		}
     56
     57		idx = pc->index;
     58		count = pc->offset;
     59		if (idx)
     60			count += rdpmc(idx - 1);
     61
     62		barrier();
     63	} while (pc->lock != seq);
     64
     65	if (enabled != running) {
     66		u64 quot, rem;
     67
     68		quot = (cyc >> time_shift);
     69		rem = cyc & (((u64)1 << time_shift) - 1);
     70		delta = time_offset + quot * time_mult +
     71			((rem * time_mult) >> time_shift);
     72
     73		enabled += delta;
     74		if (idx)
     75			running += delta;
     76
     77		quot = count / running;
     78		rem = count % running;
     79		count = quot * enabled + (rem * enabled) / running;
     80	}
     81
     82	return count;
     83}
     84
     85/*
     86 * If the RDPMC instruction faults then signal this back to the test parent task:
     87 */
     88static void segfault_handler(int sig __maybe_unused,
     89			     siginfo_t *info __maybe_unused,
     90			     void *uc __maybe_unused)
     91{
     92	exit(-1);
     93}
     94
     95static int __test__rdpmc(void)
     96{
     97	volatile int tmp = 0;
     98	u64 i, loops = 1000;
     99	int n;
    100	int fd;
    101	void *addr;
    102	struct perf_event_attr attr = {
    103		.type = PERF_TYPE_HARDWARE,
    104		.config = PERF_COUNT_HW_INSTRUCTIONS,
    105		.exclude_kernel = 1,
    106	};
    107	u64 delta_sum = 0;
    108        struct sigaction sa;
    109	char sbuf[STRERR_BUFSIZE];
    110
    111	sigfillset(&sa.sa_mask);
    112	sa.sa_sigaction = segfault_handler;
    113	sa.sa_flags = 0;
    114	sigaction(SIGSEGV, &sa, NULL);
    115
    116	fd = sys_perf_event_open(&attr, 0, -1, -1,
    117				 perf_event_open_cloexec_flag());
    118	if (fd < 0) {
    119		pr_err("Error: sys_perf_event_open() syscall returned "
    120		       "with %d (%s)\n", fd,
    121		       str_error_r(errno, sbuf, sizeof(sbuf)));
    122		return -1;
    123	}
    124
    125	addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
    126	if (addr == (void *)(-1)) {
    127		pr_err("Error: mmap() syscall returned with (%s)\n",
    128		       str_error_r(errno, sbuf, sizeof(sbuf)));
    129		goto out_close;
    130	}
    131
    132	for (n = 0; n < 6; n++) {
    133		u64 stamp, now, delta;
    134
    135		stamp = mmap_read_self(addr);
    136
    137		for (i = 0; i < loops; i++)
    138			tmp++;
    139
    140		now = mmap_read_self(addr);
    141		loops *= 10;
    142
    143		delta = now - stamp;
    144		pr_debug("%14d: %14Lu\n", n, (long long)delta);
    145
    146		delta_sum += delta;
    147	}
    148
    149	munmap(addr, page_size);
    150	pr_debug("   ");
    151out_close:
    152	close(fd);
    153
    154	if (!delta_sum)
    155		return -1;
    156
    157	return 0;
    158}
    159
    160int test__rdpmc(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    161{
    162	int status = 0;
    163	int wret = 0;
    164	int ret;
    165	int pid;
    166
    167	pid = fork();
    168	if (pid < 0)
    169		return -1;
    170
    171	if (!pid) {
    172		ret = __test__rdpmc();
    173
    174		exit(ret);
    175	}
    176
    177	wret = waitpid(pid, &status, 0);
    178	if (wret < 0 || status)
    179		return -1;
    180
    181	return 0;
    182}