162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <asm/unistd.h>
462306a36Sopenharmony_ci#include <linux/hw_breakpoint.h>
562306a36Sopenharmony_ci#include <linux/ptrace.h>
662306a36Sopenharmony_ci#include <memory.h>
762306a36Sopenharmony_ci#include <stdlib.h>
862306a36Sopenharmony_ci#include <sys/wait.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "utils.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * Child subroutine that performs a load on the address, then traps
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_civoid same_watch_addr_child(unsigned long *addr);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Address of the ld instruction in same_watch_addr_child() */
1862306a36Sopenharmony_ciextern char same_watch_addr_load[];
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Address of the end trap instruction in same_watch_addr_child() */
2162306a36Sopenharmony_ciextern char same_watch_addr_trap[];
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * Child subroutine that performs a load on the first address, then a load on
2562306a36Sopenharmony_ci * the second address (with no instructions separating this from the first
2662306a36Sopenharmony_ci * load), then traps.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_civoid perf_then_ptrace_child(unsigned long *first_addr, unsigned long *second_addr);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Address of the first ld instruction in perf_then_ptrace_child() */
3162306a36Sopenharmony_ciextern char perf_then_ptrace_load1[];
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Address of the second ld instruction in perf_then_ptrace_child() */
3462306a36Sopenharmony_ciextern char perf_then_ptrace_load2[];
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Address of the end trap instruction in perf_then_ptrace_child() */
3762306a36Sopenharmony_ciextern char perf_then_ptrace_trap[];
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic inline long sys_ptrace(long request, pid_t pid, unsigned long addr, unsigned long data)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	return syscall(__NR_ptrace, request, pid, addr, data);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic long ptrace_traceme(void)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return sys_ptrace(PTRACE_TRACEME, 0, 0, 0);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic long ptrace_getregs(pid_t pid, struct pt_regs *result)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	return sys_ptrace(PTRACE_GETREGS, pid, 0, (unsigned long)result);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic long ptrace_setregs(pid_t pid, struct pt_regs *result)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return sys_ptrace(PTRACE_SETREGS, pid, 0, (unsigned long)result);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic long ptrace_cont(pid_t pid, long signal)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return sys_ptrace(PTRACE_CONT, pid, 0, signal);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic long ptrace_singlestep(pid_t pid, long signal)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return sys_ptrace(PTRACE_SINGLESTEP, pid, 0, signal);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic long ppc_ptrace_gethwdbginfo(pid_t pid, struct ppc_debug_info *dbginfo)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return sys_ptrace(PPC_PTRACE_GETHWDBGINFO, pid, 0, (unsigned long)dbginfo);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic long ppc_ptrace_sethwdbg(pid_t pid, struct ppc_hw_breakpoint *bp_info)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return sys_ptrace(PPC_PTRACE_SETHWDEBUG, pid, 0, (unsigned long)bp_info);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic long ppc_ptrace_delhwdbg(pid_t pid, int bp_id)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	return sys_ptrace(PPC_PTRACE_DELHWDEBUG, pid, 0L, bp_id);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic long ptrace_getreg_pc(pid_t pid, void **pc)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct pt_regs regs;
8762306a36Sopenharmony_ci	long err;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	err = ptrace_getregs(pid, &regs);
9062306a36Sopenharmony_ci	if (err)
9162306a36Sopenharmony_ci		return err;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	*pc = (void *)regs.nip;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic long ptrace_setreg_pc(pid_t pid, void *pc)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct pt_regs regs;
10162306a36Sopenharmony_ci	long err;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	err = ptrace_getregs(pid, &regs);
10462306a36Sopenharmony_ci	if (err)
10562306a36Sopenharmony_ci		return err;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	regs.nip = (unsigned long)pc;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	err = ptrace_setregs(pid, &regs);
11062306a36Sopenharmony_ci	if (err)
11162306a36Sopenharmony_ci		return err;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
11762306a36Sopenharmony_ci			   int group_fd, unsigned long flags)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void perf_user_event_attr_set(struct perf_event_attr *attr, void *addr, u64 len)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	memset(attr, 0, sizeof(struct perf_event_attr));
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	attr->type		= PERF_TYPE_BREAKPOINT;
12762306a36Sopenharmony_ci	attr->size		= sizeof(struct perf_event_attr);
12862306a36Sopenharmony_ci	attr->bp_type		= HW_BREAKPOINT_R;
12962306a36Sopenharmony_ci	attr->bp_addr		= (u64)addr;
13062306a36Sopenharmony_ci	attr->bp_len		= len;
13162306a36Sopenharmony_ci	attr->exclude_kernel	= 1;
13262306a36Sopenharmony_ci	attr->exclude_hv	= 1;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int perf_watchpoint_open(pid_t child_pid, void *addr, u64 len)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct perf_event_attr attr;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	perf_user_event_attr_set(&attr, addr, len);
14062306a36Sopenharmony_ci	return perf_event_open(&attr, child_pid, -1, -1, 0);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int perf_read_counter(int perf_fd, u64 *count)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	/*
14662306a36Sopenharmony_ci	 * A perf counter is retrieved by the read() syscall. It contains
14762306a36Sopenharmony_ci	 * the current count as 8 bytes that are interpreted as a u64
14862306a36Sopenharmony_ci	 */
14962306a36Sopenharmony_ci	ssize_t len = read(perf_fd, count, sizeof(*count));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (len != sizeof(*count))
15262306a36Sopenharmony_ci		return -1;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void ppc_ptrace_init_breakpoint(struct ppc_hw_breakpoint *info,
15862306a36Sopenharmony_ci				       int type, void *addr, int len)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	info->version = 1;
16162306a36Sopenharmony_ci	info->trigger_type = type;
16262306a36Sopenharmony_ci	info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
16362306a36Sopenharmony_ci	info->addr = (u64)addr;
16462306a36Sopenharmony_ci	info->addr2 = (u64)addr + len;
16562306a36Sopenharmony_ci	info->condition_value = 0;
16662306a36Sopenharmony_ci	if (!len)
16762306a36Sopenharmony_ci		info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
16862306a36Sopenharmony_ci	else
16962306a36Sopenharmony_ci		info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/*
17362306a36Sopenharmony_ci * Checks if we can place at least 2 watchpoints on the child process
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_cistatic int check_watchpoints(pid_t pid)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct ppc_debug_info dbginfo;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	FAIL_IF_MSG(ppc_ptrace_gethwdbginfo(pid, &dbginfo), "PPC_PTRACE_GETHWDBGINFO failed");
18062306a36Sopenharmony_ci	SKIP_IF_MSG(dbginfo.num_data_bps <= 1, "Not enough data watchpoints (need at least 2)");
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Wrapper around a plain fork() call that sets up the child for
18762306a36Sopenharmony_ci * ptrace-ing. Both the parent and child return from this, though
18862306a36Sopenharmony_ci * the child is stopped until ptrace_cont(pid) is run by the parent.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_cistatic int ptrace_fork_child(pid_t *pid)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	int status;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	*pid = fork();
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (*pid < 0)
19762306a36Sopenharmony_ci		FAIL_IF_MSG(1, "Failed to fork child");
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!*pid) {
20062306a36Sopenharmony_ci		FAIL_IF_EXIT_MSG(ptrace_traceme(), "PTRACE_TRACEME failed");
20162306a36Sopenharmony_ci		FAIL_IF_EXIT_MSG(raise(SIGSTOP), "Child failed to raise SIGSTOP");
20262306a36Sopenharmony_ci	} else {
20362306a36Sopenharmony_ci		/* Synchronise on child SIGSTOP */
20462306a36Sopenharmony_ci		FAIL_IF_MSG(waitpid(*pid, &status, 0) == -1, "Failed to wait for child");
20562306a36Sopenharmony_ci		FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/*
21262306a36Sopenharmony_ci * Tests the interaction between ptrace and perf watching the same data.
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * We expect ptrace to take 'priority', as it is has before-execute
21562306a36Sopenharmony_ci * semantics.
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * The perf counter should not be incremented yet because perf has after-execute
21862306a36Sopenharmony_ci * semantics. E.g., if ptrace changes the child PC, we don't even execute the
21962306a36Sopenharmony_ci * instruction at all.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * When the child is stopped for ptrace, we test both continue and single step.
22262306a36Sopenharmony_ci * Both should increment the perf counter. We also test changing the PC somewhere
22362306a36Sopenharmony_ci * different and stepping, which should not increment the perf counter.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_ciint same_watch_addr_test(void)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct ppc_hw_breakpoint bp_info;	/* ptrace breakpoint info */
22862306a36Sopenharmony_ci	int bp_id;	/* Breakpoint handle of ptrace watchpoint */
22962306a36Sopenharmony_ci	int perf_fd;	/* File descriptor of perf performance counter */
23062306a36Sopenharmony_ci	u64 perf_count;	/* Most recently fetched perf performance counter value */
23162306a36Sopenharmony_ci	pid_t pid;	/* PID of child process */
23262306a36Sopenharmony_ci	void *pc;	/* Most recently fetched child PC value */
23362306a36Sopenharmony_ci	int status;	/* Stop status of child after waitpid */
23462306a36Sopenharmony_ci	unsigned long value;	/* Dummy value to be read/written to by child */
23562306a36Sopenharmony_ci	int err;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	err = ptrace_fork_child(&pid);
23862306a36Sopenharmony_ci	if (err)
23962306a36Sopenharmony_ci		return err;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (!pid) {
24262306a36Sopenharmony_ci		same_watch_addr_child(&value);
24362306a36Sopenharmony_ci		exit(1);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	err = check_watchpoints(pid);
24762306a36Sopenharmony_ci	if (err)
24862306a36Sopenharmony_ci		return err;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Place a perf watchpoint counter on value */
25162306a36Sopenharmony_ci	perf_fd = perf_watchpoint_open(pid, &value, sizeof(value));
25262306a36Sopenharmony_ci	FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter");
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Place a ptrace watchpoint on value */
25562306a36Sopenharmony_ci	ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, &value, sizeof(value));
25662306a36Sopenharmony_ci	bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
25762306a36Sopenharmony_ci	FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Let the child run. It should stop on the ptrace watchpoint */
26062306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
26362306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
26462306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
26562306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction");
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/*
26862306a36Sopenharmony_ci	 * We stopped before executing the load, so perf should not have
26962306a36Sopenharmony_ci	 * recorded any events yet
27062306a36Sopenharmony_ci	 */
27162306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
27262306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 0, "perf recorded unexpected event");
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Single stepping over the load should increment the perf counter */
27562306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_singlestep(pid, 0), "Failed to single step child");
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
27862306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
27962306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
28062306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_load + 4, "Failed to single step load instruction");
28162306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
28262306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 1, "perf counter did not increment");
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/*
28562306a36Sopenharmony_ci	 * Set up a ptrace watchpoint on the value again and trigger it.
28662306a36Sopenharmony_ci	 * The perf counter should not have incremented because we do not
28762306a36Sopenharmony_ci	 * execute the load yet.
28862306a36Sopenharmony_ci	 */
28962306a36Sopenharmony_ci	FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint");
29062306a36Sopenharmony_ci	bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
29162306a36Sopenharmony_ci	FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
29262306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
29362306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
29662306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
29762306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
29862306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load trap");
29962306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
30062306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 1, "perf counter should not have changed");
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Continuing over the load should increment the perf counter */
30362306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
30662306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
30762306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
30862306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
30962306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
31062306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 2, "perf counter did not increment");
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/*
31362306a36Sopenharmony_ci	 * If we set the child PC back to the load instruction, then continue,
31462306a36Sopenharmony_ci	 * we should reach the end trap (because ptrace is one-shot) and have
31562306a36Sopenharmony_ci	 * another perf event.
31662306a36Sopenharmony_ci	 */
31762306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
31862306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
32162306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
32262306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
32362306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
32462306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
32562306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 3, "perf counter did not increment");
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/*
32862306a36Sopenharmony_ci	 * If we set the child PC back to the load instruction, set a ptrace
32962306a36Sopenharmony_ci	 * watchpoint on the load, then continue, we should immediately get
33062306a36Sopenharmony_ci	 * the ptrace trap without incrementing the perf counter
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint");
33362306a36Sopenharmony_ci	bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
33462306a36Sopenharmony_ci	FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
33562306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
33662306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
33962306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
34062306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
34162306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction");
34262306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
34362306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed");
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/*
34662306a36Sopenharmony_ci	 * If we change the PC while stopped on the load instruction, we should
34762306a36Sopenharmony_ci	 * not increment the perf counter (because ptrace is before-execute,
34862306a36Sopenharmony_ci	 * perf is after-execute).
34962306a36Sopenharmony_ci	 */
35062306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load + 4), "Failed to set child PC");
35162306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
35462306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
35562306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
35662306a36Sopenharmony_ci	FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
35762306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
35862306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed");
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Clean up child */
36162306a36Sopenharmony_ci	FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child");
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/*
36762306a36Sopenharmony_ci * Tests the interaction between ptrace and perf when:
36862306a36Sopenharmony_ci * 1. perf watches a value
36962306a36Sopenharmony_ci * 2. ptrace watches a different value
37062306a36Sopenharmony_ci * 3. The perf value is read, then the ptrace value is read immediately after
37162306a36Sopenharmony_ci *
37262306a36Sopenharmony_ci * A breakpoint implementation may accidentally misattribute/skip one of
37362306a36Sopenharmony_ci * the ptrace or perf handlers, as interrupt based work is done after perf
37462306a36Sopenharmony_ci * and before ptrace.
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * We expect the perf counter to increment before the ptrace watchpoint
37762306a36Sopenharmony_ci * triggers.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_ciint perf_then_ptrace_test(void)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct ppc_hw_breakpoint bp_info;	/* ptrace breakpoint info */
38262306a36Sopenharmony_ci	int bp_id;	/* Breakpoint handle of ptrace watchpoint */
38362306a36Sopenharmony_ci	int perf_fd;	/* File descriptor of perf performance counter */
38462306a36Sopenharmony_ci	u64 perf_count;	/* Most recently fetched perf performance counter value */
38562306a36Sopenharmony_ci	pid_t pid;	/* PID of child process */
38662306a36Sopenharmony_ci	void *pc;	/* Most recently fetched child PC value */
38762306a36Sopenharmony_ci	int status;	/* Stop status of child after waitpid */
38862306a36Sopenharmony_ci	unsigned long perf_value;	/* Dummy value to be watched by perf */
38962306a36Sopenharmony_ci	unsigned long ptrace_value;	/* Dummy value to be watched by ptrace */
39062306a36Sopenharmony_ci	int err;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	err = ptrace_fork_child(&pid);
39362306a36Sopenharmony_ci	if (err)
39462306a36Sopenharmony_ci		return err;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/*
39762306a36Sopenharmony_ci	 * If we are the child, run a subroutine that reads the perf value,
39862306a36Sopenharmony_ci	 * then reads the ptrace value with consecutive load instructions
39962306a36Sopenharmony_ci	 */
40062306a36Sopenharmony_ci	if (!pid) {
40162306a36Sopenharmony_ci		perf_then_ptrace_child(&perf_value, &ptrace_value);
40262306a36Sopenharmony_ci		exit(0);
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	err = check_watchpoints(pid);
40662306a36Sopenharmony_ci	if (err)
40762306a36Sopenharmony_ci		return err;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Place a perf watchpoint counter */
41062306a36Sopenharmony_ci	perf_fd = perf_watchpoint_open(pid, &perf_value, sizeof(perf_value));
41162306a36Sopenharmony_ci	FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter");
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Place a ptrace watchpoint */
41462306a36Sopenharmony_ci	ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ,
41562306a36Sopenharmony_ci				   &ptrace_value, sizeof(ptrace_value));
41662306a36Sopenharmony_ci	bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
41762306a36Sopenharmony_ci	FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Let the child run. It should stop on the ptrace watchpoint */
42062306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
42362306a36Sopenharmony_ci	FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
42462306a36Sopenharmony_ci	FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
42562306a36Sopenharmony_ci	FAIL_IF_MSG(pc != perf_then_ptrace_load2, "Child did not stop on ptrace load");
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* perf should have recorded the first load */
42862306a36Sopenharmony_ci	FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
42962306a36Sopenharmony_ci	FAIL_IF_MSG(perf_count != 1, "perf counter did not increment");
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Clean up child */
43262306a36Sopenharmony_ci	FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child");
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ciint main(int argc, char *argv[])
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int err = 0;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	err |= test_harness(same_watch_addr_test, "same_watch_addr");
44262306a36Sopenharmony_ci	err |= test_harness(perf_then_ptrace_test, "perf_then_ptrace");
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return err;
44562306a36Sopenharmony_ci}
446