18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Ptrace test for hw breakpoints
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This test forks and the parent then traces the child doing various
98c2ecf20Sopenharmony_ci * types of ptrace enabled breakpoints
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (C) 2018 Michael Neuling, IBM Corporation.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <sys/ptrace.h>
158c2ecf20Sopenharmony_ci#include <unistd.h>
168c2ecf20Sopenharmony_ci#include <stddef.h>
178c2ecf20Sopenharmony_ci#include <sys/user.h>
188c2ecf20Sopenharmony_ci#include <stdio.h>
198c2ecf20Sopenharmony_ci#include <stdlib.h>
208c2ecf20Sopenharmony_ci#include <signal.h>
218c2ecf20Sopenharmony_ci#include <sys/types.h>
228c2ecf20Sopenharmony_ci#include <sys/wait.h>
238c2ecf20Sopenharmony_ci#include <sys/syscall.h>
248c2ecf20Sopenharmony_ci#include <linux/limits.h>
258c2ecf20Sopenharmony_ci#include "ptrace.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SPRN_PVR	0x11F
288c2ecf20Sopenharmony_ci#define PVR_8xx		0x00500000
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cibool is_8xx;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Use volatile on all global var so that compiler doesn't
348c2ecf20Sopenharmony_ci * optimise their load/stores. Otherwise selftest can fail.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_cistatic volatile __u64 glvar;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define DAWR_MAX_LEN 512
398c2ecf20Sopenharmony_cistatic volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define A_LEN 6
428c2ecf20Sopenharmony_ci#define B_LEN 6
438c2ecf20Sopenharmony_cistruct gstruct {
448c2ecf20Sopenharmony_ci	__u8 a[A_LEN]; /* double word aligned */
458c2ecf20Sopenharmony_ci	__u8 b[B_LEN]; /* double word unaligned */
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_cistatic volatile struct gstruct gstruct __attribute__((aligned(512)));
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
548c2ecf20Sopenharmony_ci		perror("Can't get breakpoint info");
558c2ecf20Sopenharmony_ci		exit(-1);
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic bool dawr_present(struct ppc_debug_info *dbginfo)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void write_var(int len)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	__u8 *pcvar;
678c2ecf20Sopenharmony_ci	__u16 *psvar;
688c2ecf20Sopenharmony_ci	__u32 *pivar;
698c2ecf20Sopenharmony_ci	__u64 *plvar;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	switch (len) {
728c2ecf20Sopenharmony_ci	case 1:
738c2ecf20Sopenharmony_ci		pcvar = (__u8 *)&glvar;
748c2ecf20Sopenharmony_ci		*pcvar = 0xff;
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	case 2:
778c2ecf20Sopenharmony_ci		psvar = (__u16 *)&glvar;
788c2ecf20Sopenharmony_ci		*psvar = 0xffff;
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci	case 4:
818c2ecf20Sopenharmony_ci		pivar = (__u32 *)&glvar;
828c2ecf20Sopenharmony_ci		*pivar = 0xffffffff;
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	case 8:
858c2ecf20Sopenharmony_ci		plvar = (__u64 *)&glvar;
868c2ecf20Sopenharmony_ci		*plvar = 0xffffffffffffffffLL;
878c2ecf20Sopenharmony_ci		break;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void read_var(int len)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	__u8 cvar __attribute__((unused));
948c2ecf20Sopenharmony_ci	__u16 svar __attribute__((unused));
958c2ecf20Sopenharmony_ci	__u32 ivar __attribute__((unused));
968c2ecf20Sopenharmony_ci	__u64 lvar __attribute__((unused));
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	switch (len) {
998c2ecf20Sopenharmony_ci	case 1:
1008c2ecf20Sopenharmony_ci		cvar = (__u8)glvar;
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci	case 2:
1038c2ecf20Sopenharmony_ci		svar = (__u16)glvar;
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci	case 4:
1068c2ecf20Sopenharmony_ci		ivar = (__u32)glvar;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case 8:
1098c2ecf20Sopenharmony_ci		lvar = (__u64)glvar;
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void test_workload(void)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	__u8 cvar __attribute__((unused));
1178c2ecf20Sopenharmony_ci	__u32 ivar __attribute__((unused));
1188c2ecf20Sopenharmony_ci	int len = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
1218c2ecf20Sopenharmony_ci		perror("Child can't be traced?");
1228c2ecf20Sopenharmony_ci		exit(-1);
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Wake up father so that it sets up the first test */
1268c2ecf20Sopenharmony_ci	kill(getpid(), SIGUSR1);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, WO test */
1298c2ecf20Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1)
1308c2ecf20Sopenharmony_ci		write_var(len);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RO test */
1338c2ecf20Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1)
1348c2ecf20Sopenharmony_ci		read_var(len);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RW test */
1378c2ecf20Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
1388c2ecf20Sopenharmony_ci		if (rand() % 2)
1398c2ecf20Sopenharmony_ci			read_var(len);
1408c2ecf20Sopenharmony_ci		else
1418c2ecf20Sopenharmony_ci			write_var(len);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
1458c2ecf20Sopenharmony_ci	syscall(__NR_getcwd, &cwd, PATH_MAX);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
1488c2ecf20Sopenharmony_ci	write_var(1);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
1518c2ecf20Sopenharmony_ci	read_var(1);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
1548c2ecf20Sopenharmony_ci	if (rand() % 2)
1558c2ecf20Sopenharmony_ci		write_var(1);
1568c2ecf20Sopenharmony_ci	else
1578c2ecf20Sopenharmony_ci		read_var(1);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
1608c2ecf20Sopenharmony_ci	syscall(__NR_getcwd, &cwd, PATH_MAX);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
1638c2ecf20Sopenharmony_ci	gstruct.a[rand() % A_LEN] = 'a';
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
1668c2ecf20Sopenharmony_ci	cvar = gstruct.a[rand() % A_LEN];
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
1698c2ecf20Sopenharmony_ci	if (rand() % 2)
1708c2ecf20Sopenharmony_ci		gstruct.a[rand() % A_LEN] = 'a';
1718c2ecf20Sopenharmony_ci	else
1728c2ecf20Sopenharmony_ci		cvar = gstruct.a[rand() % A_LEN];
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
1758c2ecf20Sopenharmony_ci	gstruct.b[rand() % B_LEN] = 'b';
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
1788c2ecf20Sopenharmony_ci	cvar = gstruct.b[rand() % B_LEN];
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
1818c2ecf20Sopenharmony_ci	if (rand() % 2)
1828c2ecf20Sopenharmony_ci		gstruct.b[rand() % B_LEN] = 'b';
1838c2ecf20Sopenharmony_ci	else
1848c2ecf20Sopenharmony_ci		cvar = gstruct.b[rand() % B_LEN];
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
1878c2ecf20Sopenharmony_ci	if (rand() % 2)
1888c2ecf20Sopenharmony_ci		*((int *)(gstruct.a + 4)) = 10;
1898c2ecf20Sopenharmony_ci	else
1908c2ecf20Sopenharmony_ci		ivar = *((int *)(gstruct.a + 4));
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
1938c2ecf20Sopenharmony_ci	if (rand() % 2)
1948c2ecf20Sopenharmony_ci		big_var[rand() % DAWR_MAX_LEN] = 'a';
1958c2ecf20Sopenharmony_ci	else
1968c2ecf20Sopenharmony_ci		cvar = big_var[rand() % DAWR_MAX_LEN];
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void check_success(pid_t child_pid, const char *name, const char *type,
2008c2ecf20Sopenharmony_ci			  unsigned long saddr, int len)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int status;
2038c2ecf20Sopenharmony_ci	siginfo_t siginfo;
2048c2ecf20Sopenharmony_ci	unsigned long eaddr = (saddr + len - 1) | 0x7;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	saddr &= ~0x7;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Wait for the child to SIGTRAP */
2098c2ecf20Sopenharmony_ci	wait(&status);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
2148c2ecf20Sopenharmony_ci	    (unsigned long)siginfo.si_addr < saddr ||
2158c2ecf20Sopenharmony_ci	    (unsigned long)siginfo.si_addr > eaddr) {
2168c2ecf20Sopenharmony_ci		printf("%s, %s, len: %d: Fail\n", name, type, len);
2178c2ecf20Sopenharmony_ci		exit(-1);
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	printf("%s, %s, len: %d: Ok\n", name, type, len);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (!is_8xx) {
2238c2ecf20Sopenharmony_ci		/*
2248c2ecf20Sopenharmony_ci		 * For ptrace registered watchpoint, signal is generated
2258c2ecf20Sopenharmony_ci		 * before executing load/store. Singlestep the instruction
2268c2ecf20Sopenharmony_ci		 * and then continue the test.
2278c2ecf20Sopenharmony_ci		 */
2288c2ecf20Sopenharmony_ci		ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
2298c2ecf20Sopenharmony_ci		wait(NULL);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
2368c2ecf20Sopenharmony_ci		perror("PTRACE_SET_DEBUGREG failed");
2378c2ecf20Sopenharmony_ci		exit(-1);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (wh <= 0) {
2468c2ecf20Sopenharmony_ci		perror("PPC_PTRACE_SETHWDEBUG failed");
2478c2ecf20Sopenharmony_ci		exit(-1);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return wh;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void ptrace_delhwdebug(pid_t child_pid, int wh)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
2558c2ecf20Sopenharmony_ci		perror("PPC_PTRACE_DELHWDEBUG failed");
2568c2ecf20Sopenharmony_ci		exit(-1);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci#define DABR_READ_SHIFT		0
2618c2ecf20Sopenharmony_ci#define DABR_WRITE_SHIFT	1
2628c2ecf20Sopenharmony_ci#define DABR_TRANSLATION_SHIFT	2
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int test_set_debugreg(pid_t child_pid)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	unsigned long wp_addr = (unsigned long)&glvar;
2678c2ecf20Sopenharmony_ci	char *name = "PTRACE_SET_DEBUGREG";
2688c2ecf20Sopenharmony_ci	int len;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, WO test*/
2718c2ecf20Sopenharmony_ci	wp_addr &= ~0x7UL;
2728c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_WRITE_SHIFT);
2738c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
2748c2ecf20Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
2758c2ecf20Sopenharmony_ci		ptrace_set_debugreg(child_pid, wp_addr);
2768c2ecf20Sopenharmony_ci		ptrace(PTRACE_CONT, child_pid, NULL, 0);
2778c2ecf20Sopenharmony_ci		check_success(child_pid, name, "WO", wp_addr, len);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RO test */
2818c2ecf20Sopenharmony_ci	wp_addr &= ~0x7UL;
2828c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_READ_SHIFT);
2838c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
2848c2ecf20Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
2858c2ecf20Sopenharmony_ci		ptrace_set_debugreg(child_pid, wp_addr);
2868c2ecf20Sopenharmony_ci		ptrace(PTRACE_CONT, child_pid, NULL, 0);
2878c2ecf20Sopenharmony_ci		check_success(child_pid, name, "RO", wp_addr, len);
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, RW test */
2918c2ecf20Sopenharmony_ci	wp_addr &= ~0x7UL;
2928c2ecf20Sopenharmony_ci	wp_addr |= (1Ul << DABR_READ_SHIFT);
2938c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_WRITE_SHIFT);
2948c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
2958c2ecf20Sopenharmony_ci	for (len = 1; len <= sizeof(glvar); len <<= 1) {
2968c2ecf20Sopenharmony_ci		ptrace_set_debugreg(child_pid, wp_addr);
2978c2ecf20Sopenharmony_ci		ptrace(PTRACE_CONT, child_pid, NULL, 0);
2988c2ecf20Sopenharmony_ci		check_success(child_pid, name, "RW", wp_addr, len);
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	ptrace_set_debugreg(child_pid, 0);
3028c2ecf20Sopenharmony_ci	return 0;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int test_set_debugreg_kernel_userspace(pid_t child_pid)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	unsigned long wp_addr = (unsigned long)cwd;
3088c2ecf20Sopenharmony_ci	char *name = "PTRACE_SET_DEBUGREG";
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */
3118c2ecf20Sopenharmony_ci	wp_addr &= ~0x7UL;
3128c2ecf20Sopenharmony_ci	wp_addr |= (1Ul << DABR_READ_SHIFT);
3138c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_WRITE_SHIFT);
3148c2ecf20Sopenharmony_ci	wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
3158c2ecf20Sopenharmony_ci	ptrace_set_debugreg(child_pid, wp_addr);
3168c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
3178c2ecf20Sopenharmony_ci	check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ptrace_set_debugreg(child_pid, 0);
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
3248c2ecf20Sopenharmony_ci				  unsigned long addr, int len)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	info->version = 1;
3278c2ecf20Sopenharmony_ci	info->trigger_type = type;
3288c2ecf20Sopenharmony_ci	info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
3298c2ecf20Sopenharmony_ci	info->addr = (__u64)addr;
3308c2ecf20Sopenharmony_ci	info->addr2 = (__u64)addr + len;
3318c2ecf20Sopenharmony_ci	info->condition_value = 0;
3328c2ecf20Sopenharmony_ci	if (!len)
3338c2ecf20Sopenharmony_ci		info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
3348c2ecf20Sopenharmony_ci	else
3358c2ecf20Sopenharmony_ci		info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic void test_sethwdebug_exact(pid_t child_pid)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct ppc_hw_breakpoint info;
3418c2ecf20Sopenharmony_ci	unsigned long wp_addr = (unsigned long)&glvar;
3428c2ecf20Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
3438c2ecf20Sopenharmony_ci	int len = 1; /* hardcoded in kernel */
3448c2ecf20Sopenharmony_ci	int wh;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
3478c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
3488c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
3498c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
3508c2ecf20Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr, len);
3518c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
3548c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
3558c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
3568c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
3578c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr, len);
3588c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
3618c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
3628c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
3638c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
3648c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
3658c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void test_sethwdebug_exact_kernel_userspace(pid_t child_pid)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct ppc_hw_breakpoint info;
3718c2ecf20Sopenharmony_ci	unsigned long wp_addr = (unsigned long)&cwd;
3728c2ecf20Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
3738c2ecf20Sopenharmony_ci	int len = 1; /* hardcoded in kernel */
3748c2ecf20Sopenharmony_ci	int wh;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */
3778c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
3788c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
3798c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
3808c2ecf20Sopenharmony_ci	check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len);
3818c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic void test_sethwdebug_range_aligned(pid_t child_pid)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct ppc_hw_breakpoint info;
3878c2ecf20Sopenharmony_ci	unsigned long wp_addr;
3888c2ecf20Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
3898c2ecf20Sopenharmony_ci	int len;
3908c2ecf20Sopenharmony_ci	int wh;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
3938c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.a;
3948c2ecf20Sopenharmony_ci	len = A_LEN;
3958c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
3968c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
3978c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
3988c2ecf20Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr, len);
3998c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
4028c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.a;
4038c2ecf20Sopenharmony_ci	len = A_LEN;
4048c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
4058c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4068c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4078c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr, len);
4088c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
4118c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.a;
4128c2ecf20Sopenharmony_ci	len = A_LEN;
4138c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
4148c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4158c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4168c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
4178c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic void test_sethwdebug_range_unaligned(pid_t child_pid)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct ppc_hw_breakpoint info;
4238c2ecf20Sopenharmony_ci	unsigned long wp_addr;
4248c2ecf20Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
4258c2ecf20Sopenharmony_ci	int len;
4268c2ecf20Sopenharmony_ci	int wh;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
4298c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
4308c2ecf20Sopenharmony_ci	len = B_LEN;
4318c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
4328c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4338c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4348c2ecf20Sopenharmony_ci	check_success(child_pid, name, "WO", wp_addr, len);
4358c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
4388c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
4398c2ecf20Sopenharmony_ci	len = B_LEN;
4408c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
4418c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4428c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4438c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RO", wp_addr, len);
4448c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
4478c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
4488c2ecf20Sopenharmony_ci	len = B_LEN;
4498c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
4508c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4518c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4528c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
4538c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct ppc_hw_breakpoint info;
4608c2ecf20Sopenharmony_ci	unsigned long wp_addr;
4618c2ecf20Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
4628c2ecf20Sopenharmony_ci	int len;
4638c2ecf20Sopenharmony_ci	int wh;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
4668c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)&gstruct.b;
4678c2ecf20Sopenharmony_ci	len = B_LEN;
4688c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
4698c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4708c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4718c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
4728c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic void test_sethwdebug_dawr_max_range(pid_t child_pid)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct ppc_hw_breakpoint info;
4788c2ecf20Sopenharmony_ci	unsigned long wp_addr;
4798c2ecf20Sopenharmony_ci	char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
4808c2ecf20Sopenharmony_ci	int len;
4818c2ecf20Sopenharmony_ci	int wh;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
4848c2ecf20Sopenharmony_ci	wp_addr = (unsigned long)big_var;
4858c2ecf20Sopenharmony_ci	len = DAWR_MAX_LEN;
4868c2ecf20Sopenharmony_ci	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
4878c2ecf20Sopenharmony_ci	wh = ptrace_sethwdebug(child_pid, &info);
4888c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
4898c2ecf20Sopenharmony_ci	check_success(child_pid, name, "RW", wp_addr, len);
4908c2ecf20Sopenharmony_ci	ptrace_delhwdebug(child_pid, wh);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/* Set the breakpoints and check the child successfully trigger them */
4948c2ecf20Sopenharmony_cistatic void
4958c2ecf20Sopenharmony_cirun_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	test_set_debugreg(child_pid);
4988c2ecf20Sopenharmony_ci	test_set_debugreg_kernel_userspace(child_pid);
4998c2ecf20Sopenharmony_ci	test_sethwdebug_exact(child_pid);
5008c2ecf20Sopenharmony_ci	test_sethwdebug_exact_kernel_userspace(child_pid);
5018c2ecf20Sopenharmony_ci	if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
5028c2ecf20Sopenharmony_ci		test_sethwdebug_range_aligned(child_pid);
5038c2ecf20Sopenharmony_ci		if (dawr || is_8xx) {
5048c2ecf20Sopenharmony_ci			test_sethwdebug_range_unaligned(child_pid);
5058c2ecf20Sopenharmony_ci			test_sethwdebug_range_unaligned_dar(child_pid);
5068c2ecf20Sopenharmony_ci			test_sethwdebug_dawr_max_range(child_pid);
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int ptrace_hwbreak(void)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	pid_t child_pid;
5148c2ecf20Sopenharmony_ci	struct ppc_debug_info dbginfo;
5158c2ecf20Sopenharmony_ci	bool dawr;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	child_pid = fork();
5188c2ecf20Sopenharmony_ci	if (!child_pid) {
5198c2ecf20Sopenharmony_ci		test_workload();
5208c2ecf20Sopenharmony_ci		return 0;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	wait(NULL);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	get_dbginfo(child_pid, &dbginfo);
5268c2ecf20Sopenharmony_ci	SKIP_IF(dbginfo.num_data_bps == 0);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	dawr = dawr_present(&dbginfo);
5298c2ecf20Sopenharmony_ci	run_tests(child_pid, &dbginfo, dawr);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* Let the child exit first. */
5328c2ecf20Sopenharmony_ci	ptrace(PTRACE_CONT, child_pid, NULL, 0);
5338c2ecf20Sopenharmony_ci	wait(NULL);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/*
5368c2ecf20Sopenharmony_ci	 * Testcases exits immediately with -1 on any failure. If
5378c2ecf20Sopenharmony_ci	 * it has reached here, it means all tests were successful.
5388c2ecf20Sopenharmony_ci	 */
5398c2ecf20Sopenharmony_ci	return TEST_PASS;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ciint main(int argc, char **argv, char **envp)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	int pvr = 0;
5458c2ecf20Sopenharmony_ci	asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
5468c2ecf20Sopenharmony_ci	if (pvr == PVR_8xx)
5478c2ecf20Sopenharmony_ci		is_8xx = true;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
5508c2ecf20Sopenharmony_ci}
551