18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/compiler.h> 38c2ecf20Sopenharmony_ci#include <sys/types.h> 48c2ecf20Sopenharmony_ci#include <sys/wait.h> 58c2ecf20Sopenharmony_ci#include <sys/user.h> 68c2ecf20Sopenharmony_ci#include <syscall.h> 78c2ecf20Sopenharmony_ci#include <unistd.h> 88c2ecf20Sopenharmony_ci#include <stdio.h> 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <string.h> 118c2ecf20Sopenharmony_ci#include <sys/ptrace.h> 128c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 138c2ecf20Sopenharmony_ci#include <errno.h> 148c2ecf20Sopenharmony_ci#include "debug.h" 158c2ecf20Sopenharmony_ci#include "tests/tests.h" 168c2ecf20Sopenharmony_ci#include "arch-tests.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic noinline int bp_1(void) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci pr_debug("in %s\n", __func__); 218c2ecf20Sopenharmony_ci return 0; 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic noinline int bp_2(void) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci pr_debug("in %s\n", __func__); 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int spawn_child(void) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int child = fork(); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (child == 0) { 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * The child sets itself for as tracee and 378c2ecf20Sopenharmony_ci * waits in signal for parent to trace it, 388c2ecf20Sopenharmony_ci * then it calls bp_1 and quits. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (err) { 438c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_TRACEME\n"); 448c2ecf20Sopenharmony_ci exit(1); 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci raise(SIGCONT); 488c2ecf20Sopenharmony_ci bp_1(); 498c2ecf20Sopenharmony_ci exit(0); 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return child; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * This tests creates HW breakpoint, tries to 578c2ecf20Sopenharmony_ci * change it and checks it was properly changed. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic int bp_modify1(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci pid_t child; 628c2ecf20Sopenharmony_ci int status; 638c2ecf20Sopenharmony_ci unsigned long rip = 0, dr7 = 1; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci child = spawn_child(); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci waitpid(child, &status, 0); 688c2ecf20Sopenharmony_ci if (WIFEXITED(status)) { 698c2ecf20Sopenharmony_ci pr_debug("tracee exited prematurely 1\n"); 708c2ecf20Sopenharmony_ci return TEST_FAIL; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * The parent does following steps: 758c2ecf20Sopenharmony_ci * - creates a new breakpoint (id 0) for bp_2 function 768c2ecf20Sopenharmony_ci * - changes that breakponit to bp_1 function 778c2ecf20Sopenharmony_ci * - waits for the breakpoint to hit and checks 788c2ecf20Sopenharmony_ci * it has proper rip of bp_1 function 798c2ecf20Sopenharmony_ci * - detaches the child 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 828c2ecf20Sopenharmony_ci offsetof(struct user, u_debugreg[0]), bp_2)) { 838c2ecf20Sopenharmony_ci pr_debug("failed to set breakpoint, 1st time: %s\n", 848c2ecf20Sopenharmony_ci strerror(errno)); 858c2ecf20Sopenharmony_ci goto out; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 898c2ecf20Sopenharmony_ci offsetof(struct user, u_debugreg[0]), bp_1)) { 908c2ecf20Sopenharmony_ci pr_debug("failed to set breakpoint, 2nd time: %s\n", 918c2ecf20Sopenharmony_ci strerror(errno)); 928c2ecf20Sopenharmony_ci goto out; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 968c2ecf20Sopenharmony_ci offsetof(struct user, u_debugreg[7]), dr7)) { 978c2ecf20Sopenharmony_ci pr_debug("failed to set dr7: %s\n", strerror(errno)); 988c2ecf20Sopenharmony_ci goto out; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 1028c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); 1038c2ecf20Sopenharmony_ci goto out; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci waitpid(child, &status, 0); 1078c2ecf20Sopenharmony_ci if (WIFEXITED(status)) { 1088c2ecf20Sopenharmony_ci pr_debug("tracee exited prematurely 2\n"); 1098c2ecf20Sopenharmony_ci return TEST_FAIL; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci rip = ptrace(PTRACE_PEEKUSER, child, 1138c2ecf20Sopenharmony_ci offsetof(struct user_regs_struct, rip), NULL); 1148c2ecf20Sopenharmony_ci if (rip == (unsigned long) -1) { 1158c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_PEEKUSER: %s\n", 1168c2ecf20Sopenharmony_ci strerror(errno)); 1178c2ecf20Sopenharmony_ci goto out; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciout: 1238c2ecf20Sopenharmony_ci if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 1248c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); 1258c2ecf20Sopenharmony_ci return TEST_FAIL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * This tests creates HW breakpoint, tries to 1338c2ecf20Sopenharmony_ci * change it to bogus value and checks the original 1348c2ecf20Sopenharmony_ci * breakpoint is hit. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic int bp_modify2(void) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci pid_t child; 1398c2ecf20Sopenharmony_ci int status; 1408c2ecf20Sopenharmony_ci unsigned long rip = 0, dr7 = 1; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci child = spawn_child(); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci waitpid(child, &status, 0); 1458c2ecf20Sopenharmony_ci if (WIFEXITED(status)) { 1468c2ecf20Sopenharmony_ci pr_debug("tracee exited prematurely 1\n"); 1478c2ecf20Sopenharmony_ci return TEST_FAIL; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * The parent does following steps: 1528c2ecf20Sopenharmony_ci * - creates a new breakpoint (id 0) for bp_1 function 1538c2ecf20Sopenharmony_ci * - tries to change that breakpoint to (-1) address 1548c2ecf20Sopenharmony_ci * - waits for the breakpoint to hit and checks 1558c2ecf20Sopenharmony_ci * it has proper rip of bp_1 function 1568c2ecf20Sopenharmony_ci * - detaches the child 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 1598c2ecf20Sopenharmony_ci offsetof(struct user, u_debugreg[0]), bp_1)) { 1608c2ecf20Sopenharmony_ci pr_debug("failed to set breakpoint: %s\n", 1618c2ecf20Sopenharmony_ci strerror(errno)); 1628c2ecf20Sopenharmony_ci goto out; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, child, 1668c2ecf20Sopenharmony_ci offsetof(struct user, u_debugreg[7]), dr7)) { 1678c2ecf20Sopenharmony_ci pr_debug("failed to set dr7: %s\n", strerror(errno)); 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!ptrace(PTRACE_POKEUSER, child, 1728c2ecf20Sopenharmony_ci offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) { 1738c2ecf20Sopenharmony_ci pr_debug("failed, breakpoint set to bogus address\n"); 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 1788c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); 1798c2ecf20Sopenharmony_ci goto out; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci waitpid(child, &status, 0); 1838c2ecf20Sopenharmony_ci if (WIFEXITED(status)) { 1848c2ecf20Sopenharmony_ci pr_debug("tracee exited prematurely 2\n"); 1858c2ecf20Sopenharmony_ci return TEST_FAIL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci rip = ptrace(PTRACE_PEEKUSER, child, 1898c2ecf20Sopenharmony_ci offsetof(struct user_regs_struct, rip), NULL); 1908c2ecf20Sopenharmony_ci if (rip == (unsigned long) -1) { 1918c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_PEEKUSER: %s\n", 1928c2ecf20Sopenharmony_ci strerror(errno)); 1938c2ecf20Sopenharmony_ci goto out; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciout: 1998c2ecf20Sopenharmony_ci if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 2008c2ecf20Sopenharmony_ci pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); 2018c2ecf20Sopenharmony_ci return TEST_FAIL; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciint test__bp_modify(struct test *test __maybe_unused, 2088c2ecf20Sopenharmony_ci int subtest __maybe_unused) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1()); 2118c2ecf20Sopenharmony_ci TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2()); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 215