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

tpidr2.c (5601B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include <linux/sched.h>
      4#include <linux/wait.h>
      5
      6#define SYS_TPIDR2 "S3_3_C13_C0_5"
      7
      8#define EXPECTED_TESTS 5
      9
     10static void putstr(const char *str)
     11{
     12	write(1, str, strlen(str));
     13}
     14
     15static void putnum(unsigned int num)
     16{
     17	char c;
     18
     19	if (num / 10)
     20		putnum(num / 10);
     21
     22	c = '0' + (num % 10);
     23	write(1, &c, 1);
     24}
     25
     26static int tests_run;
     27static int tests_passed;
     28static int tests_failed;
     29static int tests_skipped;
     30
     31static void set_tpidr2(uint64_t val)
     32{
     33	asm volatile (
     34		"msr	" SYS_TPIDR2 ", %0\n"
     35		:
     36		: "r"(val)
     37		: "cc");
     38}
     39
     40static uint64_t get_tpidr2(void)
     41{
     42	uint64_t val;
     43
     44	asm volatile (
     45		"mrs	%0, " SYS_TPIDR2 "\n"
     46		: "=r"(val)
     47		:
     48		: "cc");
     49
     50	return val;
     51}
     52
     53static void print_summary(void)
     54{
     55	if (tests_passed + tests_failed + tests_skipped != EXPECTED_TESTS)
     56		putstr("# UNEXPECTED TEST COUNT: ");
     57
     58	putstr("# Totals: pass:");
     59	putnum(tests_passed);
     60	putstr(" fail:");
     61	putnum(tests_failed);
     62	putstr(" xfail:0 xpass:0 skip:");
     63	putnum(tests_skipped);
     64	putstr(" error:0\n");
     65}
     66
     67/* Processes should start with TPIDR2 == 0 */
     68static int default_value(void)
     69{
     70	return get_tpidr2() == 0;
     71}
     72
     73/* If we set TPIDR2 we should read that value */
     74static int write_read(void)
     75{
     76	set_tpidr2(getpid());
     77
     78	return getpid() == get_tpidr2();
     79}
     80
     81/* If we set a value we should read the same value after scheduling out */
     82static int write_sleep_read(void)
     83{
     84	set_tpidr2(getpid());
     85
     86	msleep(100);
     87
     88	return getpid() == get_tpidr2();
     89}
     90
     91/*
     92 * If we fork the value in the parent should be unchanged and the
     93 * child should start with the same value and be able to set its own
     94 * value.
     95 */
     96static int write_fork_read(void)
     97{
     98	pid_t newpid, waiting, oldpid;
     99	int status;
    100
    101	set_tpidr2(getpid());
    102
    103	oldpid = getpid();
    104	newpid = fork();
    105	if (newpid == 0) {
    106		/* In child */
    107		if (get_tpidr2() != oldpid) {
    108			putstr("# TPIDR2 changed in child: ");
    109			putnum(get_tpidr2());
    110			putstr("\n");
    111			exit(0);
    112		}
    113
    114		set_tpidr2(getpid());
    115		if (get_tpidr2() == getpid()) {
    116			exit(1);
    117		} else {
    118			putstr("# Failed to set TPIDR2 in child\n");
    119			exit(0);
    120		}
    121	}
    122	if (newpid < 0) {
    123		putstr("# fork() failed: -");
    124		putnum(-newpid);
    125		putstr("\n");
    126		return 0;
    127	}
    128
    129	for (;;) {
    130		waiting = waitpid(newpid, &status, 0);
    131
    132		if (waiting < 0) {
    133			if (errno == EINTR)
    134				continue;
    135			putstr("# waitpid() failed: ");
    136			putnum(errno);
    137			putstr("\n");
    138			return 0;
    139		}
    140		if (waiting != newpid) {
    141			putstr("# waitpid() returned wrong PID\n");
    142			return 0;
    143		}
    144
    145		if (!WIFEXITED(status)) {
    146			putstr("# child did not exit\n");
    147			return 0;
    148		}
    149
    150		if (getpid() != get_tpidr2()) {
    151			putstr("# TPIDR2 corrupted in parent\n");
    152			return 0;
    153		}
    154
    155		return WEXITSTATUS(status);
    156	}
    157}
    158
    159/*
    160 * sys_clone() has a lot of per architecture variation so just define
    161 * it here rather than adding it to nolibc, plus the raw API is a
    162 * little more convenient for this test.
    163 */
    164static int sys_clone(unsigned long clone_flags, unsigned long newsp,
    165		     int *parent_tidptr, unsigned long tls,
    166		     int *child_tidptr)
    167{
    168	return my_syscall5(__NR_clone, clone_flags, newsp, parent_tidptr, tls,
    169			   child_tidptr);
    170}
    171
    172/*
    173 * If we clone with CLONE_SETTLS then the value in the parent should
    174 * be unchanged and the child should start with zero and be able to
    175 * set its own value.
    176 */
    177static int write_clone_read(void)
    178{
    179	int parent_tid, child_tid;
    180	pid_t parent, waiting;
    181	int ret, status;
    182
    183	parent = getpid();
    184	set_tpidr2(parent);
    185
    186	ret = sys_clone(CLONE_SETTLS, 0, &parent_tid, 0, &child_tid);
    187	if (ret == -1) {
    188		putstr("# clone() failed\n");
    189		putnum(errno);
    190		putstr("\n");
    191		return 0;
    192	}
    193
    194	if (ret == 0) {
    195		/* In child */
    196		if (get_tpidr2() != 0) {
    197			putstr("# TPIDR2 non-zero in child: ");
    198			putnum(get_tpidr2());
    199			putstr("\n");
    200			exit(0);
    201		}
    202
    203		if (gettid() == 0)
    204			putstr("# Child TID==0\n");
    205		set_tpidr2(gettid());
    206		if (get_tpidr2() == gettid()) {
    207			exit(1);
    208		} else {
    209			putstr("# Failed to set TPIDR2 in child\n");
    210			exit(0);
    211		}
    212	}
    213
    214	for (;;) {
    215		waiting = wait4(ret, &status, __WCLONE, NULL);
    216
    217		if (waiting < 0) {
    218			if (errno == EINTR)
    219				continue;
    220			putstr("# wait4() failed: ");
    221			putnum(errno);
    222			putstr("\n");
    223			return 0;
    224		}
    225		if (waiting != ret) {
    226			putstr("# wait4() returned wrong PID ");
    227			putnum(waiting);
    228			putstr("\n");
    229			return 0;
    230		}
    231
    232		if (!WIFEXITED(status)) {
    233			putstr("# child did not exit\n");
    234			return 0;
    235		}
    236
    237		if (parent != get_tpidr2()) {
    238			putstr("# TPIDR2 corrupted in parent\n");
    239			return 0;
    240		}
    241
    242		return WEXITSTATUS(status);
    243	}
    244}
    245
    246#define run_test(name)			     \
    247	if (name()) {			     \
    248		tests_passed++;		     \
    249	} else {			     \
    250		tests_failed++;		     \
    251		putstr("not ");		     \
    252	}				     \
    253	putstr("ok ");			     \
    254	putnum(++tests_run);		     \
    255	putstr(" " #name "\n");
    256
    257int main(int argc, char **argv)
    258{
    259	int ret, i;
    260
    261	putstr("TAP version 13\n");
    262	putstr("1..");
    263	putnum(EXPECTED_TESTS);
    264	putstr("\n");
    265
    266	putstr("# PID: ");
    267	putnum(getpid());
    268	putstr("\n");
    269
    270	/*
    271	 * This test is run with nolibc which doesn't support hwcap and
    272	 * it's probably disproportionate to implement so instead check
    273	 * for the default vector length configuration in /proc.
    274	 */
    275	ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
    276	if (ret >= 0) {
    277		run_test(default_value);
    278		run_test(write_read);
    279		run_test(write_sleep_read);
    280		run_test(write_fork_read);
    281		run_test(write_clone_read);
    282
    283	} else {
    284		putstr("# SME support not present\n");
    285
    286		for (i = 0; i < EXPECTED_TESTS; i++) {
    287			putstr("ok ");
    288			putnum(i);
    289			putstr(" skipped, TPIDR2 not supported\n");
    290		}
    291
    292		tests_skipped += EXPECTED_TESTS;
    293	}
    294
    295	print_summary();
    296
    297	return 0;
    298}