From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5D524427FB7 for ; Mon, 3 Aug 2026 13:49:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764987; cv=none; b=ETqYONU5WOQ6ay+o/1GQyJKmHFtzhtismcOffJDJf8WCmTqp4c0qiNRVa/Na8riTEbMdRzY1q6+SoO6dTKjaoUWKReGAX+cweCnfYRKULJx9V8WfPnLO5sdpdko1yuW+mNFddfqdfnbhVbnv9TL5CEcnDcnePHQq+biIxYvq3jg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764987; c=relaxed/simple; bh=NQWrSfo3ilYzx+cLY2N/CE7Pchd/5aB2CnXgykg813c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dkgZXwA9P/Flf8jeQoE+MRczA5GGu+yox6vwcLBi+5Y41JeenBAG1SpdOgZNev8qr9EpRWMEWU10r6x9z9o1To6bchDqBWy65V7h6rw38/XwCdhDw+bgxdhBj2FvRZCZbCBK6k5s+GHsNKMqLjXnrKmo6y3xivjxgKcZrHFTx5Y= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=VogTMB9H; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=MZzu1dhC; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="VogTMB9H"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="MZzu1dhC" Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 673DRh5c136434 for ; Mon, 3 Aug 2026 13:49:44 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=ShQBno9gsVG rzPPyjW7zHkWoz0rbT24OP8r/WauE3gQ=; b=VogTMB9HV57MIBinon3t/yMuElw RWBFl8s9+dFyMShr07l7rLxRJVf9wZRw3/tFSabBIsy7nVPlfamn72RS/kzWznCs JU32FHn9tFFY0KoHyTwhZfxQ5s0kFV4EyUbJoXbPj6TmUzKikrrmyayXq4rMEvPu 2cS2yDjuLw2GntknBEba4pZ0eaKEjqgNP2+qCr74u63hIfrxpuST3QvtTtxZZacf lMOJ0QokrptP5A+H5MEcqB3h8YJqn8mAY6eemKtOTeNUs/0ZXG/4X7iKu9aPirbE bL3A07RRXY306SuEN+1H9uZMPe6KWIYYYouAXXvesbZzwMvwVZCUQ/QKXsg== Received: from mail-pf1-f199.google.com (mail-pf1-f199.google.com [209.85.210.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ftdmxb0dn-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 03 Aug 2026 13:49:44 +0000 (GMT) Received: by mail-pf1-f199.google.com with SMTP id d2e1a72fcca58-84c4cd31b51so4974779b3a.0 for ; Mon, 03 Aug 2026 06:49:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1785764984; x=1786369784; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ShQBno9gsVGrzPPyjW7zHkWoz0rbT24OP8r/WauE3gQ=; b=MZzu1dhCn1NgVBizltH6DyPL9Sd0Qwd3ed9+//0s82083auHOQt59n+alErW6yxJbN 1qwGF/IYNp67dT4D+JlSQI6Lf5Lqifhy7PSNSHkjZv6bhtOeOKVcACAA0FxnINrMOCJy nDsNtPPTPSHjOTV+F5qxgcwxUEMwBMrC+RvI4sBmibDUeXi7yiJzDkaXbelRVtIMtWIV RO2q9t4UqPP0MYaMrKVywFgc5QoBcH499i8RAhv9ROWgovXh8d9uNiOXL51b7L83QNVu D2e2Me/Ii/HNCmNt8okCr9Z2S4mg+2g4WJiReIvh6wAqMf7jFHGmpfeF6FoQZLnm9SNA 1vng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785764984; x=1786369784; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ShQBno9gsVGrzPPyjW7zHkWoz0rbT24OP8r/WauE3gQ=; b=RhLFZvcZPKeKaNPeF66n/nDINqxTgSGE1hywtT2ZXbvUudqoA5hhXHzmEQ+7cZ7pZy 7c+r291xmKuDanauNYPCXGfFtn8IfFh1cIalx/VDeS9bGNfytwi6smIBdS/tra8356PF xaC+BFCRpMunKdWOVYl21sdNV8LnfCtZJEk8ukQB4zDet61GUwQ0dsyHsgenZqxWBAXH py0+YUY2wBlGI8dxdyOpjhGkYGQHmyhZM5UFlNVKDQZ2NXlaiPyBR7qZWYFW3iOUZ84y LfYVAWvmwgyMyQTSeWaErJbwl2LZlh6tzfRcV51QhHExiDhmWUBc11F9S0AtjaD1RCie iZvA== X-Forwarded-Encrypted: i=1; AHgh+Rr4fprQJ4MqffeTqZngm7khIl5WIIjAacnIsPEIy4IX8V80XRH9K55VGNfeIj2HGK2mmg8Qv3rZVJ7XSmo=@vger.kernel.org X-Gm-Message-State: AOJu0YzPYb4Yi++jOXXLao6pga0szIFrJ9noJlZhY6+KK9cfOgQXRT05 oZuXZzBveLb2nx4k3PB8o7aWlPS4648qW9zt4lvNGT2D950z0HnzyKJrdgLZRHv3L1zIe+ew5HH NtizJ2DVjEwcpmYVllbt2LOIM9NszfWqcfHEqSK4POAQyW23YK5c9xQkufJ61FM9S22M= X-Gm-Gg: AR+sD104X1Z6/yBrxo9PRr5J9MVCMzyb/gZc21kv9la7DMfnO15cea4mn2OT09bghkF 7ZYWmXpyWKeJbumflM+fxTFZL+JFU2rk3Mb11l6Yi8/OFFWw8HWOcoAqX+bo3BUFs2ZUIritzuu ZA4Brn0Y/aRkfWiaV0+SFG7CYAUAFeXZ3CDdCiD8TknXgYgdjqaJVuzNZHDr7OIoJnoH4XlNmq9 xzMXV0aTTqklj7v1oZVvWSD8UwM9slHgBPYo9DXY6Ta9K6lIYDLXP0PX50ulpCL3aJBeLcVQhud 6l+0s+Hm3qr1O76HW1PUmw2BBWXtcxyqyxfuwQCjqU2n7oP2KXbvFQ9Ilz4FxISFrl1weyPKJt+ lDZwrCNunWUDyol8A92qp5QJTfU/ef/iWh0fpCsgSxYH4XXIY3iEg4gKL8KQn1obU5UoqZr4Jkr TQCazd+0Q58zhsU3Z2G9rQgVXb X-Received: by 2002:a05:6a21:6e46:b0:3bf:aa54:4cbc with SMTP id adf61e73a8af0-3c91a972ec7mr12617710637.26.1785764983667; Mon, 03 Aug 2026 06:49:43 -0700 (PDT) X-Received: by 2002:a05:6a21:6e46:b0:3bf:aa54:4cbc with SMTP id adf61e73a8af0-3c91a972ec7mr12617680637.26.1785764983204; Mon, 03 Aug 2026 06:49:43 -0700 (PDT) Received: from hu-himchau-blr.qualcomm.com (blr-bdr-fw-01_GlobalNAT_AllZones-Outside.qualcomm.com. [103.229.18.19]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3153e18d9c6sm39278870eec.30.2026.08.03.06.49.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 06:49:42 -0700 (PDT) From: Himanshu Chauhan To: linux-riscv@lists.infradead.org Cc: qingfang.deng@linux.dev, alex@ghiti.fr, aou@eecs.berkeley.edu, palmer@dabbelt.com, pjw@kernel.org, shuah@kernel.org, linux-kernel@vger.kernel.org, thecharlesjenkins@gmail.com, jtaubepe@redhat.com, Himanshu Chauhan Subject: [PATCH v6 4/5] selftests/breakpoints: extend riscv test for ptrace hw break/watchpoints Date: Mon, 3 Aug 2026 19:19:12 +0530 Message-ID: <20260803134913.2013674-5-himanshu.chauhan@oss.qualcomm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260803134913.2013674-1-himanshu.chauhan@oss.qualcomm.com> References: <20260803134913.2013674-1-himanshu.chauhan@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Proofpoint-GUID: CoiGnk4aNqMrXxc1hBFiXfgtmS52C2y_ X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX8sHrgcQb+Kw7 LoYeeCJt8RTdyVDk0WKrlhRbRuqFrm+UoIZAR30CfnyoX9xv9DDgrO3aehp1qAgnY+SQKHwO+N9 Hf+bEj/u+/flGA3iCcSX62P9G3uBM25vNSr5ZX1qvPwAKCMCpRld3xshiZ2N3A4WlX2b2VO89Av x44Ii7GRW7fg2l07COhQQp69JWJaizJmratng6TKSLyKceLvOEzcifA4isZGUdRlEKXyOaxQCRX wNNXRd4xzlW6RdAqbYbGWznFIqdXsZE4NThoD4JhB5c8ou7sGC0ROLdxnGtsG/+Nbpg9ESoOxG9 89Dpn4AVTXHiJN1iZ1wXUkDERHEXx+hJGUrDuPUCoYAJ++KAd2ZfrH+TUMSnoUcjkTwoURaqudq O44ZRFXpJgWdxOXQKB54ueQSNuAsh0wb5ljDRP+elsbkqQpe6QJL11T2yBLk3FO0aPLHL5/8DjN lu2yutP7Ku8Poji3hkQ== X-Proofpoint-ORIG-GUID: CoiGnk4aNqMrXxc1hBFiXfgtmS52C2y_ X-Proofpoint-Spam-Info: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX/ky+OID+tAyy 6+n6Bj0y6I9iPLn7Ah5APKDATOlxOtVxDotkObEq4dMoYQX0px4AXv/WIxVECj7H0iLE/uLWywC DiIwPA+ZqMkA1qmvdboIGgUdW3ubUyo= X-Authority-Analysis: v=2.4 cv=L7MtheT8 c=1 sm=1 tr=0 ts=6a709c78 cx=c_pps a=WW5sKcV1LcKqjgzy2JUPuA==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=EUspDBNiAAAA:8 a=07_doiqUAAAA:8 a=vV4cC3RZRCDA8ntysFEA:9 a=OpyuDcXvxspvyRM73sMx:22 a=S1vEknPFSxWy9jUyH7lK:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-03_03,2026-08-03_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 clxscore=1015 malwarescore=0 bulkscore=0 phishscore=0 priorityscore=1501 adultscore=0 impostorscore=0 lowpriorityscore=0 spamscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608030124 Cover the new ptrace-based hardware breakpoint/watchpoint support added for riscv: exercise the PTRACE_GETREGSET/SETREGSET regset path as well as the raw PTRACE_GETHBPREGS/SETHBPREGS interface, alongside the existing perf_event-based tests. Also drop a leftover #if 0 block duplicating the HWDEBUG_* control field macros already defined in uapi/asm/ptrace.h. Signed-off-by: Himanshu Chauhan --- .../breakpoints/breakpoint_test_riscv.c | 590 +++++++++++++++++- 1 file changed, 568 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_riscv.c b/tools/testing/selftests/breakpoints/breakpoint_test_riscv.c index 0649940b709e..ee6641531fa7 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test_riscv.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test_riscv.c @@ -9,6 +9,7 @@ #include /* Definition of PERF_* constants */ #include /* Definition of HW_* constants */ #include /* Definition of SYS_* constants */ +#include #include #include #include @@ -18,18 +19,572 @@ #include #include #include +#include +#include #include #include #include +#include +#include +#include #ifndef noinline #define noinline __attribute__((noinline)) #endif +#include "kselftest.h" + +static int test_func_sink; + +/* + * Keep a real instruction address for HW execute breakpoints: prevent inlining + * and force a visible side effect so the function can't be optimized away. + */ +static noinline void test_func(void) +{ + test_func_sink++; + __asm__ __volatile__("" : : "g" (test_func_sink)); +} + +/* + * BREAKPOINT TEST USING PTRACE + */ +static int do_wp_child(void *addr, size_t size) +{ + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { + ksft_print_msg( + "ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); + _exit(1); + } + + if (raise(SIGSTOP) != 0) { + ksft_print_msg( + "raise(SIGSTOP) failed: %s\n", strerror(errno)); + _exit(1); + } + + sleep(1); + switch (size) { + case 1: + *(uint8_t *)addr = 47; + break; + case 2: + *(uint16_t *)addr = 47; + break; + case 4: + *(uint32_t *)addr = 47; + break; + case 8: + *(uint64_t *)addr = 47; + break; + default: + ksft_print_msg("Unknown watchpoint access size %u\n", size); + break; + } + + _exit(0); +} + +static int do_bp_child(void (*bp_func)(void)) +{ + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { + ksft_print_msg( + "ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); + _exit(1); + } + + if (raise(SIGSTOP) != 0) { + ksft_print_msg( + "raise(SIGSTOP) failed: %s\n", strerror(errno)); + _exit(1); + } + + bp_func(); +} + +unsigned long var; + +static bool set_watchpoint(pid_t pid, int size) +{ + uint8_t *addr = (uint8_t *)&var; + unsigned int control = 0; + struct user_hwdebug_state dreg_state; + struct iovec iov; + + /* Write watchpoint */ + control = (HW_BREAKPOINT_W << 14) & ((0x7 << 14)); + control |= (HW_BREAKPOINT_LEN_8 << 4) & ((0x1f << 4)); + memset(&dreg_state, 0, sizeof(dreg_state)); + dreg_state.dbg_regs[0].addr = (uintptr_t)(addr); + dreg_state.dbg_regs[0].control = control; + iov.iov_base = &dreg_state; + iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + + sizeof(dreg_state.dbg_regs[0]); + + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_HW_WATCH, &iov) == 0) { + memset(&iov, 0, sizeof(iov)); + memset(&dreg_state, 0, sizeof(dreg_state)); + + iov.iov_base = &dreg_state; + iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + + sizeof(dreg_state.dbg_regs[0]); + + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_HW_WATCH, &iov) == 0) { + ksft_print_msg( + "ptrace(PTRACE_GETREGSET): Number of watchpoints: %u\n", dreg_state.info); + ksft_print_msg( + "ptrace(PTRACE_GETREGSet): addr: 0x%lx control: 0x%x\n", dreg_state.dbg_regs[0].addr, dreg_state.dbg_regs[0].control); + } else { + ksft_print_msg( + "ptrace(PTRACE_GETREGSET): Failed\n"); + return false; + } + + return true; + } + + if (errno == EIO) + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_WATCH) not supported on this hardware: %s\n", + strerror(errno)); + else + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_WATCH) failed: %s\n", + strerror(errno)); + return false; +} + +static bool set_breakpoint(pid_t pid, void (*bp_func)(void)) +{ + struct user_hwdebug_state dreg_state; + struct iovec iov; + unsigned int control = 0; + + control = (HW_BREAKPOINT_X << 14) & ((0x7 << 14)); + control |= (HW_BREAKPOINT_LEN_8 << 4) & ((0x1f << 4)); + memset(&dreg_state, 0, sizeof(dreg_state)); + dreg_state.dbg_regs[0].addr = (uintptr_t)bp_func; + dreg_state.dbg_regs[0].control = control; + iov.iov_base = &dreg_state; + iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]); + + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_HW_BREAK, &iov) == 0) + return true; + + if (errno == EIO) + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_BREAK) not supported on this hardware: %s\n", strerror(errno)); + else + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_BREAK) failed: %s\n", strerror(errno)); + + return false; +} + +static int run_ptrace_wp_test(void) +{ + pid_t pid = fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid == 0) + do_wp_child(&var, 8); + + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg( + "child did not stop: %s\n", strerror(errno)); + return false; + } + if (WSTOPSIG(status) != SIGSTOP) { + ksft_print_msg("child did not stop with SIGSTOP\n"); + return false; + } + + if (!set_watchpoint(pid, 8)) + return false; + + if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { + ksft_print_msg( + "ptrace(PTRACE_CONT) failed: %s\n", + strerror(errno)); + return false; + } + + alarm(3); + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); + return false; + } + alarm(0); + if (WIFEXITED(status)) { + ksft_print_msg("child exited prematurely\n"); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg("child did not stop\n"); + return false; + } + if (WSTOPSIG(status) != SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { + ksft_print_msg( + "ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code != TRAP_HWBKPT) { + ksft_print_msg( + "Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid = waitpid(pid, &status, 0); + if (wpid != pid) { + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); + return false; + } + + ksft_print_msg("[ptrace]: Watchpoint test passed!\n"); + + return true; +} + +static int run_ptrace_bp_test(void) +{ + pid_t pid = fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid == 0) + do_bp_child(test_func); + + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg( + "child did not stop: %s\n", strerror(errno)); + return false; + } + if (WSTOPSIG(status) != SIGSTOP) { + ksft_print_msg("child did not stop with SIGSTOP\n"); + return false; + } + + if (!set_breakpoint(pid, test_func)) + return false; + + if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { + ksft_print_msg( + "ptrace(PTRACE_CONT) failed: %s\n", + strerror(errno)); + return false; + } + + alarm(3); + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); + return false; + } + alarm(0); + if (WIFEXITED(status)) { + ksft_print_msg("child exited prematurely\n"); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg("child did not stop\n"); + return false; + } + if (WSTOPSIG(status) != SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { + ksft_print_msg( + "ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code != TRAP_HWBKPT) { + ksft_print_msg( + "Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid = waitpid(pid, &status, 0); + if (wpid != pid) { + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); + return false; + } + + ksft_print_msg("[ptrace]: Breakpoint test passed!\n"); + + return true; +} + +/* + * BREAKPOINT TEST USING PTRACE_SETHBPREGS / PTRACE_GETHBPREGS + */ +static bool set_hbpregs_watchpoint(pid_t pid) +{ + struct __riscv_hwdebug_state state; + + memset(&state, 0, sizeof(state)); + state.addr = (unsigned long)&var; + state.len = HW_BREAKPOINT_LEN_8; + state.type = HW_BREAKPOINT_W; + state.ctrl = 0; /* enabled */ + + if (ptrace(PTRACE_SETHBPREGS, pid, 0, &state) != 0) { + ksft_print_msg( + "ptrace(PTRACE_SETHBPREGS) failed: %s\n", + strerror(errno)); + return false; + } + + /* Read back and verify */ + memset(&state, 0, sizeof(state)); + if (ptrace(PTRACE_GETHBPREGS, pid, 0, &state) != 0) { + ksft_print_msg( + "ptrace(PTRACE_GETHBPREGS) failed: %s\n", + strerror(errno)); + return false; + } + + ksft_print_msg( + "[hbpregs] watchpoint readback: addr=0x%lx type=%lu len=%lu ctrl=%lu\n", + state.addr, state.type, state.len, state.ctrl); + + return true; +} + +static bool set_hbpregs_breakpoint(pid_t pid, void (*bp_func)(void)) +{ + struct __riscv_hwdebug_state state; + + memset(&state, 0, sizeof(state)); + state.addr = (unsigned long)bp_func; + state.len = HW_BREAKPOINT_LEN_4; + state.type = HW_BREAKPOINT_X; + state.ctrl = 0; /* enabled */ + + if (ptrace(PTRACE_SETHBPREGS, pid, 0, &state) != 0) { + ksft_print_msg( + "ptrace(PTRACE_SETHBPREGS) failed: %s\n", + strerror(errno)); + return false; + } + + /* Read back and verify */ + memset(&state, 0, sizeof(state)); + if (ptrace(PTRACE_GETHBPREGS, pid, 0, &state) != 0) { + ksft_print_msg( + "ptrace(PTRACE_GETHBPREGS) failed: %s\n", + strerror(errno)); + return false; + } + + ksft_print_msg( + "[hbpregs] breakpoint readback: addr=0x%lx type=%lu len=%lu ctrl=%lu\n", + state.addr, state.type, state.len, state.ctrl); + + return true; +} + +static int run_hbpregs_wp_test(void) +{ + pid_t pid = fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid == 0) + do_wp_child(&var, 8); + + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg("child did not stop: %s\n", strerror(errno)); + return false; + } + if (WSTOPSIG(status) != SIGSTOP) { + ksft_print_msg("child did not stop with SIGSTOP\n"); + return false; + } + + if (!set_hbpregs_watchpoint(pid)) + return false; + + if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { + ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n", + strerror(errno)); + return false; + } + + alarm(3); + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); + return false; + } + alarm(0); + if (WIFEXITED(status)) { + ksft_print_msg("child exited prematurely\n"); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg("child did not stop\n"); + return false; + } + if (WSTOPSIG(status) != SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { + ksft_print_msg("ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code != TRAP_HWBKPT) { + ksft_print_msg("Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid = waitpid(pid, &status, 0); + if (wpid != pid) { + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); + return false; + } + + ksft_print_msg("[hbpregs]: Watchpoint test passed!\n"); + return true; +} + +static int run_hbpregs_bp_test(void) +{ + pid_t pid = fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid == 0) + do_bp_child(test_func); + + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg("child did not stop: %s\n", strerror(errno)); + return false; + } + if (WSTOPSIG(status) != SIGSTOP) { + ksft_print_msg("child did not stop with SIGSTOP\n"); + return false; + } + + if (!set_hbpregs_breakpoint(pid, test_func)) + return false; + + if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { + ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n", + strerror(errno)); + return false; + } + + alarm(3); + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); + return false; + } + alarm(0); + if (WIFEXITED(status)) { + ksft_print_msg("child exited prematurely\n"); + return false; + } + if (!WIFSTOPPED(status)) { + ksft_print_msg("child did not stop\n"); + return false; + } + if (WSTOPSIG(status) != SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { + ksft_print_msg("ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code != TRAP_HWBKPT) { + ksft_print_msg("Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid = waitpid(pid, &status, 0); + if (wpid != pid) { + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); + return false; + } + + ksft_print_msg("[hbpregs]: Breakpoint test passed!\n"); + return true; +} + +static void run_hbpregs_tests(void) +{ + run_hbpregs_bp_test(); + run_hbpregs_wp_test(); +} + +/* + * BREAKPOINT TEST USING PTRACE_SETHBPREGS / PTRACE_GETHBPREGS - END + */ +static void run_ptrace_tests(void) +{ + run_ptrace_bp_test(); + run_ptrace_wp_test(); +} + +/* + * BREAKPOINT TEST USING PTRACE - END + */ + +/* + * BREAKPOINT TEST USING perf events + */ static int gfd; sem_t ib_mtx, wp_mtx; static int bp_triggered, wp_triggered; -static int test_func_sink; static const int wait_timeout_sec = 5; int setup_bp(bool is_x, void *addr, int sig) @@ -56,7 +611,7 @@ int setup_bp(bool is_x, void *addr, int sig) fd = syscall(SYS_perf_event_open, &pe, 0, -1, -1, 0); if (fd < 0) { - printf("Failed to open event: %llx\n", pe.config); + ksft_print_msg("Failed to open event: %llx\n", pe.config); return -1; } @@ -75,11 +630,10 @@ static void sig_handler_bp(int signum, siginfo_t *oh, void *uc) bp_triggered++; - printf("Breakpoint triggered!\n"); ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); ret = sem_post(&ib_mtx); if (ret) { - printf("Failed to report BP success\n"); + ksft_print_msg("Failed to report BP success\n"); return; } } @@ -88,28 +642,17 @@ static void sig_handler_wp(int signum, siginfo_t *oh, void *uc) { int ret; - printf("Watchpoint triggered!\n"); ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); wp_triggered++; ret = sem_post(&wp_mtx); if (ret) { - printf("Failed to report WP success\n"); + ksft_print_msg("Failed to report WP success\n"); return; } } -/* - * Keep a real instruction address for HW execute breakpoints: prevent inlining - * and force a visible side effect so the function can't be optimized away. - */ -static noinline void test_func(void) -{ - test_func_sink++; - __asm__ __volatile__("" : : "g" (test_func_sink)); -} - static int trigger_bp(void) { struct sigaction sa; @@ -119,14 +662,14 @@ static int trigger_bp(void) sa.sa_flags = SA_SIGINFO; if (sigaction(SIGIO, &sa, NULL) < 0) { - printf("Failed to setup signal handler\n"); + ksft_print_msg("Failed to setup signal handler\n"); return -1; } gfd = setup_bp(1, test_func, SIGIO); if (gfd < 0) { - printf("Failed to setup breakpoint.\n"); + ksft_print_msg("Failed to setup breakpoint.\n"); return -1; } @@ -151,14 +694,14 @@ static int trigger_wp(void) sa.sa_flags = SA_SIGINFO; if (sigaction(SIGUSR1, &sa, NULL) < 0) { - printf("Failed to setup signal handler\n"); + ksft_print_msg("Failed to setup signal handler\n"); return -1; } gfd = setup_bp(0, &test_data, SIGUSR1); if (gfd < 0) { - printf("Failed to setup watchpoint\n"); + ksft_print_msg("Failed to setup watchpoint\n"); return -1; } @@ -204,7 +747,7 @@ int main(int argc, char *argv[]) return -1; if (bp_triggered) - printf("Breakpoint test passed!\n"); + ksft_print_msg("[perf_event]: Breakpoint test passed!\n"); sem_init(&wp_mtx, 0, 0); if (trigger_wp() < 0) @@ -213,7 +756,10 @@ int main(int argc, char *argv[]) return -1; if (wp_triggered) - printf("Watchpoint test passed!\n"); + ksft_print_msg("[perf_event]: Watchpoint test passed!\n"); + + run_ptrace_tests(); + run_hbpregs_tests(); return 0; } -- 2.43.0