162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ptrace test for GPR/FPR registers
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include "ptrace.h"
862306a36Sopenharmony_ci#include "ptrace-gpr.h"
962306a36Sopenharmony_ci#include "reg.h"
1062306a36Sopenharmony_ci#include <time.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* Tracer and Tracee Shared Data */
1362306a36Sopenharmony_ciint shm_id;
1462306a36Sopenharmony_ciint *cptr, *pptr;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciextern void gpr_child_loop(int *read_flag, int *write_flag,
1762306a36Sopenharmony_ci			   unsigned long *gpr_buf, double *fpr_buf);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciunsigned long child_gpr_val, parent_gpr_val;
2062306a36Sopenharmony_cidouble child_fpr_val, parent_fpr_val;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int child(void)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	unsigned long gpr_buf[32];
2562306a36Sopenharmony_ci	double fpr_buf[32];
2662306a36Sopenharmony_ci	int i;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	cptr = (int *)shmat(shm_id, NULL, 0);
2962306a36Sopenharmony_ci	memset(gpr_buf, 0, sizeof(gpr_buf));
3062306a36Sopenharmony_ci	memset(fpr_buf, 0, sizeof(fpr_buf));
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
3362306a36Sopenharmony_ci		gpr_buf[i] = child_gpr_val;
3462306a36Sopenharmony_ci		fpr_buf[i] = child_fpr_val;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	gpr_child_loop(&cptr[0], &cptr[1], gpr_buf, fpr_buf);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	shmdt((void *)cptr);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	FAIL_IF(validate_gpr(gpr_buf, parent_gpr_val));
4262306a36Sopenharmony_ci	FAIL_IF(validate_fpr_double(fpr_buf, parent_fpr_val));
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint trace_gpr(pid_t child)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	__u64 tmp, fpr[32], *peeked_fprs;
5062306a36Sopenharmony_ci	unsigned long gpr[18];
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	FAIL_IF(start_trace(child));
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	// Check child GPRs match what we expect using GETREGS
5562306a36Sopenharmony_ci	FAIL_IF(show_gpr(child, gpr));
5662306a36Sopenharmony_ci	FAIL_IF(validate_gpr(gpr, child_gpr_val));
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	// Check child FPRs match what we expect using GETFPREGS
5962306a36Sopenharmony_ci	FAIL_IF(show_fpr(child, fpr));
6062306a36Sopenharmony_ci	memcpy(&tmp, &child_fpr_val, sizeof(tmp));
6162306a36Sopenharmony_ci	FAIL_IF(validate_fpr(fpr, tmp));
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	// Check child FPRs match what we expect using PEEKUSR
6462306a36Sopenharmony_ci	peeked_fprs = peek_fprs(child);
6562306a36Sopenharmony_ci	FAIL_IF(!peeked_fprs);
6662306a36Sopenharmony_ci	FAIL_IF(validate_fpr(peeked_fprs, tmp));
6762306a36Sopenharmony_ci	free(peeked_fprs);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	// Write child GPRs using SETREGS
7062306a36Sopenharmony_ci	FAIL_IF(write_gpr(child, parent_gpr_val));
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	// Write child FPRs using SETFPREGS
7362306a36Sopenharmony_ci	memcpy(&tmp, &parent_fpr_val, sizeof(tmp));
7462306a36Sopenharmony_ci	FAIL_IF(write_fpr(child, tmp));
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	// Check child FPRs match what we just set, using PEEKUSR
7762306a36Sopenharmony_ci	peeked_fprs = peek_fprs(child);
7862306a36Sopenharmony_ci	FAIL_IF(!peeked_fprs);
7962306a36Sopenharmony_ci	FAIL_IF(validate_fpr(peeked_fprs, tmp));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	// Write child FPRs using POKEUSR
8262306a36Sopenharmony_ci	FAIL_IF(poke_fprs(child, (unsigned long *)peeked_fprs));
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	// Child will check its FPRs match before exiting
8562306a36Sopenharmony_ci	FAIL_IF(stop_trace(child));
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return TEST_PASS;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#ifndef __LONG_WIDTH__
9162306a36Sopenharmony_ci#define __LONG_WIDTH__ (sizeof(long) * 8)
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic uint64_t rand_reg(void)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	uint64_t result;
9762306a36Sopenharmony_ci	long r;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	r = random();
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	// Small values are typical
10262306a36Sopenharmony_ci	result = r & 0xffff;
10362306a36Sopenharmony_ci	if (r & 0x10000)
10462306a36Sopenharmony_ci		return result;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	// Pointers tend to have high bits set
10762306a36Sopenharmony_ci	result |= random() << (__LONG_WIDTH__ - 31);
10862306a36Sopenharmony_ci	if (r & 0x100000)
10962306a36Sopenharmony_ci		return result;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	// And sometimes we want a full 64-bit value
11262306a36Sopenharmony_ci	result ^= random() << 16;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return result;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciint ptrace_gpr(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	unsigned long seed;
12062306a36Sopenharmony_ci	int ret, status;
12162306a36Sopenharmony_ci	pid_t pid;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	seed = getpid() ^ time(NULL);
12462306a36Sopenharmony_ci	printf("srand(%lu)\n", seed);
12562306a36Sopenharmony_ci	srand(seed);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	child_gpr_val = rand_reg();
12862306a36Sopenharmony_ci	child_fpr_val = rand_reg();
12962306a36Sopenharmony_ci	parent_gpr_val = rand_reg();
13062306a36Sopenharmony_ci	parent_fpr_val = rand_reg();
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT);
13362306a36Sopenharmony_ci	pid = fork();
13462306a36Sopenharmony_ci	if (pid < 0) {
13562306a36Sopenharmony_ci		perror("fork() failed");
13662306a36Sopenharmony_ci		return TEST_FAIL;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	if (pid == 0)
13962306a36Sopenharmony_ci		exit(child());
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (pid) {
14262306a36Sopenharmony_ci		pptr = (int *)shmat(shm_id, NULL, 0);
14362306a36Sopenharmony_ci		while (!pptr[1])
14462306a36Sopenharmony_ci			asm volatile("" : : : "memory");
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		ret = trace_gpr(pid);
14762306a36Sopenharmony_ci		if (ret) {
14862306a36Sopenharmony_ci			kill(pid, SIGTERM);
14962306a36Sopenharmony_ci			shmdt((void *)pptr);
15062306a36Sopenharmony_ci			shmctl(shm_id, IPC_RMID, NULL);
15162306a36Sopenharmony_ci			return TEST_FAIL;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		pptr[0] = 1;
15562306a36Sopenharmony_ci		shmdt((void *)pptr);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		ret = wait(&status);
15862306a36Sopenharmony_ci		shmctl(shm_id, IPC_RMID, NULL);
15962306a36Sopenharmony_ci		if (ret != pid) {
16062306a36Sopenharmony_ci			printf("Child's exit status not captured\n");
16162306a36Sopenharmony_ci			return TEST_FAIL;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL :
16562306a36Sopenharmony_ci			TEST_PASS;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return TEST_PASS;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciint main(int argc, char *argv[])
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	return test_harness(ptrace_gpr, "ptrace_gpr");
17462306a36Sopenharmony_ci}
175