From nobody Fri Oct 2 08:26:24 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.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 3C4AF3B994A for ; Mon, 3 Aug 2026 13:49:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764977; cv=none; b=JvP+KpQkNA/bJAtrMXvYzlN6ZWqqHWeLBRrzaDmGih55+UDyxms0UAE3aM74m/yExxoLPEaScWj2nrO66NM2UvkIy+npw4wvAf0rozptXqBgniP7w0lopF4SZ3T2OdZgwH2giMuGjMghxB36bjTdNq3M7N6/8GobVd0S3wWty0g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764977; c=relaxed/simple; bh=DBTeMsWnH72Kk16BU+Tv9h/ErIro7+1K8h5dKOMi6V0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j2N6VH3Rh1uGVOYzaNFHgUjHuTUARgF118M7Q64ajO/dLtYO7AV8SktXIzSfC1esbKUbDfJg2LAe+joaxZctpYegY4NJmHCJ5qLwulrYWIydSKvq75ZvOAY9XoMlJ5N2aJSqnbRaIdGa2c6d+8AGB+llaBUXtQxaTnmHmwEuXBk= 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=nWoFRdBF; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=ijwObJnM; arc=none smtp.client-ip=205.220.180.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="nWoFRdBF"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="ijwObJnM" Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 673DSCJI1048125 for ; Mon, 3 Aug 2026 13:49:33 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=GogFcmZK3QU pvZPf6ymol9rMA8EmMthoUGmNFn2L0U4=; b=nWoFRdBFKUcubw0LUMPqLvCOCS+ EbBeBz3nCq4SrrCVLsjnJwhHe3ExuQOP7V9rXjBZU0Qq8Yt2X6Fr13mZEES6OeeK ++xCM358wYcB+2KNlnxqNPvXNsHF7VAxLFYanpOA5FXrOPbRbNnrNAP6odp++YcH 9t73jGSBGDRdGbSlvIcjR2VWjgsgXbDfqHoq1COIDRU0NdBMOS8A6QyC+ijAKuAc Yv84aPcyQiJwky7dlTb+KsYZveP8t+2B0ZD7zC2yCbIBUkWCmQaoFRHX/ZDCbZTz watDlnDSIl23DjmYMHfZCiSTNfZcp3RofOGNBIezkVdHl+1Ph7zdoeMdOZg== Received: from mail-pg1-f197.google.com (mail-pg1-f197.google.com [209.85.215.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ftnrb9td4-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 03 Aug 2026 13:49:32 +0000 (GMT) Received: by mail-pg1-f197.google.com with SMTP id 41be03b00d2f7-cbb92868263so4421557a12.2 for ; Mon, 03 Aug 2026 06:49:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1785764972; x=1786369772; 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=GogFcmZK3QUpvZPf6ymol9rMA8EmMthoUGmNFn2L0U4=; b=ijwObJnM1XD/gpQVpR+k3NWjttfCV5Rg7Fjics9xUwPD4vItxiJhc/HysofyRsgK0r 4+3pBWE2WceuoIR69d7zfeqy0KidwveN+7ZTwz2c0vt4pv2pjkb2979ike5qV7CEKwEW ENbXAaD2tGnslFH/SCpIZXQXKlSGEOxIOZGYRijxXTHLtg1qht8lOaWHOGm/HnmX/Psj PKb7/+3+R7iehf+urB11vCU3MyP18zHQST5wjgOpVIpJP2bl1YoqTY7myicGT1noxc3E Ydzo062SefAXXM3k/7wxGcdzQgT07ySGiQNhayACYjN+n62YvBvhEGXDFkPMXDETlPg/ pYlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785764972; x=1786369772; 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=GogFcmZK3QUpvZPf6ymol9rMA8EmMthoUGmNFn2L0U4=; b=e7rRcbEUB2bVoEzSPzs9cqmCeRhAXj++cmmH7mZzo8t+VxIv0n/xoXo6Kz7sO9XxVP of7ffaF46mwmNoB1C2MoHxc0Sy2BBmGmIJBfWPSILOsv87Gwo1qNHi/JgWMnLIuae3xA P62GLkemwPGyDIWeGxvnrv3OyCYYbLgKymoEMj5mDIOLB/L8Cc9HkPy3Dgyc4mG0CLpz bdzfNbAF3kSXokHcvM37omIrAghbhfXjMVCEtVhg4eSy2Or6wvDbqSnMok0efxe5PTL/ lrNeeZ8KwZfXeHJIGdyjd5S1Trl1sNS5aTcbh8VkB+6uCu9xQxzYS8zFimlMMsct86dE e1tA== X-Forwarded-Encrypted: i=1; AHgh+Rq+tvGhpO9zHGGgelTlZqzT1ABlKa48Qa1jxpaYPeZgskHrsCvdTgpnf9bPr92YvLUDiHGKaPc7oUD2XTA=@vger.kernel.org X-Gm-Message-State: AOJu0YwhcQJALhmGvbDf7jx3g298j539n7pgJKmuqJI91Rl1xB2KmCEF WuxapvJ31FLEZHO3xRHKvq/9KfhoRx2fFNW4Jxlbbd4NZtuGrn9uo9SjDsESdpeHcaqdu7ogyvA zf8U3t0K30/4w6zmVAFLeRodzO4aC3eY/LrI7Oa1TpUoudik2ziOHkVnsZ1Omrid0K0Y= X-Gm-Gg: AR+sD10/gsMHm0tQODA/++4bFhZyAtjvYJ5yELS+LcSy/Opc/jfzPn3NXJd7es6JIsR RONy+7WciqWX6JulN3mxTq0CLUs1H1NCYLkhbrJgTQXdxx/gwoW7jG7pGE28a+cp0DkH1j7Bc/Z EZJ+QbLJPTgb/UJVCsLv/a1XbsLKn0H3WrBqexySkmeZY7GSFe4Hr2NcyzrvyAQn5TP3l3pmpfn NSDzd2YVYMnfBXJcIZt7LWo2ZwUSVokDx4l10OzxQufzbP2dyIFrYSuHOdL9ZALqsZhHC7Njsa0 5dAIUdR62edZIdyjENprASpWju0CmboAoi6glaOS1BIHrK4yThgj/jp73Gm6850NUJmxCW7013F Jc/AjqQx0Lhr0ln+OdWH7JF5x174oxRyvvTDULcRkqHiRDTqvsRdfWNGnqo/sbh8OmUvo7SbZaI GY9tPWcHpyMBo74VZ4U2FMh3iR X-Received: by 2002:a05:6a21:7a46:b0:3bf:6011:53b with SMTP id adf61e73a8af0-3c92a8139c6mr10068284637.38.1785764971430; Mon, 03 Aug 2026 06:49:31 -0700 (PDT) X-Received: by 2002:a05:6a21:7a46:b0:3bf:6011:53b with SMTP id adf61e73a8af0-3c92a8139c6mr10068233637.38.1785764970753; Mon, 03 Aug 2026 06:49:30 -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.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 06:49:30 -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 1/5] riscv: Introduce support for hardware break/watchpoints Date: Mon, 3 Aug 2026 19:19:09 +0530 Message-ID: <20260803134913.2013674-2-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: quoted-printable X-Proofpoint-Spam-Info: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX143KS8fhOLtt gv0iniWq4yeOsIEjl38q2MfhGeuGm1SEdNfujmkWcElqYcQVwhWGlZx8e+89cVa4dPlkILYliZ2 7yU7zFnUAjQP71ADfa50q9OzsAWdhmw= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX0G/fbgFDV0w/ U/MwA6u6cgoUg0shTqi4SGUUVh4pXysA3+MS0zXG46+uudES7lU1Ecr1V6md4UiBFsDBZEd4sho KPnt/jLn0tKZblztVSznC9zqYQOLPwBVyR4uvjzOGSOGFOdCIOtO0wdCSC2REj+lKyqStqtNXop Ew8Ta7ZlZSSwpT3ByxZEnFMb8EjS4cKlUY5bMnx/7OzQ1Y0vrWtTRjVWYfoPelsGkAqQIAR0auW Ji1C+j6GnnvmxxMvN2j63JrAFExCRewlaph3V9vvXqYUrsvrecHk6O9ZlMJVlW6OENP6iBh6WtV X/orzXL0DHO1Y3MjMU836leYgt/cWNPZtLQqAH2UQvaBwnEwSaHYG+kQbuURSyex+33OZTmGORw qStEqfGNS7uiKfZDAHYjEpuexigLFJXOwtLJTxTLC3H9lWTl99gBI1pZm9dD3kXi48rl6yarXGP QXfrsNqHKZlUunSQjLQ== X-Authority-Analysis: v=2.4 cv=RNSD2Yi+ c=1 sm=1 tr=0 ts=6a709c6c cx=c_pps a=rz3CxIlbcmazkYymdCej/Q==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=HQMOZhRFWg9NeK7U6LwA:9 a=bFCP_H2QrGi7Okbo017w:22 X-Proofpoint-GUID: mHeKd_Pftv9heQbEgr8d-cuhIt86HBfl X-Proofpoint-ORIG-GUID: mHeKd_Pftv9heQbEgr8d-cuhIt86HBfl 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 spamscore=0 suspectscore=0 clxscore=1015 adultscore=0 lowpriorityscore=0 malwarescore=0 phishscore=0 impostorscore=0 priorityscore=1501 bulkscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608030124 Content-Type: text/plain; charset="utf-8" RISC-V hardware breakpoint framework is built on top of perf subsystem and uses SBI debug trigger extension to install/uninstall/update/enable/disable hardware triggers as specified in Sdtrig ISA extension. Signed-off-by: Himanshu Chauhan --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/hw_breakpoint.h | 289 +++++++++++ arch/riscv/include/asm/kdebug.h | 3 +- arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/hw_breakpoint.c | 678 +++++++++++++++++++++++++ arch/riscv/kernel/traps.c | 6 + 6 files changed, 977 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/include/asm/hw_breakpoint.h create mode 100644 arch/riscv/kernel/hw_breakpoint.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index f7028caaeae0..a624dacdaf12 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -172,6 +172,7 @@ config RISCV select HAVE_FUNCTION_ERROR_INJECTION select HAVE_GCC_PLUGINS select HAVE_GENERIC_VDSO if MMU + select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_IRQ_TIME_ACCOUNTING select HAVE_KERNEL_BZIP2 if !EFI_ZBOOT select HAVE_KERNEL_GZIP if !EFI_ZBOOT diff --git a/arch/riscv/include/asm/hw_breakpoint.h b/arch/riscv/include/as= m/hw_breakpoint.h new file mode 100644 index 000000000000..4df1bfe0507e --- /dev/null +++ b/arch/riscv/include/asm/hw_breakpoint.h @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2026 Qualcomm Technologies, Inc. + */ + +#ifndef __RISCV_HW_BREAKPOINT_H +#define __RISCV_HW_BREAKPOINT_H + +struct task_struct; + +#ifdef CONFIG_HAVE_HW_BREAKPOINT + +#include + +/* Maximum number of hardware breakpoints supported */ +#define RISCV_HW_BP_NUM_MAX 32 + +#if __riscv_xlen =3D=3D 64 +#define cpu_to_le cpu_to_le64 +#define le_to_cpu le64_to_cpu +#elif __riscv_xlen =3D=3D 32 +#define cpu_to_le cpu_to_le32 +#define le_to_cpu le32_to_cpu +#else +#error "Unexpected __riscv_xlen" +#endif + +#define CLEAR_DBTR_BIT(_target, _bit) ((_target) &=3D ~BIT(_bit)) +#define SET_DBTR_BIT(_target, _bit) ((_target) |=3D BIT(_bit)) + +#define RISCV_DBTR_EXEC BIT(0) +#define RISCV_DBTR_LOAD BIT(1) +#define RISCV_DBTR_STORE BIT(2) +#define RISCV_DBTR_LDST (RISCV_DBTR_LOAD | RISCV_DBTR_STORE) + +enum { + RISCV_DBTR_TRIG_NONE =3D 0, + RISCV_DBTR_TRIG_LEGACY, + RISCV_DBTR_TRIG_MCONTROL, + RISCV_DBTR_TRIG_ICOUNT, + RISCV_DBTR_TRIG_ITRIGGER, + RISCV_DBTR_TRIG_ETRIGGER, + RISCV_DBTR_TRIG_MCONTROL6, +}; + +/* Trigger Data 1 */ +#define RISCV_DBTR_TDATA1_DATA_BIT 0 +#if __riscv_xlen =3D=3D 64 +#define RISCV_DBTR_TDATA1_DMODE_BIT 59 +#define RISCV_DBTR_TDATA1_TYPE_BIT 60 +#elif __riscv_xlen =3D=3D 32 +#define RISCV_DBTR_TDATA1_DMODE_BIT 27 +#define RISCV_DBTR_TDATA1_TYPE_BIT 28 +#else +#error "Unknown __riscv_xlen" +#endif + +#if __riscv_xlen =3D=3D 64 +#define RISCV_DBTR_TDATA1_DATA_BIT_MASK GENMASK(58, RISCV_DBTR_TDATA1_DAT= A_BIT) +#elif __riscv_xlen =3D=3D 32 +#define RISCV_DBTR_TDATA1_DATA_BIT_MASK GENMASK(26, RISCV_DBTR_TDATA1_DAT= A_BIT) +#else +#error "Unknown __riscv_xlen" +#endif +#define RISCV_DBTR_TDATA1_DMODE_BIT_MASK BIT(RISCV_DBTR_TDATA1_DMODE_BIT) +#define RISCV_DBTR_TDATA1_TYPE_BIT_MASK \ + GENMASK(RISCV_DBTR_TDATA1_TYPE_BIT + 3, RISCV_DBTR_TDATA1_TYPE_BIT) + +/* MC - Match Control Type Register */ +#define RISCV_DBTR_MC_LOAD_BIT 0 +#define RISCV_DBTR_MC_STORE_BIT 1 +#define RISCV_DBTR_MC_EXEC_BIT 2 +#define RISCV_DBTR_MC_U_BIT 3 +#define RISCV_DBTR_MC_S_BIT 4 +#define RISCV_DBTR_MC_RES2_BIT 5 +#define RISCV_DBTR_MC_M_BIT 6 +#define RISCV_DBTR_MC_MATCH_BIT 7 +#define RISCV_DBTR_MC_CHAIN_BIT 11 +#define RISCV_DBTR_MC_ACTION_BIT 12 +#define RISCV_DBTR_MC_SIZELO_BIT 16 +#define RISCV_DBTR_MC_TIMING_BIT 18 +#define RISCV_DBTR_MC_SELECT_BIT 19 +#define RISCV_DBTR_MC_HIT_BIT 20 +#if __riscv_xlen >=3D 64 +#define RISCV_DBTR_MC_SIZEHI_BIT 21 +#endif +#if __riscv_xlen =3D=3D 64 +#define RISCV_DBTR_MC_MASKMAX_BIT 53 +#define RISCV_DBTR_MC_DMODE_BIT 59 +#define RISCV_DBTR_MC_TYPE_BIT 60 +#elif __riscv_xlen =3D=3D 32 +#define RISCV_DBTR_MC_MASKMAX_BIT 21 +#define RISCV_DBTR_MC_DMODE_BIT 27 +#define RISCV_DBTR_MC_TYPE_BIT 28 +#else +#error "Unknown riscv xlen" +#endif + +#define RISCV_DBTR_MC_LOAD_BIT_MASK BIT(RISCV_DBTR_MC_LOAD_BIT) +#define RISCV_DBTR_MC_STORE_BIT_MASK BIT(RISCV_DBTR_MC_STORE_BIT) +#define RISCV_DBTR_MC_EXEC_BIT_MASK BIT(RISCV_DBTR_MC_EXEC_BIT) +#define RISCV_DBTR_MC_U_BIT_MASK BIT(RISCV_DBTR_MC_U_BIT) +#define RISCV_DBTR_MC_S_BIT_MASK BIT(RISCV_DBTR_MC_S_BIT) +#define RISCV_DBTR_MC_RES2_BIT_MASK BIT(RISCV_DBTR_MC_RES2_BIT) +#define RISCV_DBTR_MC_M_BIT_MASK BIT(RISCV_DBTR_MC_M_BIT) +#define RISCV_DBTR_MC_MATCH_BIT_MASK \ + GENMASK(RISCV_DBTR_MC_MATCH_BIT + 3, RISCV_DBTR_MC_MATCH_BIT) +#define RISCV_DBTR_MC_CHAIN_BIT_MASK BIT(RISCV_DBTR_MC_CHAIN_BIT) +#define RISCV_DBTR_MC_ACTION_BIT_MASK \ + GENMASK(RISCV_DBTR_MC_ACTION_BIT + 3, RISCV_DBTR_MC_ACTION_BIT) +#define RISCV_DBTR_MC_SIZELO_BIT_MASK \ + GENMASK(RISCV_DBTR_MC_SIZELO_BIT + 1, RISCV_DBTR_MC_SIZELO_BIT) +#define RISCV_DBTR_MC_TIMING_BIT_MASK BIT(RISCV_DBTR_MC_TIMING_BIT) +#define RISCV_DBTR_MC_SELECT_BIT_MASK BIT(RISCV_DBTR_MC_SELECT_BIT) +#define RISCV_DBTR_MC_HIT_BIT_MASK BIT(RISCV_DBTR_MC_HIT_BIT) +#if __riscv_xlen >=3D 64 +#define RISCV_DBTR_MC_SIZEHI_BIT_MASK \ + GENMASK(RISCV_DBTR_MC_SIZEHI_BIT + 1, RISCV_DBTR_MC_SIZEHI_BIT) +#endif +#define RISCV_DBTR_MC_MASKMAX_BIT_MASK \ + GENMASK(RISCV_DBTR_MC_MASKMAX_BIT + 5, RISCV_DBTR_MC_MASKMAX_BIT) +#define RISCV_DBTR_MC_DMODE_BIT_MASK BIT(RISCV_DBTR_MC_DMODE_BIT) +#define RISCV_DBTR_MC_TYPE_BIT_MASK GENMASK(RISCV_DBTR_MC_TYPE_BIT + 3, RI= SCV_DBTR_MC_TYPE_BIT) + +/* MC6 - Match Control 6 Type Register */ +#define RISCV_DBTR_MC6_LOAD_BIT 0 +#define RISCV_DBTR_MC6_STORE_BIT 1 +#define RISCV_DBTR_MC6_EXEC_BIT 2 +#define RISCV_DBTR_MC6_U_BIT 3 +#define RISCV_DBTR_MC6_S_BIT 4 +#define RISCV_DBTR_MC6_RES2_BIT 5 +#define RISCV_DBTR_MC6_M_BIT 6 +#define RISCV_DBTR_MC6_MATCH_BIT 7 +#define RISCV_DBTR_MC6_CHAIN_BIT 11 +#define RISCV_DBTR_MC6_ACTION_BIT 12 +#define RISCV_DBTR_MC6_SIZE_BIT 16 +#define RISCV_DBTR_MC6_TIMING_BIT 20 +#define RISCV_DBTR_MC6_SELECT_BIT 21 +#define RISCV_DBTR_MC6_HIT_BIT 22 +#define RISCV_DBTR_MC6_VU_BIT 23 +#define RISCV_DBTR_MC6_VS_BIT 24 +#if __riscv_xlen =3D=3D 64 +#define RISCV_DBTR_MC6_DMODE_BIT 59 +#define RISCV_DBTR_MC6_TYPE_BIT 60 +#elif __riscv_xlen =3D=3D 32 +#define RISCV_DBTR_MC6_DMODE_BIT 27 +#define RISCV_DBTR_MC6_TYPE_BIT 28 +#else +#error "Unknown riscv xlen" +#endif + +#define RISCV_DBTR_MC6_LOAD_BIT_MASK BIT(RISCV_DBTR_MC6_LOAD_BIT) +#define RISCV_DBTR_MC6_STORE_BIT_MASK BIT(RISCV_DBTR_MC6_STORE_BIT) +#define RISCV_DBTR_MC6_EXEC_BIT_MASK BIT(RISCV_DBTR_MC6_EXEC_BIT) +#define RISCV_DBTR_MC6_U_BIT_MASK BIT(RISCV_DBTR_MC6_U_BIT) +#define RISCV_DBTR_MC6_S_BIT_MASK BIT(RISCV_DBTR_MC6_S_BIT) +#define RISCV_DBTR_MC6_RES2_BIT_MASK BIT(RISCV_DBTR_MC6_RES2_BIT) +#define RISCV_DBTR_MC6_M_BIT_MASK BIT(RISCV_DBTR_MC6_M_BIT) +#define RISCV_DBTR_MC6_MATCH_BIT_MASK \ + GENMASK(RISCV_DBTR_MC6_MATCH_BIT + 3, RISCV_DBTR_MC6_MATCH_BIT) +#define RISCV_DBTR_MC6_CHAIN_BIT_MASK BIT(RISCV_DBTR_MC6_CHAIN_BIT) +#define RISCV_DBTR_MC6_ACTION_BIT_MASK \ + GENMASK(RISCV_DBTR_MC6_ACTION_BIT + 3, RISCV_DBTR_MC6_ACTION_BIT) +#define RISCV_DBTR_MC6_SIZE_BIT_MASK \ + GENMASK(RISCV_DBTR_MC6_SIZE_BIT + 3, RISCV_DBTR_MC6_SIZE_BIT) +#define RISCV_DBTR_MC6_TIMING_BIT_MASK BIT(RISCV_DBTR_MC6_TIMING_BIT) +#define RISCV_DBTR_MC6_SELECT_BIT_MASK BIT(RISCV_DBTR_MC6_SELECT_BIT) +#define RISCV_DBTR_MC6_HIT_BIT_MASK BIT(RISCV_DBTR_MC6_HIT_BIT) +#define RISCV_DBTR_MC6_VU_BIT_MASK BIT(RISCV_DBTR_MC6_VU_BIT) +#define RISCV_DBTR_MC6_VS_BIT_MASK BIT(RISCV_DBTR_MC6_VS_BIT) +#define RISCV_DBTR_MC6_DMODE_BIT_MASK BIT(RISCV_DBTR_MC6_DMODE_BIT) +#define RISCV_DBTR_MC6_TYPE_BIT_MASK \ + GENMASK(RISCV_DBTR_MC6_TYPE_BIT + 3, RISCV_DBTR_MC6_TYPE_BIT) + +#define RISCV_DBTR_SET_TDATA1_TYPE(_t1, _type) \ + ({ \ + typeof(_t1) (td1t1) =3D (_t1); \ + (td1t1) &=3D ~RISCV_DBTR_TDATA1_TYPE_BIT_MASK; \ + (td1t1) |=3D (((unsigned long)(_type) \ + << RISCV_DBTR_TDATA1_TYPE_BIT) \ + & RISCV_DBTR_TDATA1_TYPE_BIT_MASK); \ + (td1t1); \ + }) + +#define RISCV_DBTR_SET_MC_TYPE(_t1, _type) \ + ({ \ + typeof(_t1) (mct1) =3D (_t1); \ + (mct1) &=3D ~RISCV_DBTR_MC_TYPE_BIT_MASK; \ + (mct1) |=3D (((unsigned long)(_type) \ + << RISCV_DBTR_MC_TYPE_BIT) \ + & RISCV_DBTR_MC_TYPE_BIT_MASK); \ + (mct1); \ + }) + +#define RISCV_DBTR_SET_MC6_TYPE(_t1, _type) \ + ({ \ + typeof(_t1) (mc6t1) =3D (_t1); \ + (mc6t1) &=3D ~RISCV_DBTR_MC6_TYPE_BIT_MASK; \ + (mc6t1) |=3D (((unsigned long)(_type) \ + << RISCV_DBTR_MC6_TYPE_BIT) \ + & RISCV_DBTR_MC6_TYPE_BIT_MASK); \ + (mc6t1); \ + }) + +#define RISCV_DBTR_SET_MC_EXEC_BIT(_t1) \ + SET_DBTR_BIT(_t1, RISCV_DBTR_MC_EXEC_BIT) + +#define RISCV_DBTR_SET_MC_LOAD_BIT(_t1) \ + SET_DBTR_BIT(_t1, RISCV_DBTR_MC_LOAD_BIT) + +#define RISCV_DBTR_SET_MC_STORE_BIT(_t1) \ + SET_DBTR_BIT(_t1, RISCV_DBTR_MC_STORE_BIT) + +#define RISCV_DBTR_SET_MC_SIZELO(_t1, _val) \ + ({ \ + typeof(_t1) (mcslt1) =3D (_t1); \ + mcslt1 &=3D ~RISCV_DBTR_MC_SIZELO_BIT_MASK; \ + mcslt1 |=3D (((_val) << RISCV_DBTR_MC_SIZELO_BIT) \ + & RISCV_DBTR_MC_SIZELO_BIT_MASK); \ + (mcslt1); \ + }) + +#if __riscv_xlen >=3D 64 +#define RISCV_DBTR_SET_MC_SIZEHI(_t1, _val) \ + ({ \ + typeof(_t1) (mcsht1) =3D (_t1); \ + mcsht1 &=3D ~RISCV_DBTR_MC_SIZEHI_BIT_MASK; \ + mcsht1 |=3D (((_val) << RISCV_DBTR_MC_SIZEHI_BIT) \ + & RISCV_DBTR_MC_SIZEHI_BIT_MASK); \ + (mcsht1); \ + }) +#else +/* SIZEHI does not exist in the rv32 mcontrol layout; nothing to set. */ +#define RISCV_DBTR_SET_MC_SIZEHI(_t1, _val) ((void)(_val), (_t1)) +#endif + +#define RISCV_DBTR_SET_MC6_EXEC_BIT(_t1) \ + SET_DBTR_BIT(_t1, RISCV_DBTR_MC6_EXEC_BIT) + +#define RISCV_DBTR_SET_MC6_LOAD_BIT(_t1) \ + SET_DBTR_BIT(_t1, RISCV_DBTR_MC6_LOAD_BIT) + +#define RISCV_DBTR_SET_MC6_STORE_BIT(_t1) \ + SET_DBTR_BIT(_t1, RISCV_DBTR_MC6_STORE_BIT) + +#define RISCV_DBTR_SET_MC6_SIZE(_t1, _val) \ + ({ \ + typeof(_t1) (mc6szt1) =3D (_t1); \ + (mc6szt1) &=3D ~RISCV_DBTR_MC6_SIZE_BIT_MASK; \ + (mc6szt1) |=3D (((_val) << RISCV_DBTR_MC6_SIZE_BIT) \ + & RISCV_DBTR_MC6_SIZE_BIT_MASK); \ + (mc6szt1); \ + }) + +struct arch_hw_breakpoint { + unsigned long address; + unsigned long len; + unsigned int type; + + /* Trigger configuration data */ + unsigned long tdata1; + unsigned long tdata2; + unsigned long tdata3; +}; + +struct perf_event_attr; +struct notifier_block; +struct perf_event; +struct pt_regs; + +int hw_breakpoint_slots(int type); +int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw); +int hw_breakpoint_arch_parse(struct perf_event *bp, + const struct perf_event_attr *attr, + struct arch_hw_breakpoint *hw); +int hw_breakpoint_exceptions_notify(struct notifier_block *unused, + unsigned long val, void *data); + +void arch_enable_hw_breakpoint(struct perf_event *bp); +void arch_update_hw_breakpoint(struct perf_event *bp); +void arch_disable_hw_breakpoint(struct perf_event *bp); +int arch_install_hw_breakpoint(struct perf_event *bp); +void arch_uninstall_hw_breakpoint(struct perf_event *bp); +void hw_breakpoint_pmu_read(struct perf_event *bp); + +#else + +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ +#endif /* __RISCV_HW_BREAKPOINT_H */ diff --git a/arch/riscv/include/asm/kdebug.h b/arch/riscv/include/asm/kdebu= g.h index 85ac00411f6e..53e989781aa1 100644 --- a/arch/riscv/include/asm/kdebug.h +++ b/arch/riscv/include/asm/kdebug.h @@ -6,7 +6,8 @@ enum die_val { DIE_UNUSED, DIE_TRAP, - DIE_OOPS + DIE_OOPS, + DIE_DEBUG }; =20 #endif diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index cabb99cadfb6..590a280762c9 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) +=3D mcount-dyn.o =20 obj-$(CONFIG_PERF_EVENTS) +=3D perf_callchain.o obj-$(CONFIG_HAVE_PERF_REGS) +=3D perf_regs.o +obj-$(CONFIG_HAVE_HW_BREAKPOINT) +=3D hw_breakpoint.o obj-$(CONFIG_RISCV_SBI) +=3D sbi.o sbi_ecall.o ifeq ($(CONFIG_RISCV_SBI), y) obj-$(CONFIG_SMP) +=3D sbi-ipi.o diff --git a/arch/riscv/kernel/hw_breakpoint.c b/arch/riscv/kernel/hw_break= point.c new file mode 100644 index 000000000000..fc54a1a897c5 --- /dev/null +++ b/arch/riscv/kernel/hw_breakpoint.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Qualcomm Technologies, Inc. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Registered per-cpu bp/wp */ +static DEFINE_PER_CPU(struct perf_event *, pcpu_hw_bp_events[RISCV_HW_BP_N= UM_MAX]); +static DEFINE_PER_CPU(unsigned long, ecall_lock_flags); +static DEFINE_PER_CPU(raw_spinlock_t, ecall_lock); + +/* Per-cpu shared memory between S and M mode */ +static union sbi_dbtr_shmem_entry __percpu *sbi_dbtr_shmem; + +/* number of debug triggers on this cpu . */ +static int dbtr_total_num __ro_after_init; +static int dbtr_type __ro_after_init; +static int dbtr_init __ro_after_init; + +#define MEM_HI(_m) ((unsigned long)upper_32_bits(_m)) +#define MEM_LO(_m) ((unsigned long)lower_32_bits(_m)) + +static int arch_smp_setup_sbi_shmem(unsigned int cpu) +{ + union sbi_dbtr_shmem_entry *dbtr_shmem; + phys_addr_t shmem_pa; + struct sbiret ret; + + dbtr_shmem =3D per_cpu_ptr(sbi_dbtr_shmem, cpu); + if (!dbtr_shmem) { + pr_err("Invalid per-cpu shared memory for debug triggers\n"); + return -ENODEV; + } + + shmem_pa =3D per_cpu_ptr_to_phys(dbtr_shmem); + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM, + MEM_LO(shmem_pa), MEM_HI(shmem_pa), 0, 0, 0, 0); + + if (ret.error) { + pr_warn("%s: failed to setup shared memory. error: %ld\n", + __func__, ret.error); + return sbi_err_map_linux_errno(ret.error); + } + + pr_info("CPU %d: HW Breakpoint shared memory registered.\n", cpu); + + return 0; +} + +static int arch_smp_teardown_sbi_shmem(unsigned int cpu) +{ + struct sbiret ret; + + /* Disable shared memory */ + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM, + SBI_SHMEM_DISABLE, SBI_SHMEM_DISABLE, 0, 0, 0, 0); + + if (ret.error) + pr_warn("%s: failed to disable shared memory. error: %ld\n", + __func__, ret.error); + else + pr_info("CPU %d: HW Breakpoint shared memory disabled.\n", cpu); + + return 0; +} + +static void init_sbi_dbtr(void) +{ + unsigned long tdata1; + struct sbiret ret; + + if (sbi_probe_extension(SBI_EXT_DBTR) <=3D 0) { + pr_warn("SBI_EXT_DBTR is not supported\n"); + dbtr_total_num =3D 0; + goto done; + } + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS, + 0, 0, 0, 0, 0, 0); + if (ret.error) { + pr_warn("Failed to detect triggers\n"); + dbtr_total_num =3D 0; + goto done; + } + + tdata1 =3D 0; + tdata1 =3D RISCV_DBTR_SET_TDATA1_TYPE(tdata1, RISCV_DBTR_TRIG_MCONTROL6); + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS, + tdata1, 0, 0, 0, 0, 0); + if (ret.error) { + pr_warn("Failed to detect mcontrol6 triggers\n"); + } else if (!ret.value) { + pr_warn("Type 6 triggers not available\n"); + } else { + dbtr_total_num =3D ret.value; + dbtr_type =3D RISCV_DBTR_TRIG_MCONTROL6; + pr_warn("Mcontrol6 trigger available.\n"); + goto done; + } + + /* fallback to type 2 triggers if type 6 is not available */ + + tdata1 =3D 0; + tdata1 =3D RISCV_DBTR_SET_TDATA1_TYPE(tdata1, RISCV_DBTR_TRIG_MCONTROL); + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS, + tdata1, 0, 0, 0, 0, 0); + if (ret.error) { + pr_warn("Failed to detect mcontrol triggers\n"); + } else if (!ret.value) { + pr_warn("Type 2 triggers not available\n"); + } else { + dbtr_total_num =3D ret.value; + dbtr_type =3D RISCV_DBTR_TRIG_MCONTROL; + goto done; + } + +done: + dbtr_init =3D 1; +} + +int hw_breakpoint_slots(int type) +{ + /* + * We can be called early, so don't rely on + * static variables being initialised. + */ + + if (!dbtr_init) + init_sbi_dbtr(); + + return dbtr_total_num; +} + +int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw) +{ + unsigned int len; + unsigned long va; + + va =3D hw->address; + len =3D hw->len; + + return (va >=3D TASK_SIZE) && ((va + len - 1) >=3D TASK_SIZE); +} + +static int rv_init_mcontrol_trigger(const struct perf_event_attr *attr, + struct arch_hw_breakpoint *hw) +{ + switch (attr->bp_type) { + case HW_BREAKPOINT_X: + hw->type =3D RISCV_DBTR_EXEC; + RISCV_DBTR_SET_MC_EXEC_BIT(hw->tdata1); + break; + case HW_BREAKPOINT_R: + hw->type =3D RISCV_DBTR_LOAD; + RISCV_DBTR_SET_MC_LOAD_BIT(hw->tdata1); + break; + case HW_BREAKPOINT_W: + hw->type =3D RISCV_DBTR_STORE; + RISCV_DBTR_SET_MC_STORE_BIT(hw->tdata1); + break; + case HW_BREAKPOINT_RW: + hw->type =3D RISCV_DBTR_LDST; + RISCV_DBTR_SET_MC_LOAD_BIT(hw->tdata1); + RISCV_DBTR_SET_MC_STORE_BIT(hw->tdata1); + break; + default: + return -EINVAL; + } + + if (attr->bp_type =3D=3D HW_BREAKPOINT_X) { + /* + * Userspace debuggers can request execute breakpoints with + * bp_len =3D=3D 2 for compressed/non-aligned instruction + * addresses. Program execute triggers with "match any size" + * to avoid missing valid instruction fetches. + */ + hw->len =3D 0; + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZELO(hw->tdata1, 0); + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZEHI(hw->tdata1, 0); + } else { + switch (attr->bp_len) { + case HW_BREAKPOINT_LEN_1: + hw->len =3D 1; + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZELO(hw->tdata1, 1); + break; + case HW_BREAKPOINT_LEN_2: + hw->len =3D 2; + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZELO(hw->tdata1, 2); + break; + case HW_BREAKPOINT_LEN_4: + hw->len =3D 4; + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZELO(hw->tdata1, 3); + break; +#if __riscv_xlen >=3D 64 + case HW_BREAKPOINT_LEN_8: + hw->len =3D 8; + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZELO(hw->tdata1, 1); + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZEHI(hw->tdata1, 1); + break; +#endif + /* Set to match any size */ + default: + hw->len =3D 0; + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZELO(hw->tdata1, 0); + hw->tdata1 =3D RISCV_DBTR_SET_MC_SIZEHI(hw->tdata1, 0); + break; + } + } + + hw->tdata1 =3D RISCV_DBTR_SET_MC_TYPE(hw->tdata1, RISCV_DBTR_TRIG_MCONTRO= L); + + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_DMODE_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_TIMING_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_SELECT_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_ACTION_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_CHAIN_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_MATCH_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_M_BIT); + + SET_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_S_BIT); + SET_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC_U_BIT); + + return 0; +} + +static int rv_init_mcontrol6_trigger(const struct perf_event_attr *attr, + struct arch_hw_breakpoint *hw) +{ + switch (attr->bp_type) { + case HW_BREAKPOINT_X: + hw->type =3D RISCV_DBTR_EXEC; + RISCV_DBTR_SET_MC6_EXEC_BIT(hw->tdata1); + break; + case HW_BREAKPOINT_R: + hw->type =3D RISCV_DBTR_LOAD; + RISCV_DBTR_SET_MC6_LOAD_BIT(hw->tdata1); + break; + case HW_BREAKPOINT_W: + hw->type =3D RISCV_DBTR_STORE; + RISCV_DBTR_SET_MC6_STORE_BIT(hw->tdata1); + break; + case HW_BREAKPOINT_RW: + hw->type =3D RISCV_DBTR_LDST; + RISCV_DBTR_SET_MC6_STORE_BIT(hw->tdata1); + RISCV_DBTR_SET_MC6_LOAD_BIT(hw->tdata1); + break; + default: + return -EINVAL; + } + + if (attr->bp_type =3D=3D HW_BREAKPOINT_X) { + /* See rv_init_mcontrol_trigger() for rationale. */ + hw->len =3D 0; + hw->tdata1 =3D RISCV_DBTR_SET_MC6_SIZE(hw->tdata1, 0); + } else { + switch (attr->bp_len) { + case HW_BREAKPOINT_LEN_1: + hw->len =3D 1; + hw->tdata1 =3D RISCV_DBTR_SET_MC6_SIZE(hw->tdata1, 1); + break; + case HW_BREAKPOINT_LEN_2: + hw->len =3D 2; + hw->tdata1 =3D RISCV_DBTR_SET_MC6_SIZE(hw->tdata1, 2); + break; + case HW_BREAKPOINT_LEN_4: + hw->len =3D 4; + hw->tdata1 =3D RISCV_DBTR_SET_MC6_SIZE(hw->tdata1, 3); + break; +#if __riscv_xlen >=3D 64 + case HW_BREAKPOINT_LEN_8: + hw->len =3D 8; + hw->tdata1 =3D RISCV_DBTR_SET_MC6_SIZE(hw->tdata1, 5); + break; +#endif + /* Set to match any size */ + default: + hw->len =3D 0; + hw->tdata1 =3D RISCV_DBTR_SET_MC6_SIZE(hw->tdata1, 0); + } + } + + hw->tdata1 =3D RISCV_DBTR_SET_MC6_TYPE(hw->tdata1, RISCV_DBTR_TRIG_MCONTR= OL6); + + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_DMODE_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_TIMING_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_SELECT_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_ACTION_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_CHAIN_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_MATCH_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_M_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_VS_BIT); + CLEAR_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_VU_BIT); + + SET_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_S_BIT); + SET_DBTR_BIT(hw->tdata1, RISCV_DBTR_MC6_U_BIT); + + return 0; +} + +int hw_breakpoint_arch_parse(struct perf_event *bp, + const struct perf_event_attr *attr, + struct arch_hw_breakpoint *hw) +{ + int ret; + + /* Breakpoint address */ + hw->address =3D attr->bp_addr; + hw->tdata2 =3D attr->bp_addr; + hw->tdata3 =3D 0x0; + + switch (dbtr_type) { + case RISCV_DBTR_TRIG_MCONTROL: + ret =3D rv_init_mcontrol_trigger(attr, hw); + break; + case RISCV_DBTR_TRIG_MCONTROL6: + ret =3D rv_init_mcontrol6_trigger(attr, hw); + break; + default: + pr_warn("Unsupported trigger type\n"); + ret =3D -EOPNOTSUPP; + break; + } + + return ret; +} + +/* + * HW Breakpoint/watchpoint handler + */ +static int hw_breakpoint_handler(struct die_args *args) +{ + int ret =3D NOTIFY_DONE; + struct arch_hw_breakpoint *bp; + struct perf_event *event; + int i; + + for (i =3D 0; i < dbtr_total_num; i++) { + event =3D this_cpu_read(pcpu_hw_bp_events[i]); + if (!event) + continue; + + bp =3D counter_arch_bp(event); + switch (bp->type) { + /* Breakpoint */ + case RISCV_DBTR_EXEC: + if (bp->address =3D=3D args->regs->epc) { + perf_bp_event(event, args->regs); + ret =3D NOTIFY_STOP; + } + break; + + /* Watchpoint */ + case RISCV_DBTR_LOAD: + case RISCV_DBTR_STORE: + case RISCV_DBTR_LDST: + { + unsigned long stval =3D args->regs->badaddr; + unsigned long bp_start =3D bp->address; + unsigned long bp_len =3D bp->len ?: 1; + unsigned long bp_end =3D bp_start + bp_len - 1; + unsigned long stval_end =3D stval + sizeof(long) - 1; + unsigned long tdata1; + bool hit =3D false; + struct sbiret sret; + union sbi_dbtr_shmem_entry *shmem; + + if (bp_end < bp_start) + bp_end =3D ~0UL; + if (stval_end < stval) + stval_end =3D ~0UL; + + /* + * Prefer tdata1.hit from SBI trigger readout whenever + * possible. Fall back to address-based matching if HIT + * isn't observed/supported. + */ + raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + shmem =3D this_cpu_ptr(sbi_dbtr_shmem); + sret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_READ, + i, 1, 0, 0, 0, 0); + if (!sret.error) { + tdata1 =3D le_to_cpu(shmem->data.tdata1); + + if (dbtr_type =3D=3D RISCV_DBTR_TRIG_MCONTROL) + hit =3D !!(tdata1 & RISCV_DBTR_MC_HIT_BIT_MASK); + else if (dbtr_type =3D=3D RISCV_DBTR_TRIG_MCONTROL6) + hit =3D !!(tdata1 & RISCV_DBTR_MC6_HIT_BIT_MASK); + } + raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + + /* + * Sdtrig may report STVAL as the lowest accessed + * address while the watchpoint can match a higher byte + * in the same access. + */ + if (hit || + (stval >=3D bp_start && stval <=3D bp_end) || + (bp_start >=3D stval && bp_start <=3D stval_end)) { + perf_bp_event(event, args->regs); + ret =3D NOTIFY_STOP; + } + break; + } + + default: + pr_warn("Unknown type: %u\n", bp->type); + break; + } + } + + return ret; +} + +int hw_breakpoint_exceptions_notify(struct notifier_block *unused, + unsigned long val, void *data) +{ + if (val !=3D DIE_DEBUG) + return NOTIFY_DONE; + + return hw_breakpoint_handler(data); +} + +/* atomic: counter->ctx->lock is held */ +int arch_install_hw_breakpoint(struct perf_event *event) +{ + struct arch_hw_breakpoint *bp =3D counter_arch_bp(event); + union sbi_dbtr_shmem_entry *shmem =3D this_cpu_ptr(sbi_dbtr_shmem); + struct sbi_dbtr_data_msg *xmit; + struct sbi_dbtr_id_msg *recv; + struct perf_event **slot; + unsigned long idx; + struct sbiret ret; + int err =3D 0; + + raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + + xmit =3D &shmem->data; + recv =3D &shmem->id; + xmit->tdata1 =3D cpu_to_le(bp->tdata1); + xmit->tdata2 =3D cpu_to_le(bp->tdata2); + xmit->tdata3 =3D cpu_to_le(bp->tdata3); + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_INSTALL, + 1, 0, 0, 0, 0, 0); + + if (ret.error) { + pr_warn("Failed to install trigger\n"); + err =3D sbi_err_map_linux_errno(ret.error); + goto done; + } + + idx =3D le_to_cpu(recv->idx); + if (idx >=3D dbtr_total_num) { + pr_warn("Invalid trigger index %lu\n", idx); + err =3D -EINVAL; + goto done; + } + + slot =3D this_cpu_ptr(&pcpu_hw_bp_events[idx]); + if (*slot) { + pr_warn("Slot %lu is in use\n", idx); + err =3D -EBUSY; + goto done; + } + + /* Save the event - to be looked up in handler */ + *slot =3D event; + +done: + raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + return err; +} + +/* atomic: counter->ctx->lock is held */ +void arch_uninstall_hw_breakpoint(struct perf_event *event) +{ + struct sbiret ret; + int i; + + raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + + for (i =3D 0; i < dbtr_total_num; i++) { + struct perf_event **slot =3D this_cpu_ptr(&pcpu_hw_bp_events[i]); + + if (*slot =3D=3D event) { + *slot =3D NULL; + break; + } + } + + if (i =3D=3D dbtr_total_num) { + pr_warn("Breakpoint not installed.\n"); + goto out; + } + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UNINSTALL, + i, 1, 0, 0, 0, 0); + + if (ret.error) { + pr_warn("Failed to uninstall trigger %d.\n", i); + goto out; + } + + out: + raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); +} + +void arch_enable_hw_breakpoint(struct perf_event *event) +{ + struct sbiret ret; + int i; + struct perf_event **slot; + + raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + + for (i =3D 0; i < dbtr_total_num; i++) { + slot =3D this_cpu_ptr(&pcpu_hw_bp_events[i]); + + if (*slot =3D=3D event) + break; + } + + if (i =3D=3D dbtr_total_num) { + pr_warn("Breakpoint not installed.\n"); + goto out; + } + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_ENABLE, + i, 1, 0, 0, 0, 0); + + if (ret.error) { + pr_warn("Failed to install trigger %d\n", i); + goto out; + } + + out: + raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); +} +EXPORT_SYMBOL_GPL(arch_enable_hw_breakpoint); + +void arch_update_hw_breakpoint(struct perf_event *event) +{ + struct arch_hw_breakpoint *bp =3D counter_arch_bp(event); + union sbi_dbtr_shmem_entry *shmem =3D this_cpu_ptr(sbi_dbtr_shmem); + struct sbi_dbtr_data_msg *xmit; + struct perf_event **slot; + struct sbiret ret; + int i; + + for (i =3D 0; i < dbtr_total_num; i++) { + slot =3D this_cpu_ptr(&pcpu_hw_bp_events[i]); + + if (*slot =3D=3D event) + break; + } + + if (i =3D=3D dbtr_total_num) { + pr_warn("Breakpoint not installed.\n"); + return; + } + + raw_spin_lock_irqsave(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); + + xmit =3D &shmem->data; + xmit->tdata1 =3D cpu_to_le(bp->tdata1); + xmit->tdata2 =3D cpu_to_le(bp->tdata2); + xmit->tdata3 =3D cpu_to_le(bp->tdata3); + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_UPDATE, + i, 1, 0, 0, 0, 0); + if (ret.error) + pr_warn("Failed to update trigger %d.\n", i); + + raw_spin_unlock_irqrestore(this_cpu_ptr(&ecall_lock), + *this_cpu_ptr(&ecall_lock_flags)); +} +EXPORT_SYMBOL_GPL(arch_update_hw_breakpoint); + +void arch_disable_hw_breakpoint(struct perf_event *event) +{ + struct perf_event **slot; + struct sbiret ret; + int i; + + for (i =3D 0; i < dbtr_total_num; i++) { + slot =3D this_cpu_ptr(&pcpu_hw_bp_events[i]); + + if (*slot =3D=3D event) + break; + } + + if (i =3D=3D dbtr_total_num) { + pr_warn("Breakpoint not installed.\n"); + return; + } + + ret =3D sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIG_DISABLE, + i, 1, 0, 0, 0, 0); + + if (ret.error) { + pr_warn("Failed to uninstall trigger %d.\n", i); + return; + } +} +EXPORT_SYMBOL_GPL(arch_disable_hw_breakpoint); + +void hw_breakpoint_pmu_read(struct perf_event *bp) { } + +void flush_ptrace_hw_breakpoint(struct task_struct *tsk) { } + +static int __init arch_hw_breakpoint_init(void) +{ + unsigned int cpu; + int rc =3D 0; + + for_each_possible_cpu(cpu) + raw_spin_lock_init(&per_cpu(ecall_lock, cpu)); + + if (!dbtr_init) + init_sbi_dbtr(); + + if (dbtr_total_num) { + pr_info("Total number of type %d triggers: %u\n", + dbtr_type, dbtr_total_num); + } else { + pr_info("No hardware triggers available\n"); + goto out; + } + + /* Allocate per-cpu shared memory */ + sbi_dbtr_shmem =3D __alloc_percpu(sizeof(*sbi_dbtr_shmem) * dbtr_total_nu= m, + PAGE_SIZE); + + if (!sbi_dbtr_shmem) { + pr_warn("Failed to allocate shared memory.\n"); + rc =3D -ENOMEM; + goto out; + } + + /* Hotplug handler to register/unregister shared memory with SBI */ + rc =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "riscv/hw_breakpoint:prepare", + arch_smp_setup_sbi_shmem, + arch_smp_teardown_sbi_shmem); + + if (rc < 0) { + pr_warn("Failed to setup CPU hotplug state\n"); + free_percpu(sbi_dbtr_shmem); + return rc; + } + out: + return rc; +} +arch_initcall(arch_hw_breakpoint_init); diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index 8c62c771a656..029fd66a285e 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -286,6 +286,12 @@ void handle_break(struct pt_regs *regs) if (probe_breakpoint_handler(regs)) return; =20 +#ifdef CONFIG_HAVE_HW_BREAKPOINT + if (notify_die(DIE_DEBUG, "EBREAK", regs, 0, regs->cause, SIGTRAP) + =3D=3D NOTIFY_STOP) + return; +#endif + current->thread.bad_cause =3D regs->cause; =20 if (user_mode(regs)) --=20 2.43.0 From nobody Fri Oct 2 08:26:24 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.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 B3791415F3F for ; Mon, 3 Aug 2026 13:49:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764979; cv=none; b=nqAeQXtHR2v5X9ZukdzPyM01qiTUVJecCv3W2FMIxk0i67ThUjZG8h9j7jS7ZaLGBwTbRrVtLqKgj+JL2zz9tpPNLK/NGMbFkGB8ITm/Q7qlytESQx9NEUJW1dycY9x7Jb+DHww1J6OFdBQCyZ6DWhzvmPCuuOzJiq2B014P+YA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764979; c=relaxed/simple; bh=pMPMtfZB8VBITwn9uHDlV1BkaeFTtWFb4ZIRKS14VJ0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ncej+1Y8Lq9nN6K72e6CUkO33ub8l4GTUN56mMrfLezOSILtIjRl1HIhsSGaO/VQoP/FWN01H+Fns8swROpNzcveH7kLj4KrQ9Il1EHcQnfSrPiW/JOokxWNV84r3SErMltmgtCMchCEBOkcrllVol2791G3hBTxpnrexaayKqk= 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=PcvdoPEz; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=RRClV0v0; arc=none smtp.client-ip=205.220.180.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="PcvdoPEz"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="RRClV0v0" Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 673DTLoW822698 for ; Mon, 3 Aug 2026 13:49:36 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=JDL8L5Ufeca w9rYAA5nxqMzomuFEvy76+Mr0sJjLj5Y=; b=PcvdoPEzsECcN+Uj3R7ZuUQZAsQ sUl9+c/jhDZAbgmcnyV8qwbX6rssqi6ITatWqFwUDx3owcOXirwn7mIbt70zkdjL optjzwOPiRf9CRatpuOTkufgwDeHOEexD7mCySsLgL4+O3kYsPBLTW9omhI9cNT2 2gc7b7Hdk6jj8edP6GONDUwltrNiDma2QUh6ZeUyZubCCttp14dN8wVecbCyh8En vWMjBjGo410zRizW5Ie4/MVFi7nGCpQl+G+iWFOrfG2uhA5VsstGeO2z4QcGoNkW yLSAiW5wx3zLzIh12TBGlay6xzyfXzPWrDymNOTD8bvEDVJsGVJwsL0LQfw== Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ftnrt9u0n-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 03 Aug 2026 13:49:36 +0000 (GMT) Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-2cca5e0a0c9so60762255ad.1 for ; Mon, 03 Aug 2026 06:49:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1785764975; x=1786369775; 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=JDL8L5Ufecaw9rYAA5nxqMzomuFEvy76+Mr0sJjLj5Y=; b=RRClV0v0+MgbfrYC3QlO3f9N347rB5erI3OtH1Fj8FVG5dJkJSG6H9z1ZoIdTVel0Q 0JqyGMUTkFSIKG6bbgQ9wuZCKFHaV8MdVkfLgPqC0twOh+IA74Vt0cPUtuue2CjOBDOJ FnVNUO9fNtFhoe/WTNBBRsbTA8M8809ULCOG6gDTpavJrc8lzYdl2fKGjfOaG5PqlL15 iKbrUuMb7bkKv3MxI3KmVOn8JYK9fw9i8BmwkD986Mfn/6bfE3tZ+V1MB/jwBiOGeljN /YA8FoDdZk5VfE7AA8P05LqfwB5mv7jXgUKnR2yr5ur4uygXmGSeJULd17F4DVN0mk7t cu8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785764975; x=1786369775; 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=JDL8L5Ufecaw9rYAA5nxqMzomuFEvy76+Mr0sJjLj5Y=; b=JEwOSfuSkXGWteXB0WvL5NNpitLc4RpoHPAUL/h8Q62ZtzUZ6IEFoGgPkVUPCshnS4 FNc29vlw4DTxaMW9FrJoPn5Xysf/0jBepNZDRggzLi3JGH6VEzcIIQ1HqAi3CdzIVePu V/0w4Z2WM9QEzd6GG5clfTRCUaPkLPAT2c/Z2BCjXaCEKUsBdt1ZE5PfAjl4ErkY+k5D d2MLgBs6mdQlxZoomKBXf/5frfayHt6RLNwURasi+pCirH3bTqsEQXxzGgq4gK3HeU+H 3V207+K5zyFo+8VhJpKo6khjODpHXqlOUUWnnwGqT07tHwxu0VGFPPZdQRC5RO3SoWgQ E8nA== X-Forwarded-Encrypted: i=1; AHgh+Rq/0fxqk4nY+nypVWJacpfAi5gjsE6ziY1sZg/MexNUjaE57ewLZHJFaf/Z8VTH0vzF7wcrWRpwFFUwq6I=@vger.kernel.org X-Gm-Message-State: AOJu0YwEbejPwMtC/UkYDFgFBojakqT9qDM7zksO6vuf9BLDTuqnOWsq Nx7LzI1++86RISzBPG6gykvcy7KMX5QrwbNuDHMHCkVY3RWFHF38xkF+T8Tyhd5yK7dlNSDRG7d tCdNdZJqryATiPnNXSWfVddNm7B5/FnakFLeFd/S8fUpOXDZUJbEiKCa7B1zOYF2AT8M= X-Gm-Gg: AR+sD13Xk9uIvtbLCngxV2OK+7gYEgbjvII8SkxAYNRnUCtuQ4rgkvs4ebF6NKhPn0H zEDST95Nrtqfgrzqsx4tFsOdRkHEPF5bgH28wKe3dc2iBHkxh5Iv/SH+Km3/ShuqFwwcnSjCyus weAXh4lsxhgVfIbGcVKN2+Y49YFP24rqGQC8XTnucQIzRO0/0eeuoWFPIKwjvsqkO9mGR9VZ/rC WBijLvEjI3WyvyDnsYlSfIPOIO7G4M6qhP4vvd0PtBwShI8GXuP+XjTFyTm3PNONoztfXKCfH5h woK05GfLe/MohnwhszlMIdxyQSemGKTD0wgUk2oJYsl1miIOXXFPB/2wMzGC52m914F/TmDGcGd QNA6qJFWqA6rwhQbcgLStlgxnsxEHp8mrBQKhk0qtN8ns9TR9YDC0VQcA1vFFU267sA+P0nFKwg lpJg+lz7TaW2GYyfpjuMB9ySsD X-Received: by 2002:a05:6a20:431f:b0:3c3:875d:c52f with SMTP id adf61e73a8af0-3c92a540ceemr9489633637.10.1785764975350; Mon, 03 Aug 2026 06:49:35 -0700 (PDT) X-Received: by 2002:a05:6a20:431f:b0:3c3:875d:c52f with SMTP id adf61e73a8af0-3c92a540ceemr9489614637.10.1785764974876; Mon, 03 Aug 2026 06:49:34 -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.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 06:49:34 -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 2/5] riscv: Add breakpoint and watchpoint test for riscv Date: Mon, 3 Aug 2026 19:19:10 +0530 Message-ID: <20260803134913.2013674-3-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: quoted-printable X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX/kKjgG9a4qE0 xpBerMrOkb+C9HOpUw426nMoU6n/Gre+hRTJgX5PJTUv+hE2FyjEzvahAnxDN1NVHjdBr9YMpL0 VA67UoiZqvkau/xNb6PIl6NlPVABYiZ9ucYerYnbY0bO3+mqPQcVy05tykcd92qyvvoM7IFwcuX SZHPaDyKjeCKM9gwK0fUmKZZkCWtKf0+F7vrmAXC2PkN9BNxjbCZmcwQr3oB+P96AWiaU50jyU5 mD0hu69vg502gJNyVdx3PqrQGfENSYwzeS8yRgIqjlAKnAR7V1PAFdRXX7xYk8brqw7svvG9NT1 6dsi3/tlq9LrBod8w6+nbXSdyNi3goi65lluCpsKOX8ZeQ1yOI16aX4pgMyxdLwvb9M6d6tC3Oe JoO05KFvvOFUbQAjbdAz03D/Nr8Hip1F0dj6MvHALtQ8/HMixBqHzT/eVvAyqr02ImtQdhlx/X0 KF9rhdPgu4uzIeDKqOg== X-Proofpoint-GUID: LDyaRZoHU6ZtvJckHu_xHIB3NN4u4pFA X-Proofpoint-ORIG-GUID: LDyaRZoHU6ZtvJckHu_xHIB3NN4u4pFA X-Proofpoint-Spam-Info: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX+g43Qn37uUY3 oInD2wkJigl7/9itvcmyKWQp1a3wQcio2j3m0whWTaFNy3/wwjI88EZMJpzS5v2JMIKDE9+GiGb PoQytFNi6yg9XtD6m5mdsE+U1S8KNAI= X-Authority-Analysis: v=2.4 cv=Ft81OWrq c=1 sm=1 tr=0 ts=6a709c70 cx=c_pps a=cmESyDAEBpBGqyK7t0alAg==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=gowsoOTTUOVcmtlkKump:22 a=EUspDBNiAAAA:8 a=qd3H3YWZb18LjlhzOR0A:9 a=1OuFwYUASf3TG4hYMiVC: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 malwarescore=0 impostorscore=0 lowpriorityscore=0 clxscore=1015 adultscore=0 phishscore=0 bulkscore=0 suspectscore=0 priorityscore=1501 spamscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608030124 Content-Type: text/plain; charset="utf-8" Add self test for riscv architecture. It uses ptrace to ptrace framework to set/unset break/watchpoint and uses signals to check triggers. Also add $(KHDR_INCLUDES) to CFLAGS for the riscv test build so the UAPI headers exercised by the test can be located during compilation. Signed-off-by: Himanshu Chauhan --- tools/testing/selftests/breakpoints/Makefile | 5 + .../breakpoints/breakpoint_test_riscv.c | 219 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 tools/testing/selftests/breakpoints/breakpoint_test_ris= cv.c diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/s= elftests/breakpoints/Makefile index 0b8f5acf7c78..b32c56b3c1db 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile @@ -12,5 +12,10 @@ ifneq (,$(filter $(ARCH),aarch64 arm64)) TEST_GEN_PROGS +=3D breakpoint_test_arm64 endif =20 +ifneq (,$(filter $(ARCH),riscv)) +CFLAGS +=3D -static $(KHDR_INCLUDES) +TEST_GEN_PROGS +=3D breakpoint_test_riscv +endif + include ../lib.mk =20 diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_riscv.c b/= tools/testing/selftests/breakpoints/breakpoint_test_riscv.c new file mode 100644 index 000000000000..0649940b709e --- /dev/null +++ b/tools/testing/selftests/breakpoints/breakpoint_test_riscv.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Qualcomm Technologies, Inc. + * + * Author: Himanshu Chauhan + */ + +#define _GNU_SOURCE +#include /* Definition of PERF_* constants */ +#include /* Definition of HW_* constants */ +#include /* Definition of SYS_* constants */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef noinline +#define noinline __attribute__((noinline)) +#endif + +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 =3D 5; + +int setup_bp(bool is_x, void *addr, int sig) +{ + struct perf_event_attr pe; + int fd; + + memset(&pe, 0, sizeof(struct perf_event_attr)); + pe.type =3D PERF_TYPE_BREAKPOINT; + pe.size =3D sizeof(struct perf_event_attr); + + pe.config =3D 0; + pe.bp_type =3D is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; + pe.bp_addr =3D (unsigned long)addr; + pe.bp_len =3D sizeof(long); + + pe.sample_period =3D 1; + pe.sample_type =3D PERF_SAMPLE_IP; + pe.wakeup_events =3D 1; + + pe.disabled =3D 1; + pe.exclude_kernel =3D 1; + pe.exclude_hv =3D 1; + + fd =3D syscall(SYS_perf_event_open, &pe, 0, -1, -1, 0); + if (fd < 0) { + printf("Failed to open event: %llx\n", pe.config); + return -1; + } + + fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK | O_ASYNC); + fcntl(fd, F_SETSIG, sig); + fcntl(fd, F_SETOWN, getpid()); + + ioctl(fd, PERF_EVENT_IOC_RESET, 0); + + return fd; +} + +static void sig_handler_bp(int signum, siginfo_t *oh, void *uc) +{ + int ret; + + bp_triggered++; + + printf("Breakpoint triggered!\n"); + ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); + ret =3D sem_post(&ib_mtx); + if (ret) { + printf("Failed to report BP success\n"); + return; + } +} + +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 =3D sem_post(&wp_mtx); + + if (ret) { + printf("Failed to report WP success\n"); + return; + } +} + +/* + * Keep a real instruction address for HW execute breakpoints: prevent inl= ining + * 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; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction =3D (void *)sig_handler_bp; + sa.sa_flags =3D SA_SIGINFO; + + if (sigaction(SIGIO, &sa, NULL) < 0) { + printf("Failed to setup signal handler\n"); + return -1; + } + + gfd =3D setup_bp(1, test_func, SIGIO); + + if (gfd < 0) { + printf("Failed to setup breakpoint.\n"); + return -1; + } + + ioctl(gfd, PERF_EVENT_IOC_ENABLE, 0); + + test_func(); + + ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); + + close(gfd); + + return 0; +} + +static int trigger_wp(void) +{ + struct sigaction sa; + unsigned long test_data; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction =3D (void *)sig_handler_wp; + sa.sa_flags =3D SA_SIGINFO; + + if (sigaction(SIGUSR1, &sa, NULL) < 0) { + printf("Failed to setup signal handler\n"); + return -1; + } + + gfd =3D setup_bp(0, &test_data, SIGUSR1); + + if (gfd < 0) { + printf("Failed to setup watchpoint\n"); + return -1; + } + + ioctl(gfd, PERF_EVENT_IOC_ENABLE, 0); + test_data =3D 0xdeadbeef; + ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); + + return 0; +} + +static int wait_event(sem_t *sem, const char *name) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts)) { + printf("%s: Failed to get current time\n", name); + return -1; + } + + /* + * Deadlock fix: avoid blocking forever on sem_wait() if the breakpoint/ + * watchpoint signal never arrives. Use a bounded wait and fail the test + * on timeout instead. + */ + ts.tv_sec +=3D wait_timeout_sec; + if (!sem_timedwait(sem, &ts)) + return 0; + + if (errno =3D=3D ETIMEDOUT) + printf("%s: Timed out waiting for event\n", name); + else + printf("%s: sem_timedwait() failed with %d\n", name, errno); + + return -1; +} + +int main(int argc, char *argv[]) +{ + sem_init(&ib_mtx, 0, 0); + if (trigger_bp() < 0) + return -1; + if (wait_event(&ib_mtx, "Breakpoint") < 0) + return -1; + + if (bp_triggered) + printf("Breakpoint test passed!\n"); + + sem_init(&wp_mtx, 0, 0); + if (trigger_wp() < 0) + return -1; + if (wait_event(&wp_mtx, "Watchpoint") < 0) + return -1; + + if (wp_triggered) + printf("Watchpoint test passed!\n"); + + return 0; +} --=20 2.43.0 From nobody Fri Oct 2 08:26:24 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.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 68BDF427FBF for ; Mon, 3 Aug 2026 13:49:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764984; cv=none; b=CK9sYcVsjHQQ7p31Q8DIEyQ1CBYySCqchPjsaOvy8ZMiIXmLZO0cCohf66Uylzu2LF3H+FMK4qBSKmDj9odFVXwpyR1XTXE5yOGaMnPPHA65bfqU79OxqEf1dgEkK4FshH1BjeewLLL5DapD4jffzIEJx2/5BDzKPaXrvUarga0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764984; c=relaxed/simple; bh=Gx/vzQbXxz5fKEOJCe7KCzFtQ6A2pv+jIigravb6UfQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c2Pc9E1dLd5sFlR2erFDLymb7JrYWsBXZDfeNmWpFlme7VQEgyAbUfa524DIhQt8+7aeoTc1hs77OpoqQGXS65rtLVxy5uKx1lddBglndHBR9JXZZRVmxgAs0lZ0DZNvJSDcB1y2933A4cY9EjVadiEQnE69HtxVKUYUz6sNlDU= 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=DAsXVb2O; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=H115HbMm; arc=none smtp.client-ip=205.220.180.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="DAsXVb2O"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="H115HbMm" Received: from pps.filterd (m0279869.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 673DRf9W1030687 for ; Mon, 3 Aug 2026 13:49:41 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=tSm3POIbTYQ aPNVT8gJP0iNLehnmNMTeziE3mIVlMU4=; b=DAsXVb2OtUU7haELooEDYHQVj9J Ak0Sx3CBMxQH66/IwtTBPnbFfnudccKmZ/qwc7nx9OV/elzbNCq2p5aYmwocexIp +esRJ3vNvos+IsQXSKohbr4juv7ie52k6M4+Vpg0KMnMkaxJgoSdUne51prOOK4D GZFL4oFg66wxUom0G/ABOMU39TF0NbcchP3qeXzaZ65MxteMCxIzGom3loEFDru3 0/bja6FS3c4oiY7qdHddi6umiSUg5TYqZv59e8BfWLsFY62VH4esq4wZvM0+TYHT 03QCnziGocsqbB4YIKN12QkWoJeVweHehIIHvA6vN9JEJTSoyRwOjYfJICQ== Received: from mail-pl1-f200.google.com (mail-pl1-f200.google.com [209.85.214.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ftnj79thc-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 03 Aug 2026 13:49:41 +0000 (GMT) Received: by mail-pl1-f200.google.com with SMTP id d9443c01a7336-2cc7e86e7c5so66759125ad.3 for ; Mon, 03 Aug 2026 06:49:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1785764980; x=1786369780; 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=tSm3POIbTYQaPNVT8gJP0iNLehnmNMTeziE3mIVlMU4=; b=H115HbMmwV2J3bAzMgmhd/HBlIdlXFjaTqFstd1YyphIDbDdj8nsA5TN3Fv5E+W6kz d4J6r2pOULMqCocDKzoGS//7kyL0a6UbFuetFPp4jB7tTX/0mdfY6k8CVRIDX9N8WEj4 CdjhcZk4NoUeGbk5I9USAVu4FEOy/qzguNWKbIBJKx1hcDYmioJRjjEmLaZyMnrozD+1 +mEasPt1Sr+YW5GsC7cLFgFnLM6xHqrEEfZD3+IIKo9K+v/LFUXVaOcdixzzhTm9qSNp Vsj92nhqD27eLpeBifpm68TK3Bg7IYrqPUWjPbTZygyQSK0zi/rSHy1loqZk6tJfezW0 0C7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785764980; x=1786369780; 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=tSm3POIbTYQaPNVT8gJP0iNLehnmNMTeziE3mIVlMU4=; b=jAiQ9P3Nw4EdOk9cf/ffXR+RPFmWDKc2/vAAUG5I8WIzYWqim82sgVb+L0+MheZrhm YzWgLYupiDHvEyQAnZi4GDRlYe6ke9pVc0qRnVtybb33BeXygzmNRJsyWvu33q/foECN ghe37iikIwf/5hT0hRH8obUDVTQuqqLiwYTuBf15S/TFqSbfWOeg9qhjFHhomodZ1LrN RiTZ2nWlwFLZFu8dJ9ZaT3SMW586gcmXAyJ6j9ktAOSbk1EjCCG39KcKAhyAZgrArntG ITf6BRH4/fyPSzy79wofF6Y7qIoPRphMmd6cfBzApxvTfLP0gr01Nu1nQbajKrC5zCyb GlEQ== X-Forwarded-Encrypted: i=1; AHgh+Rrc9DWVMmp163FX3LUzu7vz9qjmbmNBNjEv5PVgYHWksnt0dRYCCFyC7fG1kqZM5nq3egdLeaxSSwsnksw=@vger.kernel.org X-Gm-Message-State: AOJu0YzgZgZ+wR77juofcEPl1sFFWeA9Ip9vPpnzYKgKBBeyFTqWJ86P Br4NLgH/0s2uFImPtF4igkX4+ogPWcEabWVjl9Y2vaKmjBLjkyz+AZataDlf9PvknxL0poWkz7e JZXOs3yrjlv5rUI/QC5x+lLmoVSb+f59iAgoz8IKiGSX6rd+9WRuaPsmJTZGzszq98m0= X-Gm-Gg: AR+sD10EmEwGZm/1RPGpOB7gNGM1sihHaX/PGbB4jCZyrYNnXxv3OvIjJNjEzAgI48t maQkMFBX1W1Ixaz7UcZH8hNL/rIEEjinSqMgb9o6tJRQIVaoOK72LbO8ZqB6u2pwC5p8QseEbeM faUYargfnDQZA7fd10e+Gjk+PncbctyxBonOi8C1MK4qxUYTX7NhUoc+u+4+XQo8MvFhDz/6RRY yv/nk9JVyxoSQCy3rLmpMa7Ep1R9piXEhXnHr9Bc+ERsYu2rNKlORIvokKagi2gpztg+bAHO1fs gltqKXnrZeN2HlySZ/wSPpzgYRr2DpQjkmySkYdLJWyMTKwAQ1KPBi2VtYxfYWk8AkDS2D/RWMW ySGor5qP98ED6+m6uknKBN3dEZhhw8GR4dtByDaoNmuZn/vV9aSkEDLFahDuCqJvcY66aT1P1IR rleoiSXwYHfq1X1ki7s3L6GVZP X-Received: by 2002:a05:6a21:7109:b0:3c3:856e:dccd with SMTP id adf61e73a8af0-3c92a628e22mr9670085637.23.1785764979653; Mon, 03 Aug 2026 06:49:39 -0700 (PDT) X-Received: by 2002:a05:6a21:7109:b0:3c3:856e:dccd with SMTP id adf61e73a8af0-3c92a628e22mr9670044637.23.1785764979095; Mon, 03 Aug 2026 06:49:39 -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.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 06:49:38 -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 3/5] riscv: ptrace support for hardware break/watchpoints Date: Mon, 3 Aug 2026 19:19:11 +0530 Message-ID: <20260803134913.2013674-4-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: quoted-printable X-Proofpoint-ORIG-GUID: OPoyT3VgewT2ogC7nWhsd1Hnam7DpmPt X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfXzPqDcu8Efhcf Ljs5W4WGmyXDrB+wS3bjVABevVmaK9TiZ5jijZC1vs22/S5tNKDC4SXpRqSv/w4upsFgs5NwQVc uHK1h+g9ZXa31zcEAKIcJ5C9L1fSSEnzvOzUziZcYC+J4Zb7/LSaXMInHZ8khZlkcspMLhBj83B 5zbM8b9jRXtaxqL/bp1Rseb6l+R7e9EtS9YGrxXxuLPEbPlGCfBKnB2bvZn5P7E1yLTL9BI+jky DcHAv2L5yMEqGVTTxnEFy93HKE7s9TgWI6+XruXFT/Mu18uck313VF7cxiw+z4ORnjCK6MxMDqq JMWj1N9+/X5x/kEcNS8QVbtogtsAOEwFQKuXtHLK/rHx5Ms+Bb4NdA1h4BgNA43LuCEfsCkKyeN quQpsjSqDHwAokJrIsQt9Az7aDFqsUOOLHug0pIu+w8kKQjudtTHzBml8MajV4uH+FfMXVwNh7j eurjFB4AYV2mEFfmM0w== X-Proofpoint-GUID: OPoyT3VgewT2ogC7nWhsd1Hnam7DpmPt X-Authority-Analysis: v=2.4 cv=PqSjqQM3 c=1 sm=1 tr=0 ts=6a709c75 cx=c_pps a=IZJwPbhc+fLeJZngyXXI0A==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_glEPmIy2e8OvE2BGh3C:22 a=EUspDBNiAAAA:8 a=ZfCFiO8h5mzlEIWN8m8A:9 a=uG9DUKGECoFWVXl0Dc02:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX3QLLoVlXm9HL IckOwno32WaGMVPWoM3IVynraZpfc8Kd7RoFEvU01Ofao7iX06C1PeTobZ8wgQo3qJAs7bdcF2i 1PmF7ttztbyDx+upp7rchc1lSUScS54= 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 lowpriorityscore=0 phishscore=0 spamscore=0 priorityscore=1501 adultscore=0 clxscore=1015 impostorscore=0 suspectscore=0 bulkscore=0 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608030124 Content-Type: text/plain; charset="utf-8" Add ptrace support for hardware breakpoints and watchpoints on RISC-V. Debuggers can now set and query hardware debug triggers through the standard PTRACE_GETREGSET/SETREGSET interface using new NT_RISCV_HW_BREAK/WATCH note types, backed by register_user_hw_breakpoint()/modify_user_hw_breakpoint() and delivering SIGTRAP/TRAP_HWBKPT to the tracee when a trigger fires. For convenience, also add a simpler PTRACE_GETHBPREGS/SETHBPREGS request pair that lets a tracer read or write a single breakpoint or watchpoint directly, without going through the regset machinery. These request numbers live in the arch-specific ptrace range (0x4210/0x4211) so they don't collide with the generic PTRACE_PEEKDATA/PTRACE_PEEKUSR codes. Breakpoints and watchpoints share the same trigger pool on this architecture, so select HAVE_MIXED_BREAKPOINTS_REGS. Hook up thread flush/copy so per-task breakpoints are cleaned up and cleared across fork/exec. Signed-off-by: Himanshu Chauhan --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/hw_breakpoint.h | 10 + arch/riscv/include/asm/processor.h | 18 + arch/riscv/include/uapi/asm/ptrace.h | 50 +++ arch/riscv/kernel/hw_breakpoint.c | 1 - arch/riscv/kernel/process.c | 5 + arch/riscv/kernel/ptrace.c | 507 +++++++++++++++++++++++++ include/uapi/linux/elf.h | 4 + tools/include/uapi/linux/elf.h | 2 + 9 files changed, 597 insertions(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a624dacdaf12..4d2913b49817 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -173,6 +173,7 @@ config RISCV select HAVE_GCC_PLUGINS select HAVE_GENERIC_VDSO if MMU select HAVE_HW_BREAKPOINT if PERF_EVENTS + select HAVE_MIXED_BREAKPOINTS_REGS select HAVE_IRQ_TIME_ACCOUNTING select HAVE_KERNEL_BZIP2 if !EFI_ZBOOT select HAVE_KERNEL_GZIP if !EFI_ZBOOT diff --git a/arch/riscv/include/asm/hw_breakpoint.h b/arch/riscv/include/as= m/hw_breakpoint.h index 4df1bfe0507e..94ebdd1ab9c3 100644 --- a/arch/riscv/include/asm/hw_breakpoint.h +++ b/arch/riscv/include/asm/hw_breakpoint.h @@ -14,6 +14,7 @@ struct task_struct; =20 /* Maximum number of hardware breakpoints supported */ #define RISCV_HW_BP_NUM_MAX 32 +#define RISCV_MAX_BP 16 =20 #if __riscv_xlen =3D=3D 64 #define cpu_to_le cpu_to_le64 @@ -256,6 +257,10 @@ struct arch_hw_breakpoint { unsigned long address; unsigned long len; unsigned int type; + unsigned int match; + unsigned int chain; + unsigned int select; + unsigned int time; =20 /* Trigger configuration data */ unsigned long tdata1; @@ -282,8 +287,13 @@ void arch_disable_hw_breakpoint(struct perf_event *bp); int arch_install_hw_breakpoint(struct perf_event *bp); void arch_uninstall_hw_breakpoint(struct perf_event *bp); void hw_breakpoint_pmu_read(struct perf_event *bp); +void clear_ptrace_hw_breakpoint(struct task_struct *tsk); +void flush_ptrace_hw_breakpoint(struct task_struct *tsk); +void ptrace_hw_copy_thread(struct task_struct *task); =20 #else =20 +static inline void ptrace_hw_copy_thread(struct task_struct *task) { } + #endif /* CONFIG_HAVE_HW_BREAKPOINT */ #endif /* __RISCV_HW_BREAKPOINT_H */ diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/pr= ocessor.h index 812517b2cec1..421bb6773015 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include =20 @@ -102,6 +103,19 @@ struct pt_regs; #define RISCV_PREEMPT_V_NEED_RESTORE 0x40000000 #define RISCV_PREEMPT_V_IN_SCHEDULE 0x20000000 =20 +struct debug_info { +#ifdef CONFIG_HAVE_HW_BREAKPOINT + /* Have we suspended stepping by a debugger? */ + int suspended_step; + /* Allow breakpoints and watchpoints to be disabled for this thread. */ + int bp_disabled; + int wp_disabled; + /* Hardware breakpoints pinned to this task. */ + struct perf_event *hbp_break[RISCV_MAX_BP]; + struct perf_event *hbp_watch[RISCV_MAX_BP]; +#endif +}; + /* CPU-specific state of a task */ struct thread_struct { /* Callee-saved registers */ @@ -122,6 +136,10 @@ struct thread_struct { bool force_icache_flush; /* A forced icache flush is not needed if migrating to the previous cpu. = */ unsigned int prev_cpu; +#endif + struct debug_info debug; +#ifdef CONFIG_HAVE_HW_BREAKPOINT + struct perf_event *ptrace_bps[RISCV_HW_BP_NUM_MAX]; #endif }; =20 diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi= /asm/ptrace.h index 3de2b7124aff..9b0acc0537fa 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -10,11 +10,14 @@ =20 #include #include +#include =20 #define PTRACE_GETFDPIC 33 =20 #define PTRACE_GETFDPIC_EXEC 0 #define PTRACE_GETFDPIC_INTERP 1 +#define PTRACE_GETHBPREGS 0x4210 +#define PTRACE_SETHBPREGS 0x4211 =20 /* * User-mode register state for core dumps, ptrace, sigcontext @@ -164,6 +167,53 @@ struct user_cfi_state { __u64 shstk_ptr; }; =20 +/* + * bit[3:0] Match + * bit[8:4] Size + * bit[11:9] When + * bit[12] Select + * bit[13] Chain + * bit[16:14] Type + * bit[XLEN-1:17] Reserved +*/ +#define HWDEBUG_MATCH_MASK __GENMASK(3, 0) +#define HWDEBUG_SIZE_MASK __GENMASK(8, 4) +#define HWDEBUG_WHEN_MASK __GENMASK(11, 9) +#define HWDEBUG_SELECT_MASK _BITUL(12) +#define HWDEBUG_CHAIN_MASK _BITUL(13) +#define HWDEBUG_TYPE_MASK __GENMASK(16, 14) + +#define HWDEBUG_MATCH(_ctrl) (((_ctrl) & HWDEBUG_MATCH_MASK) >> 0) +#define HWDEBUG_SIZE(_ctrl) (((_ctrl) & HWDEBUG_SIZE_MASK) >> 4) +#define HWDEBUG_WHEN(_ctrl) (((_ctrl) & HWDEBUG_WHEN_MASK) >> 9) +#define HWDEBUG_SELECT(_ctrl) (((_ctrl) & HWDEBUG_SELECT_MASK) >> 12) +#define HWDEBUG_CHAIN(_ctrl) (((_ctrl) & HWDEBUG_CHAIN_MASK) >> 13) +#define HWDEBUG_TYPE(_ctrl) (((_ctrl) & HWDEBUG_TYPE_MASK) >> 14) + +#define HWDEBUG_MK_MATCH(_match) ((_match << 0) & HWDEBUG_MATCH_MASK) +#define HWDEBUG_MK_SIZE(_sz) ((_sz << 4) & HWDEBUG_SIZE_MASK) +#define HWDEBUG_MK_WHEN(_when) ((_when << 9) & HWDEBUG_WHEN_MASK) +#define HWDEBUG_MK_SELECT(_sel) ((_sel << 12) & HWDEBUG_SELECT_MASK) +#define HWDEBUG_MK_CHAIN(_chain) ((_chain << 13) & HWDEBUG_CHAIN_MASK) +#define HWDEBUG_MK_TYPE(_type) ((_type << 14) & HWDEBUG_TYPE_MASK) + +struct user_hwdebug_state { + __u32 info; + __u32 pad; + struct { + __u64 addr; + __u32 control; + __u32 pad; + } dbg_regs[16]; +}; + +struct __riscv_hwdebug_state { + unsigned long addr; + unsigned long type; + unsigned long len; + unsigned long ctrl; +} __packed; + #endif /* __ASSEMBLER__ */ =20 #endif /* _UAPI_ASM_RISCV_PTRACE_H */ diff --git a/arch/riscv/kernel/hw_breakpoint.c b/arch/riscv/kernel/hw_break= point.c index fc54a1a897c5..b4fab41cd3f8 100644 --- a/arch/riscv/kernel/hw_breakpoint.c +++ b/arch/riscv/kernel/hw_breakpoint.c @@ -630,7 +630,6 @@ EXPORT_SYMBOL_GPL(arch_disable_hw_breakpoint); =20 void hw_breakpoint_pmu_read(struct perf_event *bp) { } =20 -void flush_ptrace_hw_breakpoint(struct task_struct *tsk) { } =20 static int __init arch_hw_breakpoint_init(void) { diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index b2df7f72241a..b20b5fbeb4e3 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -204,6 +204,7 @@ void flush_thread(void) if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) envcfg_update_bits(current, ENVCFG_PMM, ENVCFG_PMM_PMLEN_0); #endif + flush_ptrace_hw_breakpoint(current); } =20 void arch_release_task_struct(struct task_struct *tsk) @@ -283,6 +284,10 @@ int copy_thread(struct task_struct *p, const struct ke= rnel_clone_args *args) p->thread.riscv_v_flags =3D 0; if (has_vector() || has_xtheadvector()) riscv_v_thread_alloc(p); + ptrace_hw_copy_thread(p); +#ifdef CONFIG_HAVE_HW_BREAKPOINT + memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); +#endif p->thread.sp =3D (unsigned long)childregs; /* kernel sp */ return 0; } diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index f336a183667e..2d4ee51e6859 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -18,8 +18,10 @@ #include #include #include +#include #include #include +#include =20 enum riscv_regset { REGSET_X, @@ -35,6 +37,10 @@ enum riscv_regset { #ifdef CONFIG_RISCV_USER_CFI REGSET_CFI, #endif +#ifdef CONFIG_HAVE_HW_BREAKPOINT + REGSET_RISCV_HW_BREAK, + REGSET_RISCV_HW_WATCH, +#endif }; =20 static int riscv_gpr_get(struct task_struct *target, @@ -372,6 +378,397 @@ static int riscv_cfi_set(struct task_struct *target, } #endif =20 +#ifdef CONFIG_HAVE_HW_BREAKPOINT +/* + * Handle hitting a HW-breakpoint. + */ +static void riscv_ptrace_hbptriggered(struct perf_event *bp, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct arch_hw_breakpoint *bkpt =3D counter_arch_bp(bp); + + force_sig_fault(SIGTRAP, TRAP_HWBKPT, (void __user *)bkpt->address); +} + +/* + * Unregister breakpoints from this task and reset the pointers in + * the thread_struct. + */ +void flush_ptrace_hw_breakpoint(struct task_struct *tsk) +{ + int i; + struct thread_struct *t =3D &tsk->thread; + + for (i =3D 0; i < RISCV_MAX_BP; i++) { + if (t->debug.hbp_break[i]) { + unregister_hw_breakpoint(t->debug.hbp_break[i]); + t->debug.hbp_break[i] =3D NULL; + } + } + + for (i =3D 0; i < RISCV_MAX_BP; i++) { + if (t->debug.hbp_watch[i]) { + unregister_hw_breakpoint(t->debug.hbp_watch[i]); + t->debug.hbp_watch[i] =3D NULL; + } + } +} + +void ptrace_hw_copy_thread(struct task_struct *tsk) +{ + memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); +} + +static struct perf_event *ptrace_hbp_get_event(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx) +{ + struct perf_event *bp =3D ERR_PTR(-EINVAL); + + switch (note_type) { + case NT_RISCV_HW_BREAK: + if (idx >=3D RISCV_MAX_BP) + goto out; + idx =3D array_index_nospec(idx, RISCV_MAX_BP); + bp =3D tsk->thread.debug.hbp_break[idx]; + break; + case NT_RISCV_HW_WATCH: + if (idx >=3D RISCV_MAX_BP) + goto out; + idx =3D array_index_nospec(idx, RISCV_MAX_BP); + bp =3D tsk->thread.debug.hbp_watch[idx]; + break; + } + +out: + return bp; +} + +static int ptrace_hbp_set_event(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx, + struct perf_event *bp) +{ + int err =3D -EINVAL; + + switch (note_type) { + case NT_RISCV_HW_BREAK: + if (idx >=3D RISCV_MAX_BP) + goto out; + idx =3D array_index_nospec(idx, RISCV_MAX_BP); + tsk->thread.debug.hbp_break[idx] =3D bp; + err =3D 0; + break; + case NT_RISCV_HW_WATCH: + if (idx >=3D RISCV_MAX_BP) + goto out; + idx =3D array_index_nospec(idx, RISCV_MAX_BP); + tsk->thread.debug.hbp_watch[idx] =3D bp; + err =3D 0; + break; + } + +out: + return err; +} + +static struct perf_event *ptrace_hbp_create(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx) +{ + struct perf_event *bp; + struct perf_event_attr attr; + int err, type; + + switch (note_type) { + case NT_RISCV_HW_BREAK: + type =3D HW_BREAKPOINT_X; + break; + case NT_RISCV_HW_WATCH: + type =3D HW_BREAKPOINT_RW; + break; + default: + return ERR_PTR(-EINVAL); + } + + ptrace_breakpoint_init(&attr); + + /* + * Initialise fields to sane defaults + * (i.e. values that will pass validation). + */ + attr.bp_addr =3D 0; + attr.bp_len =3D HW_BREAKPOINT_LEN_4; + attr.bp_type =3D type; + attr.disabled =3D 1; + + bp =3D register_user_hw_breakpoint(&attr, riscv_ptrace_hbptriggered, NULL= , tsk); + if (IS_ERR(bp)) + return bp; + + err =3D ptrace_hbp_set_event(note_type, tsk, idx, bp); + if (err) + return ERR_PTR(err); + + return bp; +} + +static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, + struct arch_hw_breakpoint *bpctrl, + struct perf_event_attr *attr) +{ + int len, type; + + attr->disabled =3D 0; + type =3D bpctrl->type; + len =3D bpctrl->len; + + switch (note_type) { + case NT_RISCV_HW_BREAK: + if ((type & HW_BREAKPOINT_X) !=3D type) + return -EINVAL; + break; + case NT_RISCV_HW_WATCH: + if ((type & HW_BREAKPOINT_RW) !=3D type) + return -EINVAL; + break; + default: + return -EINVAL; + } + + attr->bp_len =3D len; + attr->bp_type =3D type; + attr->bp_addr =3D bpctrl->address; + + return 0; +} + +static int ptrace_hbp_get_resource_info(unsigned int note_type, u32 *info) +{ + u8 num; + + switch (note_type) { + case NT_RISCV_HW_BREAK: + num =3D hw_breakpoint_slots(TYPE_INST); + break; + case NT_RISCV_HW_WATCH: + num =3D hw_breakpoint_slots(TYPE_DATA); + break; + default: + return -EINVAL; + } + + *info =3D num; + + return 0; +} + +static u32 encode_ctrl_reg(struct perf_event *bp) +{ + struct arch_hw_breakpoint *bpctrl =3D counter_arch_bp(bp); + u32 ctrl =3D 0; + + /* Expose the generic UAPI bp_type values in ptrace control bits. */ + ctrl |=3D HWDEBUG_MK_TYPE(bp->attr.bp_type); + ctrl |=3D HWDEBUG_MK_MATCH(bpctrl->match); + ctrl |=3D HWDEBUG_MK_SELECT(bpctrl->select); + ctrl |=3D HWDEBUG_MK_WHEN(bpctrl->time); + ctrl |=3D HWDEBUG_MK_SIZE(bp->attr.bp_len); + ctrl |=3D HWDEBUG_MK_CHAIN(bpctrl->chain); + + return ctrl; +} + +static int ptrace_hbp_get_ctrl(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx, + u32 *ctrl) +{ + struct perf_event *bp =3D ptrace_hbp_get_event(note_type, tsk, idx); + + if (IS_ERR(bp)) + return PTR_ERR(bp); + + *ctrl =3D bp ? encode_ctrl_reg(bp) : 0; + return 0; +} + +static int ptrace_hbp_get_addr(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx, + u64 *addr) +{ + struct perf_event *bp =3D ptrace_hbp_get_event(note_type, tsk, idx); + + if (IS_ERR(bp)) + return PTR_ERR(bp); + + *addr =3D bp ? counter_arch_bp(bp)->address : 0; + return 0; +} + +static struct perf_event *ptrace_hbp_get_initialised_bp(unsigned int note_= type, + struct task_struct *tsk, + unsigned long idx) +{ + struct perf_event *bp =3D ptrace_hbp_get_event(note_type, tsk, idx); + + if (!bp) + bp =3D ptrace_hbp_create(note_type, tsk, idx); + + return bp; +} + +static void decode_ctrl_reg(u32 uctrl, struct arch_hw_breakpoint *bpctrl) +{ + bpctrl->type =3D HWDEBUG_TYPE(uctrl); + bpctrl->match =3D HWDEBUG_MATCH(uctrl); + bpctrl->select =3D HWDEBUG_SELECT(uctrl); + bpctrl->time =3D HWDEBUG_WHEN(uctrl); + bpctrl->len =3D HWDEBUG_SIZE(uctrl); + bpctrl->chain =3D HWDEBUG_CHAIN(uctrl); +} + +static int ptrace_hbp_set_ctrl(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx, + u32 uctrl) +{ + int err; + struct perf_event *bp; + struct perf_event_attr attr; + struct arch_hw_breakpoint bpctrl; + + bp =3D ptrace_hbp_get_initialised_bp(note_type, tsk, idx); + if (IS_ERR(bp)) { + err =3D PTR_ERR(bp); + return err; + } + + attr =3D bp->attr; + decode_ctrl_reg(uctrl, &bpctrl); + bpctrl.address =3D attr.bp_addr; + err =3D ptrace_hbp_fill_attr_ctrl(note_type, &bpctrl, &attr); + if (err) + return err; + + return modify_user_hw_breakpoint(bp, &attr); +} + +static int ptrace_hbp_set_addr(unsigned int note_type, + struct task_struct *tsk, + unsigned long idx, + u64 addr) +{ + int err; + struct perf_event *bp; + struct perf_event_attr attr; + + bp =3D ptrace_hbp_get_initialised_bp(note_type, tsk, idx); + if (IS_ERR(bp)) { + err =3D PTR_ERR(bp); + return err; + } + + attr =3D bp->attr; + attr.bp_addr =3D addr; + err =3D modify_user_hw_breakpoint(bp, &attr); + return err; +} + +#define PTRACE_HBP_ADDR_SZ sizeof(u64) +#define PTRACE_HBP_CTRL_SZ sizeof(u32) +#define PTRACE_HBP_PAD_SZ sizeof(u32) + +static int riscv_hw_break_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + unsigned int note_type =3D regset->core_note_type; + int ret, idx, num_slots; + u32 info, ctrl; + u64 addr; + + /* Resource info: number of available slots */ + ret =3D ptrace_hbp_get_resource_info(note_type, &info); + if (ret) + return ret; + + membuf_write(&to, &info, sizeof(info)); + membuf_zero(&to, sizeof(u32)); + + /* Emit one (address, ctrl, pad) entry per available slot */ + num_slots =3D (int)info; + for (idx =3D 0; idx < num_slots; idx++) { + ret =3D ptrace_hbp_get_addr(note_type, target, idx, &addr); + if (ret) + return ret; + ret =3D ptrace_hbp_get_ctrl(note_type, target, idx, &ctrl); + if (ret) + return ret; + membuf_store(&to, addr); + membuf_store(&to, ctrl); + membuf_zero(&to, sizeof(u32)); + } + return 0; +} + +static int riscv_hw_break_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned int note_type =3D regset->core_note_type; + int ret, idx =3D 0, offset, limit; + u32 ctrl; + u64 addr; + + /* Resource info and pad */ + offset =3D offsetof(struct user_hwdebug_state, dbg_regs); + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, offset); + + /* (address, ctrl) registers */ + limit =3D regset->n * regset->size; + while (count && offset < limit) { + if (count < PTRACE_HBP_ADDR_SZ) + return -EINVAL; + + ret =3D user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr, + offset, offset + PTRACE_HBP_ADDR_SZ); + if (ret) + return ret; + + ret =3D ptrace_hbp_set_addr(note_type, target, idx, addr); + if (ret) + return ret; + + offset +=3D PTRACE_HBP_ADDR_SZ; + + if (!count) + break; + + ret =3D user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, + offset, offset + PTRACE_HBP_CTRL_SZ); + if (ret) + return ret; + + ret =3D ptrace_hbp_set_ctrl(note_type, target, idx, ctrl); + if (ret) + return ret; + + offset +=3D PTRACE_HBP_CTRL_SZ; + + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + offset, offset + PTRACE_HBP_PAD_SZ); + offset +=3D PTRACE_HBP_PAD_SZ; + idx++; + } + + return 0; +} +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ + static struct user_regset riscv_user_regset[] __ro_after_init =3D { [REGSET_X] =3D { USER_REGSET_NOTE_TYPE(PRSTATUS), @@ -421,6 +818,24 @@ static struct user_regset riscv_user_regset[] __ro_aft= er_init =3D { .set =3D riscv_cfi_set, }, #endif +#ifdef CONFIG_HAVE_HW_BREAKPOINT + [REGSET_RISCV_HW_BREAK] =3D { + USER_REGSET_NOTE_TYPE(RISCV_HW_BREAK), + .n =3D sizeof(struct user_hwdebug_state) / sizeof(u32), + .size =3D sizeof(u32), + .align =3D sizeof(u32), + .regset_get =3D riscv_hw_break_get, + .set =3D riscv_hw_break_set, + }, + [REGSET_RISCV_HW_WATCH] =3D { + USER_REGSET_NOTE_TYPE(RISCV_HW_WATCH), + .n =3D sizeof(struct user_hwdebug_state) / sizeof(u32), + .size =3D sizeof(u32), + .align =3D sizeof(u32), + .regset_get =3D riscv_hw_break_get, + .set =3D riscv_hw_break_set, + }, +#endif }; =20 static const struct user_regset_view riscv_user_native_view =3D { @@ -541,12 +956,104 @@ void ptrace_disable(struct task_struct *child) { } =20 +#ifdef CONFIG_HAVE_HW_BREAKPOINT +static int riscv_ptrace_bp_get(struct task_struct *child, unsigned long id= x, + struct __riscv_hwdebug_state *state) +{ + struct perf_event *bp; + + if (idx >=3D RISCV_HW_BP_NUM_MAX) + return -EINVAL; + + bp =3D child->thread.ptrace_bps[idx]; + if (!bp) + return -ENOENT; + + state->addr =3D bp->attr.bp_addr; + state->len =3D bp->attr.bp_len; + state->type =3D bp->attr.bp_type; + state->ctrl =3D bp->attr.disabled =3D=3D 1; + + return 0; +} + +static int riscv_ptrace_bp_set(struct task_struct *child, unsigned long id= x, + struct __riscv_hwdebug_state *state) +{ + struct perf_event *bp; + struct perf_event_attr attr; + + if (idx >=3D RISCV_HW_BP_NUM_MAX) + return -EINVAL; + + bp =3D child->thread.ptrace_bps[idx]; + if (bp) + attr =3D bp->attr; + else + ptrace_breakpoint_init(&attr); + + attr.bp_addr =3D state->addr; + attr.bp_len =3D state->len; + attr.bp_type =3D state->type; + /* Always register disabled; enable below if requested */ + attr.disabled =3D 1; + + if (!bp) { + bp =3D register_user_hw_breakpoint(&attr, riscv_ptrace_hbptriggered, NUL= L, child); + if (IS_ERR(bp)) + return PTR_ERR(bp); + child->thread.ptrace_bps[idx] =3D bp; + } + + /* Enable or disable as requested by ctrl (0 =3D enabled, 1 =3D disabled)= */ + attr.disabled =3D state->ctrl =3D=3D 1; + return modify_user_hw_breakpoint(bp, &attr); +} + +static long riscv_ptrace_gethbpregs(struct task_struct *child, unsigned lo= ng idx, + unsigned long __user *datap) +{ + struct __riscv_hwdebug_state state; + long ret; + + ret =3D riscv_ptrace_bp_get(child, idx, &state); + if (ret) + return ret; + if (copy_to_user(datap, &state, sizeof(state))) + return -EFAULT; + + return 0; +} + +static long riscv_ptrace_sethbpregs(struct task_struct *child, unsigned lo= ng idx, + unsigned long __user *datap) +{ + struct __riscv_hwdebug_state state; + + if (copy_from_user(&state, datap, sizeof(state))) + return -EFAULT; + + return riscv_ptrace_bp_set(child, idx, &state); +} +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ + long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { long ret =3D -EIO; +#ifdef CONFIG_HAVE_HW_BREAKPOINT + unsigned long __user *datap =3D (unsigned long __user *)data; +#endif =20 switch (request) { +#ifdef CONFIG_HAVE_HW_BREAKPOINT + case PTRACE_GETHBPREGS: + ret =3D riscv_ptrace_gethbpregs(child, addr, datap); + break; + case PTRACE_SETHBPREGS: + ret =3D riscv_ptrace_sethbpregs(child, addr, datap); + break; +#endif default: ret =3D ptrace_request(child, request, addr, data); break; diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index ee30dcd80901..1315ac35157c 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -547,6 +547,10 @@ typedef struct elf64_shdr { #define NT_RISCV_TAGGED_ADDR_CTRL 0x902 /* RISC-V tagged address control (= prctl()) */ #define NN_RISCV_USER_CFI "LINUX" #define NT_RISCV_USER_CFI 0x903 /* RISC-V shadow stack state */ +#define NN_RISCV_HW_BREAK "LINUX" +#define NT_RISCV_HW_BREAK 0x904 /* RISC-V hardware breakpoint registers */ +#define NN_RISCV_HW_WATCH "LINUX" +#define NT_RISCV_HW_WATCH 0x905 /* RISCV-V hardware watchpoint registers = */ #define NN_LOONGARCH_CPUCFG "LINUX" #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NN_LOONGARCH_CSR "LINUX" diff --git a/tools/include/uapi/linux/elf.h b/tools/include/uapi/linux/elf.h index 5834b83d7f9a..21f225502051 100644 --- a/tools/include/uapi/linux/elf.h +++ b/tools/include/uapi/linux/elf.h @@ -460,6 +460,8 @@ typedef struct elf64_shdr { #define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ #define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */ #define NT_RISCV_TAGGED_ADDR_CTRL 0x902 /* RISC-V tagged address control (= prctl()) */ +#define NT_RISCV_HW_BREAK 0x904 +#define NT_RISCV_HW_WATCH 0x905 #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and status registers */ #define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD Extension regist= ers */ --=20 2.43.0 From nobody Fri Oct 2 08:26:24 2026 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: quoted-printable 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 Content-Type: text/plain; charset="utf-8" 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 =20 #ifndef noinline #define noinline __attribute__((noinline)) #endif =20 +#include "kselftest.h" + +static int test_func_sink; + +/* + * Keep a real instruction address for HW execute breakpoints: prevent inl= ining + * 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) !=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); + _exit(1); + } + + if (raise(SIGSTOP) !=3D 0) { + ksft_print_msg( + "raise(SIGSTOP) failed: %s\n", strerror(errno)); + _exit(1); + } + + sleep(1); + switch (size) { + case 1: + *(uint8_t *)addr =3D 47; + break; + case 2: + *(uint16_t *)addr =3D 47; + break; + case 4: + *(uint32_t *)addr =3D 47; + break; + case 8: + *(uint64_t *)addr =3D 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) !=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); + _exit(1); + } + + if (raise(SIGSTOP) !=3D 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 =3D (uint8_t *)&var; + unsigned int control =3D 0; + struct user_hwdebug_state dreg_state; + struct iovec iov; + + /* Write watchpoint */ + control =3D (HW_BREAKPOINT_W << 14) & ((0x7 << 14)); + control |=3D (HW_BREAKPOINT_LEN_8 << 4) & ((0x1f << 4)); + memset(&dreg_state, 0, sizeof(dreg_state)); + dreg_state.dbg_regs[0].addr =3D (uintptr_t)(addr); + dreg_state.dbg_regs[0].control =3D control; + iov.iov_base =3D &dreg_state; + iov.iov_len =3D offsetof(struct user_hwdebug_state, dbg_regs) + + sizeof(dreg_state.dbg_regs[0]); + + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_HW_WATCH, &iov) =3D=3D 0) { + memset(&iov, 0, sizeof(iov)); + memset(&dreg_state, 0, sizeof(dreg_state)); + + iov.iov_base =3D &dreg_state; + iov.iov_len =3D offsetof(struct user_hwdebug_state, dbg_regs) + + sizeof(dreg_state.dbg_regs[0]); + + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_HW_WATCH, &iov) =3D=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_GETREGSET): Number of watchpoints: %u\n", dreg_s= tate.info); + ksft_print_msg( + "ptrace(PTRACE_GETREGSet): addr: 0x%lx control: 0x%x\n", dreg_s= tate.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 =3D=3D EIO) + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_WATCH) not supported on th= is 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 =3D 0; + + control =3D (HW_BREAKPOINT_X << 14) & ((0x7 << 14)); + control |=3D (HW_BREAKPOINT_LEN_8 << 4) & ((0x1f << 4)); + memset(&dreg_state, 0, sizeof(dreg_state)); + dreg_state.dbg_regs[0].addr =3D (uintptr_t)bp_func; + dreg_state.dbg_regs[0].control =3D control; + iov.iov_base =3D &dreg_state; + iov.iov_len =3D offsetof(struct user_hwdebug_state, dbg_regs) + sizeof(dr= eg_state.dbg_regs[0]); + + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_HW_BREAK, &iov) =3D=3D 0) + return true; + + if (errno =3D=3D EIO) + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_BREAK) not supported on th= is hardware: %s\n", strerror(errno)); + else + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_RISCV_HW_BREAK) failed: %s\n", stre= rror(errno)); + + return false; +} + +static int run_ptrace_wp_test(void) +{ + pid_t pid =3D fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid =3D=3D 0) + do_wp_child(&var, 8); + + wpid =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D 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 =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) !=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code !=3D TRAP_HWBKPT) { + ksft_print_msg( + "Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid =3D waitpid(pid, &status, 0); + if (wpid !=3D 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 =3D fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid =3D=3D 0) + do_bp_child(test_func); + + wpid =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D 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 =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) !=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code !=3D TRAP_HWBKPT) { + ksft_print_msg( + "Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid =3D waitpid(pid, &status, 0); + if (wpid !=3D 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 =3D (unsigned long)&var; + state.len =3D HW_BREAKPOINT_LEN_8; + state.type =3D HW_BREAKPOINT_W; + state.ctrl =3D 0; /* enabled */ + + if (ptrace(PTRACE_SETHBPREGS, pid, 0, &state) !=3D 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) !=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_GETHBPREGS) failed: %s\n", + strerror(errno)); + return false; + } + + ksft_print_msg( + "[hbpregs] watchpoint readback: addr=3D0x%lx type=3D%lu len=3D%lu ctrl= =3D%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 =3D (unsigned long)bp_func; + state.len =3D HW_BREAKPOINT_LEN_4; + state.type =3D HW_BREAKPOINT_X; + state.ctrl =3D 0; /* enabled */ + + if (ptrace(PTRACE_SETHBPREGS, pid, 0, &state) !=3D 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) !=3D 0) { + ksft_print_msg( + "ptrace(PTRACE_GETHBPREGS) failed: %s\n", + strerror(errno)); + return false; + } + + ksft_print_msg( + "[hbpregs] breakpoint readback: addr=3D0x%lx type=3D%lu len=3D%lu ctrl= =3D%lu\n", + state.addr, state.type, state.len, state.ctrl); + + return true; +} + +static int run_hbpregs_wp_test(void) +{ + pid_t pid =3D fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid =3D=3D 0) + do_wp_child(&var, 8); + + wpid =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D 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 =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) !=3D 0) { + ksft_print_msg("ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code !=3D TRAP_HWBKPT) { + ksft_print_msg("Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid =3D waitpid(pid, &status, 0); + if (wpid !=3D 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 =3D fork(); + pid_t wpid; + siginfo_t siginfo; + int status; + + if (pid =3D=3D 0) + do_bp_child(test_func); + + wpid =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D 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 =3D waitpid(pid, &status, __WALL); + if (wpid !=3D 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) !=3D SIGTRAP) { + ksft_print_msg("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) !=3D 0) { + ksft_print_msg("ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); + return false; + } + if (siginfo.si_code !=3D TRAP_HWBKPT) { + ksft_print_msg("Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid =3D waitpid(pid, &status, 0); + if (wpid !=3D 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 =3D 5; =20 int setup_bp(bool is_x, void *addr, int sig) @@ -56,7 +611,7 @@ int setup_bp(bool is_x, void *addr, int sig) =20 fd =3D 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; } =20 @@ -75,11 +630,10 @@ static void sig_handler_bp(int signum, siginfo_t *oh, = void *uc) =20 bp_triggered++; =20 - printf("Breakpoint triggered!\n"); ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); ret =3D 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; =20 - printf("Watchpoint triggered!\n"); ioctl(gfd, PERF_EVENT_IOC_DISABLE, 0); wp_triggered++; =20 ret =3D sem_post(&wp_mtx); =20 if (ret) { - printf("Failed to report WP success\n"); + ksft_print_msg("Failed to report WP success\n"); return; } } =20 -/* - * Keep a real instruction address for HW execute breakpoints: prevent inl= ining - * 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 =3D SA_SIGINFO; =20 if (sigaction(SIGIO, &sa, NULL) < 0) { - printf("Failed to setup signal handler\n"); + ksft_print_msg("Failed to setup signal handler\n"); return -1; } =20 gfd =3D setup_bp(1, test_func, SIGIO); =20 if (gfd < 0) { - printf("Failed to setup breakpoint.\n"); + ksft_print_msg("Failed to setup breakpoint.\n"); return -1; } =20 @@ -151,14 +694,14 @@ static int trigger_wp(void) sa.sa_flags =3D SA_SIGINFO; =20 if (sigaction(SIGUSR1, &sa, NULL) < 0) { - printf("Failed to setup signal handler\n"); + ksft_print_msg("Failed to setup signal handler\n"); return -1; } =20 gfd =3D setup_bp(0, &test_data, SIGUSR1); =20 if (gfd < 0) { - printf("Failed to setup watchpoint\n"); + ksft_print_msg("Failed to setup watchpoint\n"); return -1; } =20 @@ -204,7 +747,7 @@ int main(int argc, char *argv[]) return -1; =20 if (bp_triggered) - printf("Breakpoint test passed!\n"); + ksft_print_msg("[perf_event]: Breakpoint test passed!\n"); =20 sem_init(&wp_mtx, 0, 0); if (trigger_wp() < 0) @@ -213,7 +756,10 @@ int main(int argc, char *argv[]) return -1; =20 if (wp_triggered) - printf("Watchpoint test passed!\n"); + ksft_print_msg("[perf_event]: Watchpoint test passed!\n"); + + run_ptrace_tests(); + run_hbpregs_tests(); =20 return 0; } --=20 2.43.0 From nobody Fri Oct 2 08:26:24 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.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 601DC43E9FB for ; Mon, 3 Aug 2026 13:49:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764991; cv=none; b=uW+X/lSsEr+KSsmXe4d3r6zfrkv4QdBp03xy6lFqjn8yQ3XtYPYX7M2E9MYKuXdznB85oVSev3papYUzOwZHNjPhSqzfkTnPhlYVZIN50+uJWKn/Yn5EHIFKweNlBTDiwsVO4csq/mQbt/fFot7tYj3njVYk9t/VKwV5rg5jz/s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785764991; c=relaxed/simple; bh=K3l6Zs7VHW8OvXGdmeBZQlCoVSWsSKWIn+bo23yYIZM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OBWvEibv/7ckYbcHJ1FqiugZqv5aTkqyGgtJI70v0iZtoPB1Pegzp3j4fpF8LFdE7NH+tbIPFCqNXz/pQvaLmoc8A/AfrQSuRwwfHI6G/4Vx/JQqjQu67Y1hLZ+vIGOoZeRTyGYknZOyK+4lb+a6euHrh2oVW+45JSvVRg+c5IQ= 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=f4uHXHYw; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=Ksg8Z0Zc; arc=none smtp.client-ip=205.220.180.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="f4uHXHYw"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="Ksg8Z0Zc" Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 673DSoCD741727 for ; Mon, 3 Aug 2026 13:49:49 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=k9yYLX6bWd+ q10JuHmJUx1dKs4VyCUKLaw59KlrlBNo=; b=f4uHXHYweK104ld9QyrsJ5cf3Bc JHbUilgYXFVq7xAomMRnaX6uYW6eIMo8HHCqXLJd0r7+k+iygf242qzZYa5SWynC zW7p3BmCCRtXPuiZXTmGgqDXxbE0QI1Ot5iD+3CA1zuIbVkCNvul2j3FT1/pKhhC LWPDuaYdVnm9Xdfw8OqyTVsMc1AEWUKEhvD2nZsfuicApZR/nDV6iw9SiJoHtj1W 6khGRvQPwWzIBJaDVVyv7qIJvGma3Nl4c9WI9qYdxaclaZba3OC/Ts4PqzrrzWkh bLr7+qofjx2pdhmSd06JBa4CjXS5COxgll9EA+6hP/sS9PUSxCcub2M8xnw== Received: from mail-pj1-f71.google.com (mail-pj1-f71.google.com [209.85.216.71]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ftnvnhsm0-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 03 Aug 2026 13:49:48 +0000 (GMT) Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-38f283baf1fso3258643a91.3 for ; Mon, 03 Aug 2026 06:49:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1785764988; x=1786369788; 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=k9yYLX6bWd+q10JuHmJUx1dKs4VyCUKLaw59KlrlBNo=; b=Ksg8Z0ZcFLIz+B96/A6StH04nQlXcaYM40t91uYxEWwlIfpkcOduz0dybkCoOSdSnZ 2TVnSf7CMZhgiI+YWjna98rA1h/YIdmCRCbo5ixmZk3I/S3gZNyTjv06z1AK+xTqiUKU QeLR6bhcTzzv745vS+rQSFXQD5ja+S0pGlRSqyla1m1UZ2Wukm+2EBVCK/rmDbRJ+yk1 /2+NFNMaJhqqtaGPBGqxemPm9yKCVhBaOQdxGWDHStHGL7nDVNxgdfImlu4+YUX1QIBE akJPOIUukQOjsCLzzTJRRbVwFIenzwGZYcdSvSLjZ8aK0MWdc/RpdBYdhUiIjsiYRwsp X96w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785764988; x=1786369788; 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=k9yYLX6bWd+q10JuHmJUx1dKs4VyCUKLaw59KlrlBNo=; b=WJZaJTwpJtJCVTHVXZuJml1xa1V19nD5Re756+JLTz3Bj7f58H8P1HjF6RgxkD+Na4 VMevcQhqm/Rg7ZYkMzVjw735nZ6lX5CVAH/P8hqe0WNxqrkb7FY05hHIsKjeJ4Jc1w1W kfbr3nn8CnbL+wPxqNrVvM+hldXkJx1KAE7mG1zJMQ5mTczd0Ml8CUSwLr/2po417JfF 7BoXo3lOpK4XrP4dnlOnB1V3OmyJtXNqz5LTsSYu0JxbPvWG+mIgKtVQF3c5htEdmNG3 E0+OEZyPUcdSLUsR216sGzqptt6XcuqVgrhlIInwgVYP2eYPq4Y7hqysMsFGANg5zlhJ 3vrA== X-Forwarded-Encrypted: i=1; AHgh+Rqe82Q/sReqZHNHa0s4cvhNG4RpmpKDY4cr6EhevEPP3vkVhDw8xkfXhegfojNRLiADIcOKOTzR8yZ4/hg=@vger.kernel.org X-Gm-Message-State: AOJu0Yzzh+EQYDxlfl6quhDgPm1LamCi8es5IlyU3optvkKG0VNMCLo9 LqvZhS8tjdIXjIn5ORR5nt6NuhpZPJ1cKo2WiKO45FILgxgLZ3XVbN+a5SrRkdEHgpQHKOMSA/g t2eNv2knjVw39xiFaJnAjpphXwHhU+VEi4FQLzRaZwW5JqGojwB+L1KQpu61esYkliyE= X-Gm-Gg: AR+sD10dRhaW21Gke++EbdlTv4/hbP2Uyg5E+AWNL0QFefQehgo2kCGtLcqXBG2mtIZ EbRe2wEnPe1ztcW+nolzqWaaxXRBxjQzxUUeWoDAGSd93JoiCXw2lQbN7B820Smpfrm+Suv0McN Y011jQVDwJaoLALHiBvUS5Bkq5FSG22Xq3gCikHEoBJ3r8SCYS5Cyze8Nzd2wVdkDmCadTHf67J PgIqdqIhHjayds/DLE5ni4c4rc9S5fH7y2SiK1K/Z9ygj6MRWw7IUgodff3RiZdxM8vDXfoAijJ EsEqWQ4iFogoWbyUhGoh0vWi8qhy++IbWXPjukge2UPwnqOL0JMkPpfCl84ZlyREUM2ywLBgQfG 1RfbHUDm9/1YgyWILgGjoKO7M2Oz8HBjtZiZYtPjxYnGw4u6jR6boGbtGuOFxJg/M2fVjlLqf+8 uuzE87SxARmZp9ubIZQT3jXcQY X-Received: by 2002:a17:90b:2dc4:b0:38e:ad9d:1151 with SMTP id 98e67ed59e1d1-38fbc3f807cmr9813144a91.4.1785764987839; Mon, 03 Aug 2026 06:49:47 -0700 (PDT) X-Received: by 2002:a17:90b:2dc4:b0:38e:ad9d:1151 with SMTP id 98e67ed59e1d1-38fbc3f807cmr9813110a91.4.1785764987258; Mon, 03 Aug 2026 06:49:47 -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.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 06:49:46 -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 5/5] MAINTAINERS: Add entry for RISC-V Debugging Date: Mon, 3 Aug 2026 19:19:13 +0530 Message-ID: <20260803134913.2013674-6-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: quoted-printable X-Authority-Analysis: v=2.4 cv=aoaCzyZV c=1 sm=1 tr=0 ts=6a709c7c cx=c_pps a=UNFcQwm+pnOIJct1K4W+Mw==:117 a=Ou0eQOY4+eZoSc0qltEV5Q==:17 a=Sv0fKeRqtYgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=ZpdpYltYx_vBUK5n70dp:22 a=EUspDBNiAAAA:8 a=JfrnYn6hAAAA:8 a=VwQbUJbxAAAA:8 a=8h6Es3YqzoBqtYBhd2sA:9 a=uKXjsCUrEbL0IQVhDsJ9:22 a=1CNFftbPRP8L7MoqJWF3:22 X-Proofpoint-GUID: z7vI8rObdgrNP1IWkheZfk24VXk6KOPm X-Proofpoint-ORIG-GUID: z7vI8rObdgrNP1IWkheZfk24VXk6KOPm X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX9VvHBnogM2ef T5Pqa+dAoxWfIoQImDVeTJ4qF5zbwq4Ut5HI45/hWSGeHYg50Pp7SkxOEMkFmFqDFp+ElIDKsvQ P1C8GbP5T63BdZe7WC0ySCwrQYafAIoHatDV507BilqN4RN6rKwqSzpwMILIdqXlCJkOqQA+Ghi VqslYFltT0qmEGfqPK2U1Q1OHbE/U2CI9Et6zK6Hetj4rpxc1jXiJvxD1DUT5S2BbT85UTnDhFV EY+4qvXCJAZRY5bq6oLQTmU3dEqy7qlxKQ7G403gnM+ZiVRziYTJut2mP49AynQ47tIgPs0gkmO NzHxSpofjzw9fY5eqg1jGUyQJ8MlN8cLpNx3faoIAvKA8IUZyiSAPFW7h2q7uuA1EcuFR6a3Zgl slJopSMWmlVBACeSvOn7KaVYTI6JJ9a/3rFqGyP0vm5io809anmk5VsIbomaPuWSG/VYBsgkeG+ rbuAj/8kmLpgJzNt/Rw== X-Proofpoint-Spam-Info: AW1haW4tMjYwODAzMDEyNCBTYWx0ZWRfX6+xUfSb7X0cL et8l7jUIXk3p+gegNdxQ2BwlJltsQ7OXoUo1LuUV+GcMJAyoVnRvbiLyxz7AQVY82tTrBDsSC5v Oe9lMyo78EJJpuYLSg1Z1d3uBy3KMqk= 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 clxscore=1015 bulkscore=0 suspectscore=0 priorityscore=1501 adultscore=0 spamscore=0 lowpriorityscore=0 malwarescore=0 phishscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608030124 Content-Type: text/plain; charset="utf-8" Added myself as maintainer for the RISC-V inline debugging with sdtrig exte= nsion Signed-off-by: Himanshu Chauhan --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5114e6db7307..fc59ab4f7684 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23311,6 +23311,15 @@ F: drivers/irqchip/irq-riscv-rpmi-sysmsi.c F: drivers/mailbox/riscv-sbi-mpxy-mbox.c F: include/linux/mailbox/riscv-rpmi-message.h =20 +RISC-V DEBUGGING +M: Himanshu Chauhan +L: linux-riscv@lists.infradead.org +F: arch/riscv/include/asm/hw_breakpoint.h +F: arch/riscv/include/asm/perf_event.h +F: arch/riscv/kernel/hw_breakpoint.c +F: arch/riscv/kernel/perf_* +F: drivers/perf/ + RISC-V SPACEMIT SoC Support M: Yixun Lan L: linux-riscv@lists.infradead.org --=20 2.43.0