162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Ptrace test for hw breakpoints
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This test forks and the parent then traces the child doing various
962306a36Sopenharmony_ci * types of ptrace enabled breakpoints
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright (C) 2018 Michael Neuling, IBM Corporation.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <sys/ptrace.h>
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci#include <stddef.h>
1762306a36Sopenharmony_ci#include <sys/user.h>
1862306a36Sopenharmony_ci#include <stdio.h>
1962306a36Sopenharmony_ci#include <stdlib.h>
2062306a36Sopenharmony_ci#include <signal.h>
2162306a36Sopenharmony_ci#include <sys/types.h>
2262306a36Sopenharmony_ci#include <sys/wait.h>
2362306a36Sopenharmony_ci#include <sys/syscall.h>
2462306a36Sopenharmony_ci#include <linux/limits.h>
2562306a36Sopenharmony_ci#include "ptrace.h"
2662306a36Sopenharmony_ci#include "reg.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define SPRN_PVR	0x11F
2962306a36Sopenharmony_ci#define PVR_8xx		0x00500000
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cibool is_8xx;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * Use volatile on all global var so that compiler doesn't
3562306a36Sopenharmony_ci * optimise their load/stores. Otherwise selftest can fail.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic volatile __u64 glvar;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define DAWR_MAX_LEN 512
4062306a36Sopenharmony_cistatic volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define A_LEN 6
4362306a36Sopenharmony_ci#define B_LEN 6
4462306a36Sopenharmony_cistruct gstruct {
4562306a36Sopenharmony_ci	__u8 a[A_LEN]; /* double word aligned */
4662306a36Sopenharmony_ci	__u8 b[B_LEN]; /* double word unaligned */
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_cistatic volatile struct gstruct gstruct __attribute__((aligned(512)));
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
5562306a36Sopenharmony_ci		perror("Can't get breakpoint info");
5662306a36Sopenharmony_ci		exit(-1);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic bool dawr_present(struct ppc_debug_info *dbginfo)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void write_var(int len)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	volatile __u8 *pcvar;
6862306a36Sopenharmony_ci	volatile __u16 *psvar;
6962306a36Sopenharmony_ci	volatile __u32 *pivar;
7062306a36Sopenharmony_ci	volatile __u64 *plvar;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	switch (len) {
7362306a36Sopenharmony_ci	case 1:
7462306a36Sopenharmony_ci		pcvar = (volatile __u8 *)&glvar;
7562306a36Sopenharmony_ci		*pcvar = 0xff;
7662306a36Sopenharmony_ci		break;
7762306a36Sopenharmony_ci	case 2:
7862306a36Sopenharmony_ci		psvar = (volatile __u16 *)&glvar;
7962306a36Sopenharmony_ci		*psvar = 0xffff;
8062306a36Sopenharmony_ci		break;
8162306a36Sopenharmony_ci	case 4:
8262306a36Sopenharmony_ci		pivar = (volatile __u32 *)&glvar;
8362306a36Sopenharmony_ci		*pivar = 0xffffffff;
8462306a36Sopenharmony_ci		break;
8562306a36Sopenharmony_ci	case 8:
8662306a36Sopenharmony_ci		plvar = (volatile __u64 *)&glvar;
8762306a36Sopenharmony_ci		*plvar = 0xffffffffffffffffLL;
8862306a36Sopenharmony_ci		break;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void read_var(int len)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	__u8 cvar __attribute__((unused));
9562306a36Sopenharmony_ci	__u16 svar __attribute__((unused));
9662306a36Sopenharmony_ci	__u32 ivar __attribute__((unused));
9762306a36Sopenharmony_ci	__u64 lvar __attribute__((unused));
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	switch (len) {
10062306a36Sopenharmony_ci	case 1:
10162306a36Sopenharmony_ci		cvar = (volatile __u8)glvar;
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	case 2:
10462306a36Sopenharmony_ci		svar = (volatile __u16)glvar;
10562306a36Sopenharmony_ci		break;
10662306a36Sopenharmony_ci	case 4:
10762306a36Sopenharmony_ci		ivar = (volatile __u32)glvar;
10862306a36Sopenharmony_ci		break;
10962306a36Sopenharmony_ci	case 8:
11062306a36Sopenharmony_ci		lvar = (volatile __u64)glvar;
11162306a36Sopenharmony_ci		break;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void test_workload(void)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	__u8 cvar __attribute__((unused));
11862306a36Sopenharmony_ci	__u32 ivar __attribute__((unused));
11962306a36Sopenharmony_ci	int len = 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
12262306a36Sopenharmony_ci		perror("Child can't be traced?");
12362306a36Sopenharmony_ci		exit(-1);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Wake up father so that it sets up the first test */
12762306a36Sopenharmony_ci	kill(getpid(), SIGUSR1);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, WO test */
13062306a36Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1)
13162306a36Sopenharmony_ci		write_var(len);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RO test */
13462306a36Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1)
13562306a36Sopenharmony_ci		read_var(len);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RW test */
13862306a36Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
13962306a36Sopenharmony_ci		if (rand() % 2)
14062306a36Sopenharmony_ci			read_var(len);
14162306a36Sopenharmony_ci		else
14262306a36Sopenharmony_ci			write_var(len);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
14662306a36Sopenharmony_ci	syscall(__NR_getcwd, &cwd, PATH_MAX);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
14962306a36Sopenharmony_ci	write_var(1);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
15262306a36Sopenharmony_ci	read_var(1);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
15562306a36Sopenharmony_ci	if (rand() % 2)
15662306a36Sopenharmony_ci		write_var(1);
15762306a36Sopenharmony_ci	else
15862306a36Sopenharmony_ci		read_var(1);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
16162306a36Sopenharmony_ci	syscall(__NR_getcwd, &cwd, PATH_MAX);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
16462306a36Sopenharmony_ci	gstruct.a[rand() % A_LEN] = 'a';
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
16762306a36Sopenharmony_ci	cvar = gstruct.a[rand() % A_LEN];
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
17062306a36Sopenharmony_ci	if (rand() % 2)
17162306a36Sopenharmony_ci		gstruct.a[rand() % A_LEN] = 'a';
17262306a36Sopenharmony_ci	else
17362306a36Sopenharmony_ci		cvar = gstruct.a[rand() % A_LEN];
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
17662306a36Sopenharmony_ci	gstruct.b[rand() % B_LEN] = 'b';
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
17962306a36Sopenharmony_ci	cvar = gstruct.b[rand() % B_LEN];
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
18262306a36Sopenharmony_ci	if (rand() % 2)
18362306a36Sopenharmony_ci		gstruct.b[rand() % B_LEN] = 'b';
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		cvar = gstruct.b[rand() % B_LEN];
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
18862306a36Sopenharmony_ci	if (rand() % 2)
18962306a36Sopenharmony_ci		*((int *)(gstruct.a + 4)) = 10;
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		ivar = *((int *)(gstruct.a + 4));
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
19462306a36Sopenharmony_ci	if (rand() % 2)
19562306a36Sopenharmony_ci		big_var[rand() % DAWR_MAX_LEN] = 'a';
19662306a36Sopenharmony_ci	else
19762306a36Sopenharmony_ci		cvar = big_var[rand() % DAWR_MAX_LEN];
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
20062306a36Sopenharmony_ci	gstruct.a[rand() % A_LEN] = 'a';
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
20362306a36Sopenharmony_ci	cvar = gstruct.b[rand() % B_LEN];
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
20662306a36Sopenharmony_ci	gstruct.a[rand() % A_LEN] = 'a';
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
20962306a36Sopenharmony_ci	cvar = gstruct.a[rand() % A_LEN];
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void check_success(pid_t child_pid, const char *name, const char *type,
21362306a36Sopenharmony_ci			  unsigned long saddr, int len)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	int status;
21662306a36Sopenharmony_ci	siginfo_t siginfo;
21762306a36Sopenharmony_ci	unsigned long eaddr = (saddr + len - 1) | 0x7;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	saddr &= ~0x7;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Wait for the child to SIGTRAP */
22262306a36Sopenharmony_ci	wait(&status);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
22762306a36Sopenharmony_ci	    (unsigned long)siginfo.si_addr < saddr ||
22862306a36Sopenharmony_ci	    (unsigned long)siginfo.si_addr > eaddr) {
22962306a36Sopenharmony_ci		printf("%s, %s, len: %d: Fail\n", name, type, len);
23062306a36Sopenharmony_ci		exit(-1);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	printf("%s, %s, len: %d: Ok\n", name, type, len);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!is_8xx) {
23662306a36Sopenharmony_ci		/*
23762306a36Sopenharmony_ci		 * For ptrace registered watchpoint, signal is generated
23862306a36Sopenharmony_ci		 * before executing load/store. Singlestep the instruction
23962306a36Sopenharmony_ci		 * and then continue the test.
24062306a36Sopenharmony_ci		 */
24162306a36Sopenharmony_ci		ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
24262306a36Sopenharmony_ci		wait(NULL);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
24962306a36Sopenharmony_ci		perror("PTRACE_SET_DEBUGREG failed");
25062306a36Sopenharmony_ci		exit(-1);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (wh <= 0) {
25962306a36Sopenharmony_ci		perror("PPC_PTRACE_SETHWDEBUG failed");
26062306a36Sopenharmony_ci		exit(-1);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	return wh;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void ptrace_delhwdebug(pid_t child_pid, int wh)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
26862306a36Sopenharmony_ci		perror("PPC_PTRACE_DELHWDEBUG failed");
26962306a36Sopenharmony_ci		exit(-1);
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci#define DABR_READ_SHIFT		0
27462306a36Sopenharmony_ci#define DABR_WRITE_SHIFT	1
27562306a36Sopenharmony_ci#define DABR_TRANSLATION_SHIFT	2
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int test_set_debugreg(pid_t child_pid)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	unsigned long wp_addr = (unsigned long)&glvar;
28062306a36Sopenharmony_ci	char *name = "PTRACE_SET_DEBUGREG";
28162306a36Sopenharmony_ci	int len;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, WO test*/
28462306a36Sopenharmony_ci	wp_addr &= ~0x7UL;
28562306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_WRITE_SHIFT);
28662306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
28762306a36Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
28862306a36Sopenharmony_ci		ptrace_set_debugreg(child_pid, wp_addr);
28962306a36Sopenharmony_ci		ptrace(PTRACE_CONT, child_pid, NULL, 0);
29062306a36Sopenharmony_ci		check_success(child_pid, name, "WO", wp_addr, len);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RO test */
29462306a36Sopenharmony_ci	wp_addr &= ~0x7UL;
29562306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_READ_SHIFT);
29662306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
29762306a36Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
29862306a36Sopenharmony_ci		ptrace_set_debugreg(child_pid, wp_addr);
29962306a36Sopenharmony_ci		ptrace(PTRACE_CONT, child_pid, NULL, 0);
30062306a36Sopenharmony_ci		check_success(child_pid, name, "RO", wp_addr, len);
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RW test */
30462306a36Sopenharmony_ci	wp_addr &= ~0x7UL;
30562306a36Sopenharmony_ci	wp_addr |= (1Ul << DABR_READ_SHIFT);
30662306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_WRITE_SHIFT);
30762306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
30862306a36Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
30962306a36Sopenharmony_ci		ptrace_set_debugreg(child_pid, wp_addr);
31062306a36Sopenharmony_ci		ptrace(PTRACE_CONT, child_pid, NULL, 0);
31162306a36Sopenharmony_ci		check_success(child_pid, name, "RW", wp_addr, len);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ptrace_set_debugreg(child_pid, 0);
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int test_set_debugreg_kernel_userspace(pid_t child_pid)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	unsigned long wp_addr = (unsigned long)cwd;
32162306a36Sopenharmony_ci	char *name = "PTRACE_SET_DEBUGREG";
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
32462306a36Sopenharmony_ci	wp_addr &= ~0x7UL;
32562306a36Sopenharmony_ci	wp_addr |= (1Ul << DABR_READ_SHIFT);
32662306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_WRITE_SHIFT);
32762306a36Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
32862306a36Sopenharmony_ci	ptrace_set_debugreg(child_pid, wp_addr);
32962306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
33062306a36Sopenharmony_ci	check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	ptrace_set_debugreg(child_pid, 0);
33362306a36Sopenharmony_ci	return 0;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
33762306a36Sopenharmony_ci				  unsigned long addr, int len)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	info->version = 1;
34062306a36Sopenharmony_ci	info->trigger_type = type;
34162306a36Sopenharmony_ci	info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
34262306a36Sopenharmony_ci	info->addr = (__u64)addr;
34362306a36Sopenharmony_ci	info->addr2 = (__u64)addr + len;
34462306a36Sopenharmony_ci	info->condition_value = 0;
34562306a36Sopenharmony_ci	if (!len)
34662306a36Sopenharmony_ci		info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
34762306a36Sopenharmony_ci	else
34862306a36Sopenharmony_ci		info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void test_sethwdebug_exact(pid_t child_pid)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct ppc_hw_breakpoint info;
35462306a36Sopenharmony_ci	unsigned long wp_addr = (unsigned long)&glvar;
35562306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
35662306a36Sopenharmony_ci	int len = 1; /* hardcoded in kernel */
35762306a36Sopenharmony_ci	int wh;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
36062306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
36162306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
36262306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
36362306a36Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr, len);
36462306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
36762306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
36862306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
36962306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
37062306a36Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr, len);
37162306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
37462306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
37562306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
37662306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
37762306a36Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
37862306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void test_sethwdebug_exact_kernel_userspace(pid_t child_pid)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct ppc_hw_breakpoint info;
38462306a36Sopenharmony_ci	unsigned long wp_addr = (unsigned long)&cwd;
38562306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
38662306a36Sopenharmony_ci	int len = 1; /* hardcoded in kernel */
38762306a36Sopenharmony_ci	int wh;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
39062306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
39162306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
39262306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
39362306a36Sopenharmony_ci	check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len);
39462306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void test_sethwdebug_range_aligned(pid_t child_pid)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct ppc_hw_breakpoint info;
40062306a36Sopenharmony_ci	unsigned long wp_addr;
40162306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
40262306a36Sopenharmony_ci	int len;
40362306a36Sopenharmony_ci	int wh;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
40662306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.a;
40762306a36Sopenharmony_ci	len = A_LEN;
40862306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
40962306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
41062306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
41162306a36Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr, len);
41262306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
41562306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.a;
41662306a36Sopenharmony_ci	len = A_LEN;
41762306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
41862306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
41962306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
42062306a36Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr, len);
42162306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
42462306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.a;
42562306a36Sopenharmony_ci	len = A_LEN;
42662306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
42762306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
42862306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
42962306a36Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
43062306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic void test_multi_sethwdebug_range(pid_t child_pid)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct ppc_hw_breakpoint info1, info2;
43662306a36Sopenharmony_ci	unsigned long wp_addr1, wp_addr2;
43762306a36Sopenharmony_ci	char *name1 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED";
43862306a36Sopenharmony_ci	char *name2 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED";
43962306a36Sopenharmony_ci	int len1, len2;
44062306a36Sopenharmony_ci	int wh1, wh2;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	wp_addr1 = (unsigned long)&gstruct.a;
44362306a36Sopenharmony_ci	wp_addr2 = (unsigned long)&gstruct.b;
44462306a36Sopenharmony_ci	len1 = A_LEN;
44562306a36Sopenharmony_ci	len2 = B_LEN;
44662306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
44762306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
45062306a36Sopenharmony_ci	wh1 = ptrace_sethwdebug(child_pid, &info1);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
45362306a36Sopenharmony_ci	wh2 = ptrace_sethwdebug(child_pid, &info2);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
45662306a36Sopenharmony_ci	check_success(child_pid, name1, "WO", wp_addr1, len1);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
45962306a36Sopenharmony_ci	check_success(child_pid, name2, "RO", wp_addr2, len2);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh1);
46262306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh2);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic void test_multi_sethwdebug_range_dawr_overlap(pid_t child_pid)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct ppc_hw_breakpoint info1, info2;
46862306a36Sopenharmony_ci	unsigned long wp_addr1, wp_addr2;
46962306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap";
47062306a36Sopenharmony_ci	int len1, len2;
47162306a36Sopenharmony_ci	int wh1, wh2;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	wp_addr1 = (unsigned long)&gstruct.a;
47462306a36Sopenharmony_ci	wp_addr2 = (unsigned long)&gstruct.a;
47562306a36Sopenharmony_ci	len1 = A_LEN;
47662306a36Sopenharmony_ci	len2 = A_LEN;
47762306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
47862306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
48162306a36Sopenharmony_ci	wh1 = ptrace_sethwdebug(child_pid, &info1);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
48462306a36Sopenharmony_ci	wh2 = ptrace_sethwdebug(child_pid, &info2);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
48762306a36Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr1, len1);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
49062306a36Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr2, len2);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh1);
49362306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh2);
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic void test_sethwdebug_range_unaligned(pid_t child_pid)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct ppc_hw_breakpoint info;
49962306a36Sopenharmony_ci	unsigned long wp_addr;
50062306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
50162306a36Sopenharmony_ci	int len;
50262306a36Sopenharmony_ci	int wh;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
50562306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
50662306a36Sopenharmony_ci	len = B_LEN;
50762306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
50862306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
50962306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
51062306a36Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr, len);
51162306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
51462306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
51562306a36Sopenharmony_ci	len = B_LEN;
51662306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
51762306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
51862306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
51962306a36Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr, len);
52062306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
52362306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
52462306a36Sopenharmony_ci	len = B_LEN;
52562306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
52662306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
52762306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
52862306a36Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
52962306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct ppc_hw_breakpoint info;
53662306a36Sopenharmony_ci	unsigned long wp_addr;
53762306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
53862306a36Sopenharmony_ci	int len;
53962306a36Sopenharmony_ci	int wh;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
54262306a36Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
54362306a36Sopenharmony_ci	len = B_LEN;
54462306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
54562306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
54662306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
54762306a36Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
54862306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic void test_sethwdebug_dawr_max_range(pid_t child_pid)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct ppc_hw_breakpoint info;
55462306a36Sopenharmony_ci	unsigned long wp_addr;
55562306a36Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
55662306a36Sopenharmony_ci	int len;
55762306a36Sopenharmony_ci	int wh;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
56062306a36Sopenharmony_ci	wp_addr = (unsigned long)big_var;
56162306a36Sopenharmony_ci	len = DAWR_MAX_LEN;
56262306a36Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
56362306a36Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
56462306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
56562306a36Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
56662306a36Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/* Set the breakpoints and check the child successfully trigger them */
57062306a36Sopenharmony_cistatic void
57162306a36Sopenharmony_cirun_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	test_set_debugreg(child_pid);
57462306a36Sopenharmony_ci	test_set_debugreg_kernel_userspace(child_pid);
57562306a36Sopenharmony_ci	test_sethwdebug_exact(child_pid);
57662306a36Sopenharmony_ci	test_sethwdebug_exact_kernel_userspace(child_pid);
57762306a36Sopenharmony_ci	if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
57862306a36Sopenharmony_ci		test_sethwdebug_range_aligned(child_pid);
57962306a36Sopenharmony_ci		if (dawr || is_8xx) {
58062306a36Sopenharmony_ci			test_sethwdebug_range_unaligned(child_pid);
58162306a36Sopenharmony_ci			test_sethwdebug_range_unaligned_dar(child_pid);
58262306a36Sopenharmony_ci			test_sethwdebug_dawr_max_range(child_pid);
58362306a36Sopenharmony_ci			if (dbginfo->num_data_bps > 1) {
58462306a36Sopenharmony_ci				test_multi_sethwdebug_range(child_pid);
58562306a36Sopenharmony_ci				test_multi_sethwdebug_range_dawr_overlap(child_pid);
58662306a36Sopenharmony_ci			}
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic int ptrace_hwbreak(void)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	pid_t child_pid;
59462306a36Sopenharmony_ci	struct ppc_debug_info dbginfo;
59562306a36Sopenharmony_ci	bool dawr;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	child_pid = fork();
59862306a36Sopenharmony_ci	if (!child_pid) {
59962306a36Sopenharmony_ci		test_workload();
60062306a36Sopenharmony_ci		return 0;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	wait(NULL);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	get_dbginfo(child_pid, &dbginfo);
60662306a36Sopenharmony_ci	SKIP_IF_MSG(dbginfo.num_data_bps == 0, "No data breakpoints present");
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	dawr = dawr_present(&dbginfo);
60962306a36Sopenharmony_ci	run_tests(child_pid, &dbginfo, dawr);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Let the child exit first. */
61262306a36Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
61362306a36Sopenharmony_ci	wait(NULL);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 * Testcases exits immediately with -1 on any failure. If
61762306a36Sopenharmony_ci	 * it has reached here, it means all tests were successful.
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	return TEST_PASS;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ciint main(int argc, char **argv, char **envp)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	is_8xx = mfspr(SPRN_PVR) == PVR_8xx;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
62762306a36Sopenharmony_ci}
628