From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbg151.qq.com (smtpbg151.qq.com [18.169.211.239]) (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 9745E33EAED for ; Tue, 14 Apr 2026 03:43:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=18.169.211.239 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138225; cv=none; b=ndBkjd6gEafGvn27VYdP/nZKYSRIIzFfq/qri5D1c/RDZdlyqA4zYuOwLuGabWqqC1r3KYiEKZY9DKoN5hel6sy2KwyPQl/9aDSL9IXlfVzbpJUFK0yhETUfabacrNGn4Vr1PhlbgmrwmcPdoapHwQ9afFvAlRQtubIxt7wyHSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138225; c=relaxed/simple; bh=UR68+bBA8tow9yphmnkIpolIfiDg4AZgcf76ZGl8j+U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AM8HQZSUEq7IWA70BhFzR+zAmF60mXc92pWdD8QTWPhVGSLQBtyV6fkDWktzuSVru2DTUO3oPXHh3X4qkMlZ2BuLbWTox3p24MfAUkewSICg7fkYlW1nqb7bLwdKppxEN9yhtE+bySDWdyXkQpy2Q8cxdpFiuaSFAd8Y+sri7po= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=DCM4FUxr; arc=none smtp.client-ip=18.169.211.239 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="DCM4FUxr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138127; bh=/8YEo9Fpa6X2nfI8zB5cPmwLZMnhM0EVOr5H8PO1hcI=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=DCM4FUxrSuU7eBdDDOq5cNzydBTqqeDL1diRCaS0HRVsSYJVvhNB6gQwIGCfUvM2Q 4HqfDUVC/0jy7fgZZqN35z+d9rZItHZBcA02hiHXTUoqU/dSu3Mrez7/4LhThm2uEi GfbDWpaeXFKcF732Wn8IaqahfmbNCn0uFDCbuKH4= X-QQ-mid: zesmtpsz5t1776138120ted4d29b0 X-QQ-Originating-IP: kl3+y6DFT4YtzEPa1Z5e8/kbAEH78IRgrQ/pQRrO5x0= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:41:58 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 2782345312967798588 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Date: Tue, 14 Apr 2026 11:41:42 +0800 Message-Id: <20260414034153.3272485-2-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: NJVYoP7aqd/U+/B+MBoPySiiE89XjkOOjVfKFZ++dtVL6nRRLIr+stvP uGFdlVio6x6Y2e0QjysRLiIKo8EFVMLffsWwoEXqSZFjXOPoG3rSAsaLs0FLWkklLkkWxSr IG+aMiBto/SWijjNOrNGNCRVpnN7C1ZYFde7NDb8HVDpJpe/Hb5KN36eknndx+cUp98wXsb Wyijx1Kx9zy/VVQlbWrAt20zP4kwK+389N/cYf+nj2dXN9ob6Yqn0rmxq3b04EHwNkvs/VV OPpbra1AIR+B4M/pZyxpCD3KRf9K0amXdbc/VZxG6p/pke2x5zMut4QGGQcg2vdzWaNPYi+ F3ry22dTZq7Y4vvhidnD5310k5ziKLBfwCphnVSoCb9grksA+D9qgbuLGrSJsLrax5RCyM7 F3FEaMQ4DsLanpF31kjbPYK1qi5FUSBpNHlPrH4U5xAiF7Uwn6k+c6sZlSqAVTBokwREDlU Po7dBu8s9KlCywNw5OgDJt4UT3elpf1jO5UzCbwOIQY8KVtfsU66kgILND3Mbbhj0vMOBhg gTbr755Ib1PA2kCmt196UM3i2iFrf01XREPXfifeQHKI+Bm8MdR855Sujd97y4yLdznr1Wg +UiFRnid/MOwc/Snzsxx3ScoA75/mSGLUr/M3ud+hqINV/wbtey+T0SgqjvxPovLkkPpm0a Ge4WZ/eQx667YW7GHLgFBjUX096xtGghcE+3vR4D2VsO/01qKTi+jTCHKXzcM+4gSsk013a 8ZKfUJvsjd4jLbotycUXKrp2B/aC63VoMzuLgKztRv4/iQXcn28ST60mly+JNkIFPfvEisa mH9GRJjIw0FshydapY/VkNzeFVA1kQnmZISBD1vicX2b0UhOChKAdVidnD2m26VHvGh+OHj I83iuRKIf0s0aHl7u61jYLH0j1kEZ6u2U0+LJ2c4hLHLWJIxobGbOv4Mg+pocfAY9jmtjKF DbhqTqEkThO6uGaYb/IMpTmB9jba4ViOJ4bDfsSI/wHhzpvaqS1mBFy0oFbz/YUoS4SEivt +cOubHFqQ1OMbxeo7TNfdZIFEiUz7Ao6PGECZY0XxSMl8mxf2WtJrhmnxQrereI6oR/RKUR IyHx3ddoJHr+5d3QXsTit1cAxxbjW7fDA== X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Enable CoreSight tracing support on RISC-V architecture by: - Adding RISC-V to Kconfig dependencies for CoreSight - Replacing ARM-specific memory barriers (isb, dmb) with RISC-V equivalents (local_flush_icache_all, __mb) - Removing ARM-specific header dependencies: perf/arm_pmu.h, asm/smp_plat.h - Adding PMU format attribute macros for cross-architecture support This allows CoreSight tracing infrastructure to work on RISC-V systems while maintaining compatibility with existing ARM/ARM64 implementations. Signed-off-by: liangzhen --- drivers/hwtracing/Kconfig | 2 ++ drivers/hwtracing/coresight/Kconfig | 2 +- drivers/hwtracing/coresight/coresight-core.c | 8 +++++++ .../hwtracing/coresight/coresight-etm-perf.c | 1 - .../hwtracing/coresight/coresight-etm-perf.h | 21 +++++++++++++++++++ .../hwtracing/coresight/coresight-platform.c | 1 - .../hwtracing/coresight/coresight-tmc-etf.c | 4 ++++ .../hwtracing/coresight/coresight-tmc-etr.c | 4 ++++ 8 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig index 911ee977103c..167ff172dd72 100644 --- a/drivers/hwtracing/Kconfig +++ b/drivers/hwtracing/Kconfig @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only menu "HW tracing support" =20 +source "drivers/hwtracing/coresight/Kconfig" + source "drivers/hwtracing/stm/Kconfig" =20 source "drivers/hwtracing/intel_th/Kconfig" diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresi= ght/Kconfig index 6a4239ebb582..2b1ebe3f614d 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -4,7 +4,7 @@ # menuconfig CORESIGHT tristate "CoreSight Tracing Support" - depends on ARM || ARM64 + depends on ARM || ARM64 || RISCV depends on OF || ACPI select ARM_AMBA select PERF_EVENTS diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtraci= ng/coresight/coresight-core.c index 80e26396ad0a..1ca202153cc4 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -141,7 +141,11 @@ static void coresight_set_self_claim_tag_unlocked(stru= ct coresight_device *csdev { csdev_access_relaxed_write32(&csdev->access, CORESIGHT_CLAIM_SELF_HOSTED, CORESIGHT_CLAIMSET); +#if defined(__riscv) + local_flush_icache_all(); +#else isb(); +#endif } =20 void coresight_clear_self_claim_tag(struct csdev_access *csa) @@ -158,7 +162,11 @@ void coresight_clear_self_claim_tag_unlocked(struct cs= dev_access *csa) { csdev_access_relaxed_write32(csa, CORESIGHT_CLAIM_SELF_HOSTED, CORESIGHT_CLAIMCLR); +#if defined(__riscv) + local_flush_icache_all(); +#else isb(); +#endif } EXPORT_SYMBOL_GPL(coresight_clear_self_claim_tag_unlocked); =20 diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwt= racing/coresight/coresight-etm-perf.c index 72017dcc3b7f..70a6aaffbf9d 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwt= racing/coresight/coresight-etm-perf.h index 24d929428633..e48c0ad46db1 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.h +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -58,6 +58,27 @@ struct cscfg_config_desc; #define ATTR_CFG_FLD_cc_threshold_LO 0 #define ATTR_CFG_FLD_cc_threshold_HI 11 =20 +#define __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \ + (lo) =3D=3D (hi) ? #cfg ":" #lo "\n" : #cfg ":" #lo "-" #hi + +#define _GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \ + __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) + +#define GEN_PMU_FORMAT_ATTR(name) \ + PMU_FORMAT_ATTR(name, \ + _GEN_PMU_FORMAT_ATTR(ATTR_CFG_FLD_##name##_CFG, \ + ATTR_CFG_FLD_##name##_LO, \ + ATTR_CFG_FLD_##name##_HI)) + +#define _ATTR_CFG_GET_FLD(attr, cfg, lo, hi) \ + ((((attr)->cfg) >> lo) & GENMASK_ULL(hi - lo, 0)) + +#define ATTR_CFG_GET_FLD(attr, name) \ + _ATTR_CFG_GET_FLD(attr, \ + ATTR_CFG_FLD_##name##_CFG, \ + ATTR_CFG_FLD_##name##_LO, \ + ATTR_CFG_FLD_##name##_HI) + /** * struct etm_filter - single instruction range or start/stop configuratio= n. * @start_addr: The address to start tracing on. diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwt= racing/coresight/coresight-platform.c index 0db64c5f4995..261ba6a75b86 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -14,7 +14,6 @@ #include #include #include -#include =20 #include "coresight-priv.h" =20 diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtr= acing/coresight/coresight-tmc-etf.c index 8882b1c4cdc0..dc366f4a5ca8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -661,7 +661,11 @@ static int tmc_panic_sync_etf(struct coresight_device = *csdev) * Make sure all previous writes are ordered, * before we mark valid */ +#if defined(__riscv) + __mb(); +#else dmb(sy); +#endif mdata->valid =3D true; /* * Below order need to maintained, since crc of metadata diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtr= acing/coresight/coresight-tmc-etr.c index 4dc1defe27a5..ac379d1751e6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1883,7 +1883,11 @@ static int tmc_panic_sync_etr(struct coresight_devic= e *csdev) * Make sure all previous writes are ordered, * before we mark valid */ +#if defined(__riscv) + __mb(); +#else dmb(sy); +#endif mdata->valid =3D true; /* * Below order need to maintained, since crc of metadata --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbguseast2.qq.com (smtpbguseast2.qq.com [54.204.34.130]) (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 49891883F for ; Tue, 14 Apr 2026 03:43:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.204.34.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138221; cv=none; b=oCHpuAHQ+VTmdo6PFd5nnOBNNFSL+WXrBzz7gq3FvCoiFSOuA4LXFHMigiY76FceFyFlPXVxVs8bTiy5TxmJRF09C+tU1/LLI1oImeasVCVXajGY+s1fa58UD9RwVMLjKL0y4Lx0ascnOJRlVsdiognIHAK4byo66fDUA/jGG1g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138221; c=relaxed/simple; bh=JWgNy3IhH2AjLG8/qrRs6jIBjNGCwI2CXuZ9KcitEQg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=T5vG5kgjyYEamM7R9SETjqNzzs1Ex2hvvT6Mjf+s9RMpACSAQXWTq584gKdS0XR0x6+sUOZihQrvtkiS2dBkrNPEB12ugHMV8CcYVyJEOD+KRw3dm9mQGT0RzYd7Jr8s6Zr3CrIP9m8IlqxnkwPwIuxd/MP6wqlUbhYVlQ3bBy4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=nX7OXDTk; arc=none smtp.client-ip=54.204.34.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="nX7OXDTk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138130; bh=iSbOge2rZ0clwseYaYE9Cu/TuLiV5T3RqaR0b6WSJmw=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=nX7OXDTkRZ/VD5/XQ3Mz/OpBxhOavI2EzQuOGuMMthRzNT3bCdOg4N4TnARH1KmqS rZgUAHUARsK8jyxuWKf8U7rONTf95eW1jljw/Djljj14rR7gi0XcLAZKQgroPzUnxG iZn090e58M7yEcKBHWExJJSd5QraDAHrIwmoW3jI= X-QQ-mid: zesmtpsz5t1776138124t358ef25d X-QQ-Originating-IP: CzPsqtwPvjJQ2v52XxmFdlX/powKwJi1Tn4QUEhQpsg= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:02 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 3322854439866736697 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver Date: Tue, 14 Apr 2026 11:41:43 +0800 Message-Id: <20260414034153.3272485-3-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: MNy0Dt/igafiV0e7XRLURMA+3lt2iaTndsrRal+IzxK3/FjoLU1fmUM7 B6+w9RywC8jP6+JoHauqZtPHjn68gdVKjvd6KXFpvp3CB60UXkq1eR1xcib6KqKzuPLg154 CBlHixSjr/Xsg8pnZ2y9ktp9FWpm1/oRbTARDFQAv6PEAwyNjzYhwvStyJn+i9AkyheuTuI dyU9rU4TSA2cGwCrQSSTEiE59loGL7YNZFaL8hNFsffXHntnP5Tx/ZdcgY46qLcmTwRLX6l Mzd2nWqqyaLhnLfDPtR6ZUIJ1KKyo+kvQxw3YDgUdiWwqNszRPQpXy3gs/eTwiakZCWJTzD T3JcPUjITBxBnwwLmT7Nzqreu3JLf6b1tZMZObX7ZyDU+WM3yyqznXdKrl92yJaguQdA9hV M50Lrgj8SlQXPZfTjpMj26N8alnztMh11xj2ViJUXYuaXWlzoNKdRifLPW+c8HR0SF1Ku2H 6LvlBkGyuYT1cbwwW5npsjeC8Chwvm5HmIi8uTBIW1KfrJskseTjDv9FEf3xtvsZksp2rsY oyR1idIPqsTDE2rRhedXjS8FNBMcjQDsfioU6ocWdMQNRHs/S7n8XNM/+NPBzKEFrueDV9M w8A0Nz/n7NhQ5ReJUk3d2u5tKPRZZjK9aZ9j/vwA7E3zLkdBVCmg9tSssvD4XdIvGfGmcq9 +8/B0TXXgl2bnx+S1ApLkdSdrk8IFVvb8dgbd9gSWuQ+t1JRT9wKBJB/0ntv+uv1ICJojP9 h35f6v/3MeUq26356Euvq00M8kgrft8FADVwgPw8Ztirkd68vIZyInLe0i5FnSn++anMiK2 c4Xt7TkjygWzngnIkcjBCVFQdCut6vT7tzykRLJ4qAoRbyBb8vnUKMEBH12ezKhXgoDf1S0 xqouAoF4ALh4UL/laP89xwUpsUJEwAszawqwAiIwi2YV07iOs/uxJwXUYHvrePXysjALCEQ 2DTIfRg4GfODijj5X3GgXUHFkN0CKN0lDtonYy4RgPy48tj0eS4RHA1qVb1Mk1rw1lZ073e qoheKRzlmnCtoD529EfeiKLcjq8iOVEmLQGTaHbjetMepMo+CGjHwjgn3n/i5g+RPHzME5t oQykfBkXrhLst4k/aCazxT4OUw0sgUXipGoVfvFOOtKWFNBaImd19qLnAGt9FkShaccs872 2/AD X-QQ-XMRINFO: MPJ6Tf5t3I/ylTmHUqvI8+Wpn+Gzalws3A== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Implement some common driver interfaces RISC-V trace where RISC-V trace components are instantiated by a common platform driver and a separate RISC-V trace driver for each type of RISC-V trace component. Signed-off-by: liangzhen --- drivers/hwtracing/coresight/Kconfig | 8 ++ drivers/hwtracing/coresight/Makefile | 2 + drivers/hwtracing/coresight/rvtrace-core.c | 135 +++++++++++++++++++++ include/linux/rvtrace.h | 116 ++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c create mode 100644 include/linux/rvtrace.h diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresi= ght/Kconfig index 2b1ebe3f614d..5adeaf78a080 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -280,4 +280,12 @@ config CORESIGHT_TNOC To compile this driver as a module, choose M here: the module will be called coresight-tnoc. =20 +config RVTRACE + bool "RISC-V Trace Support" + help + This enables support for the RISC-V trace drivers. drivers + (including Trace Encoder, Trace Funnel and ATB Bridge) are + dynamically aggregated with CoreSight trace infrastructure + at run time to form a complete trace path. + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/cores= ight/Makefile index ab16d06783a5..c21a9e25e148 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -57,3 +57,5 @@ obj-$(CONFIG_CORESIGHT_DUMMY) +=3D coresight-dummy.o obj-$(CONFIG_CORESIGHT_CTCU) +=3D coresight-ctcu.o coresight-ctcu-y :=3D coresight-ctcu-core.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) +=3D coresight-kunit-tests.o +obj-$(CONFIG_RVTRACE) +=3D rvtrace.o +rvtrace-y :=3D rvtrace-core.o diff --git a/drivers/hwtracing/coresight/rvtrace-core.c b/drivers/hwtracing= /coresight/rvtrace-core.c new file mode 100644 index 000000000000..c74f43869d8b --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-core.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include +#include + +int rvtrace_poll_bit(struct rvtrace_component *comp, int offset, + int bit, int bitval) +{ + int i =3D RVTRACE_TIMEOUT_US; + u32 val; + + while (i--) { + val =3D readl_relaxed(comp->base + offset); + if (((val >> bit) & 0x1) =3D=3D bitval) + break; + udelay(1); + } + + return (i < 0) ? -ETIMEDOUT : 0; +} +EXPORT_SYMBOL_GPL(rvtrace_poll_bit); + +int rvtrace_enable_component(struct rvtrace_component *comp) +{ + u32 val; + + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + val |=3D BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT); + writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1); +} +EXPORT_SYMBOL_GPL(rvtrace_enable_component); + +int rvtrace_disable_component(struct rvtrace_component *comp) +{ + u32 val; + + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + val &=3D ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT); + writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0); +} +EXPORT_SYMBOL_GPL(rvtrace_disable_component); + +int rvtrace_component_reset(struct rvtrace_component *comp) +{ + int ret; + + writel_relaxed(0, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + ret =3D rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0); + if (ret) + return ret; + + writel_relaxed(RVTRACE_COMPONENT_CTRL_ACTIVE_MASK, + comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1); +} +EXPORT_SYMBOL_GPL(rvtrace_component_reset); + +struct rvtrace_component *rvtrace_register_component(struct platform_devic= e *pdev) +{ + int ret; + void __iomem *base; + struct device *dev =3D &pdev->dev; + struct rvtrace_component *comp; + struct resource *res; + struct device_node *node; + u32 impl, type, major, minor; + + comp =3D devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); + if (!comp) { + ret =3D -ENOMEM; + goto err_out; + } + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + base =3D devm_ioremap_resource(dev, res); + if (IS_ERR(base)) { + ret =3D -EINVAL; + goto err_out; + } + comp->base =3D base; + + comp->cpu =3D -1; + for (int i =3D 0; ; i++) { + node =3D of_parse_phandle(dev->of_node, "cpus", i); + if (!node) + break; + + ret =3D of_cpu_node_to_id(node); + of_node_put(node); + if (ret >=3D 0 && cpu_online(ret)) { + comp->cpu =3D ret; + break; + } + } + + if (comp->cpu < 0) { + dev_err(dev, "No valid CPU found in 'cpus' property\n"); + ret =3D -EINVAL; + goto err_out; + } + + ret =3D rvtrace_component_reset(comp); + if (ret) + goto err_out; + comp->was_reset =3D true; + + impl =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_IMPL_OFFSET); + type =3D (impl >> RVTRACE_COMPONENT_IMPL_TYPE_SHIFT) & + RVTRACE_COMPONENT_IMPL_TYPE_MASK; + major =3D (impl >> RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT) & + RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK; + minor =3D (impl >> RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT) & + RVTRACE_COMPONENT_IMPL_VERMINOR_MASK; + + comp->id.type =3D type; + comp->id.version =3D rvtrace_component_mkversion(major, minor); + + return comp; + +err_out: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(rvtrace_register_component); diff --git a/include/linux/rvtrace.h b/include/linux/rvtrace.h new file mode 100644 index 000000000000..e7028d82f8fd --- /dev/null +++ b/include/linux/rvtrace.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#ifndef __LINUX_RVTRACE_H__ +#define __LINUX_RVTRACE_H__ + +#include +#include +#include +#include +#include + +/* Control register common across all RISC-V trace components */ +#define RVTRACE_COMPONENT_CTRL_OFFSET 0x000 +#define RVTRACE_COMPONENT_CTRL_ACTIVE_MASK 0x1 +#define RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT 0 +#define RVTRACE_COMPONENT_CTRL_ENABLE_MASK 0x1 +#define RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT 1 +#define RVTRACE_COMPONENT_CTRL_EMPTY_SHIFT 3 + +/* Implementation register common across all RISC-V trace components */ +#define RVTRACE_COMPONENT_IMPL_OFFSET 0x004 +#define RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK 0xf +#define RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT 0 +#define RVTRACE_COMPONENT_IMPL_VERMINOR_MASK 0xf +#define RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT 4 +#define RVTRACE_COMPONENT_IMPL_TYPE_MASK 0xf +#define RVTRACE_COMPONENT_IMPL_TYPE_SHIFT 8 + +#define RVTRACE_TIMEOUT_US 100 + +/* Possible component types defined by the RISC-V Trace Control Interface = */ +enum rvtrace_component_type { + RVTRACE_COMPONENT_TYPE_RESV0, + RVTRACE_COMPONENT_TYPE_ENCODER, /* 0x1 */ + RVTRACE_COMPONENT_TYPE_RESV2, + RVTRACE_COMPONENT_TYPE_RESV3, + RVTRACE_COMPONENT_TYPE_RESV4, + RVTRACE_COMPONENT_TYPE_RESV5, + RVTRACE_COMPONENT_TYPE_RESV6, + RVTRACE_COMPONENT_TYPE_RESV7, + RVTRACE_COMPONENT_TYPE_FUNNEL, /* 0x8 */ + RVTRACE_COMPONENT_TYPE_RAMSINK, /* 0x9 */ + RVTRACE_COMPONENT_TYPE_PIBSINK, /* 0xA */ + RVTRACE_COMPONENT_TYPE_RESV11, + RVTRACE_COMPONENT_TYPE_RESV12, + RVTRACE_COMPONENT_TYPE_RESV13, + RVTRACE_COMPONENT_TYPE_ATBBRIDGE, /* 0xE */ + RVTRACE_COMPONENT_TYPE_RESV15, + RVTRACE_COMPONENT_TYPE_MAX +}; + +/* Encoding/decoding macros for RISC-V trace component version */ +#define rvtrace_component_version_major(__version) \ + (((__version) >> 16) & 0xffff) +#define rvtrace_component_version_minor(__version) \ + ((__version) & 0xffff) +#define rvtrace_component_mkversion(__major, __minor) \ + ((((__major) & 0xffff) << 16) | ((__minor) & 0xffff)) + +/** + * struct rvtrace_component_id - Details to identify or match a RISC-V tra= ce component + * @type: Type of the component + * @version: Version of the component + * @data: Data pointer for driver use + */ +struct rvtrace_component_id { + enum rvtrace_component_type type; + u32 version; + void *data; +}; + +/** + * struct rvtrace_component - Representation of a RISC-V trace component + * base: Memory mapped base address for the component + * id: Details to match the component + * dev: Device instance + * cpu: The cpu this component is affined to + * was_reset: Flag showing whether RISC-V trace driver was reset su= ccessfully + */ +struct rvtrace_component { + void __iomem *base; + struct rvtrace_component_id id; + struct device *dev; + int cpu; + bool was_reset; +}; + +struct component_enable_arg { + struct rvtrace_component *comp; + int rc; +}; + +struct rvtrace_component *rvtrace_register_component(struct platform_devic= e *pdev); + +int rvtrace_poll_bit(struct rvtrace_component *comp, int offset, + int bit, int bitval); + +int rvtrace_enable_component(struct rvtrace_component *comp); +int rvtrace_disable_component(struct rvtrace_component *comp); +int rvtrace_component_reset(struct rvtrace_component *comp); + +static inline void *rvtrace_component_data(struct rvtrace_component *comp) +{ + return comp->id.data; +} + +static inline int rvtrace_comp_is_empty(struct rvtrace_component *comp) +{ + return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_EMPTY_SHIFT, 1); +} + +#endif --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbg150.qq.com (smtpbg150.qq.com [18.132.163.193]) (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 93A16337B99 for ; Tue, 14 Apr 2026 03:43:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=18.132.163.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138225; cv=none; b=NuiJtu+6kCFeIrrY8Wq6OfrDZDeug+J8G2oIWe4I90y5PyMelPJZnBCxwBbhsKzUDEg6guSQhKaDvXoWL1rS20ix0S5iGQweyUg5uJ7y65ZmUy4HD+lU+AGmrXQ3W2eF+9qOk0RcoDW/sdSQcgxY+/cJOGNg1eEKPAVpUrUg6hs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138225; c=relaxed/simple; bh=N7dsbV9Rm0g7nORHJGROg23ROI9FUTxfJw7qxVrtIqA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mAfqweHU4ey0BE1O188a3yoZhdnnwei86IoGgbiiY1XBExMzI6ZC10sDuod3YQnsQyUVZqYIxhUMi+43imNqbhoV82IpkK41gMjisCQOv53BGAGcSlyCw4A72EZeHJdUZc6DoGHJENaFkqBwiYg4PbyqnHwSiTHMkwxgsJHRc5M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=iQx79TlV; arc=none smtp.client-ip=18.132.163.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="iQx79TlV" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138134; bh=0bSsvlTQPtgZft1En88dfC6LD9r9mTj0HchmPAInZpU=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=iQx79TlVsvEndLu6wKeq1MhEmmFPCkC+dUusRJbKKzKznujKcZZcMWB9+a1GgFI2C JFyXndnpZPC33jTachlX4tPSEf5C2ZQji2+06gRdDWHi7mmoZsNTnfL8zIY5XbU5Gb EdRIvKJZnmp1EpxMGv9+hfslY33lElhV2ivpepF4= X-QQ-mid: zesmtpsz5t1776138128tf0e9a02d X-QQ-Originating-IP: B8mNHFkPhmtdfZ4ttm3zwqIdnDeDr5Qm/4UPzqb8Tx0= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:05 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 6506492689565388559 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver Date: Tue, 14 Apr 2026 11:41:44 +0800 Message-Id: <20260414034153.3272485-4-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: OJFmcA5TAQZeRdYzutaa00BoogBUaqKGOQX/rxN9MvEqBrgHfwTBSh79 MLwlwhM8o014fQ9DtrCydkVrqTvhnpGNVKboYFklh4/6hMeNCt39j5CcA4S5PoZomUv4oTp woL0fN6JdrJ7L3Go8eKwoOVMDQIj1fnmXQySqB5DqS+/ej/ES9oaee1OxU7wz92GsQ02sV0 UbTNFsliPHcT4kxGHm6SnWeKQm4a+xBEPO7gHMiU0Rlom5ZSuiwvFlMB5CGf0HLMjHEJ8Ke yPJtY2q+z7HHuTgOMxMNtJ6oJGLebc8hTxN6ME5sYfeuYfG6LQ/eEJ3lL71g3oktLMqGHOG Tk9Y3o5BmG9l/wrOi+L1e1PmZmJLMhQ5vwc41IYtjxGo8MaTHugRY5/GNihOzbRaSsxMvoY /+rI2zCBCByDEZueYGVyYJwGx/DTtnrlR07JvgMKoYDj2nHdbyiM5QrQOyF23zkr6/3aL3e Ig4YO15gU9xSbvBFezjIg1+sp/6dcWW/X8S+mMuUGi60Hy1ZmFi8nEMR2EYXoVgDzEbn2VJ o004m//uHyD4zyrvRFEmUAc6GnNdJzazXymECm55JQmV2nxH7x9UrQZ7tbOzOgZgyr6MW0l MHZlTAf27oRwHNfpBMHayHF2u6rL2Qd/Af1b01lE8TQEN0zLv6ZmPft85nN/0SeyZho6dIP N445HBFwfOWVyPE/O91z+wjYX8UbQxd3rIphd8KDVSTa57lLekNBhCuN6ih/CoNmsUZQOqT eY0T6BHzxqSxFIobmfiU/LRd/cSlVJHLd0w8np0jWAKUk2CULLiIx+P7YnbHTFVBOCepYnf qV5nwjxkEU+d1ww39lTz767o7X9K2346eLtQfbpfGtJqRPDEtErFLkQ0HF1f8gjPMxTSPAG wisI0GgUeRtmRn9mTWP6cGPupJIC4FMBOSYLw0Ww+3VZu/owF00bbco9ybhzM9udY32tJnw DG86aXKrXwGaWlj5vE+6UngTR0PCJkGW6tImYGobCR5aOacDI9uOCmmwEVLcliWpE3nH7vY 6yvKRrwrI9CX/S8Ay+cbjHAnkGq8tgbJ8qPVEKAW/jVtKS/UdQ9uh7Oz5Ut0PXZfc0VOjUi ER5gmpX/JYWOB82Zy1VmYcMC3OQgC0HJRz1pFHLi7voIY98YO3vX0ADysZvrbhGvA== X-QQ-XMRINFO: OD9hHCdaPRBwH5bRRRw8tsiH4UAatJqXfg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Add initial implementation of RISC-V trace encoder driver. The encoder is defined in the RISC-V Trace Control Interface specification. Signed-off-by: liangzhen --- drivers/hwtracing/coresight/Kconfig | 12 + drivers/hwtracing/coresight/Makefile | 2 + .../coresight/rvtrace-encoder-core.c | 524 ++++++++++++++++++ .../coresight/rvtrace-encoder-sysfs.c | 313 +++++++++++ drivers/hwtracing/coresight/rvtrace-encoder.h | 143 +++++ 5 files changed, 994 insertions(+) create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresi= ght/Kconfig index 5adeaf78a080..3e46728f2482 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -288,4 +288,16 @@ config RVTRACE dynamically aggregated with CoreSight trace infrastructure at run time to form a complete trace path. =20 +config RVTRACE_ENCODER + tristate "RISCV Trace Encoder driver" + depends on RVTRACE + help + This driver provides support for the Trace Encoder module, tracing + the instructions that a processor is executing. This is primarily + useful for instruction level tracing. Depending on the implemented + version data tracing may also be available. + + To compile this driver as a module, choose M here: the module + will be called rvtrace-encoder. + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/cores= ight/Makefile index c21a9e25e148..9a526b1fb95a 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -59,3 +59,5 @@ coresight-ctcu-y :=3D coresight-ctcu-core.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) +=3D coresight-kunit-tests.o obj-$(CONFIG_RVTRACE) +=3D rvtrace.o rvtrace-y :=3D rvtrace-core.o +obj-$(CONFIG_RVTRACE_ENCODER) +=3D rvtrace-encoder.o +rvtrace-encoder-y :=3D rvtrace-encoder-core.o rvtrace-encoder-sysfs.o diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-core.c b/drivers/h= wtracing/coresight/rvtrace-encoder-core.c new file mode 100644 index 000000000000..149a3cd97602 --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-encoder-core.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rvtrace-encoder.h" +#include "coresight-etm-perf.h" + +static int boot_enable; +module_param_named(boot_enable, boot_enable, int, 0444); + +static struct rvtrace_component *rvtrace_cpu_encoder[NR_CPUS]; + +static int encoder_cpu_id(struct coresight_device *csdev) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + + return comp->cpu; +} + +static void encoder_set_config(struct rvtrace_component *comp) +{ + u32 val; + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + struct device *dev =3D &encoder_data->csdev->dev; + struct encoder_config *config =3D &encoder_data->config; + + /* Configure Trace Encoder features register */ + val =3D (FIELD_PREP(RVTRACE_ENCODER_INST_NO_ADDR_DIFF, config->inst_na_di= ff) | + FIELD_PREP(RVTRACE_ENCODER_INST_NO_TRAP_ADDR, config->inst_nt_addr) | + FIELD_PREP(RVTRACE_ENCODER_INST_EN_SEQUENTIAL_JUMP, config->seq_jump)= | + FIELD_PREP(RVTRACE_ENCODER_INST_EN_IMPLICIT_RETURN, config->impl_ret)= | + FIELD_PREP(RVTRACE_ENCODER_INST_EN_BRANCH_PREDICTION, config->branch_= pred) | + FIELD_PREP(RVTRACE_ENCODER_INST_IMPLICIT_RETURN_MODE, config->impl_re= t_mode) | + FIELD_PREP(RVTRACE_ENCODER_INST_EN_REPEQTED_HISTORT, config->rep_hist= ) | + FIELD_PREP(RVTRACE_ENCODER_INST_EN_ALL_JUMPS, config->all_jumps) | + FIELD_PREP(RVTRACE_ENCODER_INST_EXTEND_ADDR_MSB, config->ext_msb) | + FIELD_PREP(RVTRACE_ENCODER_SRCID, config->srcid) | + FIELD_PREP(RVTRACE_ENCODER_SRCBITS, config->srcb)); + + writel_relaxed(val, comp->base + RVTRACE_ENCODER_INST_FTRS_OFFSET); + + /* Check Trace Encoder features register WARL bits could be set */ + val =3D readl_relaxed(comp->base + RVTRACE_ENCODER_INST_FTRS_OFFSET); + if (BMVAL(val, 0, 0) !=3D config->inst_na_diff) { + dev_warn(dev, "trTeInstNoAddrDiff %#x is not supported\n", + config->inst_na_diff); + config->inst_na_diff =3D BMVAL(val, 0, 0); + } + + if (BMVAL(val, 1, 1) !=3D config->inst_nt_addr) { + dev_warn(dev, "trTeInstNoTrapAddr %#x is not supported\n", + config->inst_nt_addr); + config->inst_nt_addr =3D BMVAL(val, 1, 1); + } + + if (BMVAL(val, 2, 2) !=3D config->seq_jump) { + dev_warn(dev, "trTeInstEnSequentialJump %#x is not supported\n", + config->seq_jump); + config->seq_jump =3D BMVAL(val, 2, 2); + } + + if (BMVAL(val, 3, 3) !=3D config->impl_ret) { + dev_warn(dev, "trTeInstEnImplicitReturn %#x is not supported\n", + config->impl_ret); + config->impl_ret =3D BMVAL(val, 3, 3); + } + + if (BMVAL(val, 4, 4) !=3D config->branch_pred) { + dev_warn(dev, "trTeInstEnBranchPrediction %#x is not supported\n", + config->branch_pred); + config->branch_pred =3D BMVAL(val, 4, 4); + } + + if (BMVAL(val, 5, 5) !=3D config->jump_target_cache) { + dev_warn(dev, "trTeInstEnJumpTargetCache %#x is not supported\n", + config->jump_target_cache); + config->jump_target_cache =3D BMVAL(val, 5, 5); + } + + if (BMVAL(val, 6, 7) !=3D config->impl_ret_mode) { + dev_warn(dev, "trTeInstImplicitReturnMode %#x is not supported\n", + config->impl_ret_mode); + config->impl_ret_mode =3D BMVAL(val, 6, 7); + } + + if (BMVAL(val, 8, 8) !=3D config->rep_hist) { + dev_warn(dev, "trTeInstEnRepeatedHistory %#x is not supported\n", + config->rep_hist); + config->rep_hist =3D BMVAL(val, 8, 8); + } + + if (BMVAL(val, 9, 9) !=3D config->all_jumps) { + dev_warn(dev, "trTeInstEnAllJumps %#x is not supported\n", + config->all_jumps); + config->all_jumps =3D BMVAL(val, 9, 9); + } + + if (BMVAL(val, 10, 10) !=3D config->ext_msb) { + dev_warn(dev, " trTeInstExtendAddrMSB %#x is not supported\n", + config->ext_msb); + config->ext_msb =3D BMVAL(val, 10, 10); + } + + if (BMVAL(val, 16, 27) !=3D config->srcid) { + dev_warn(dev, "trTeSrcID %#x is not supported\n", + config->srcid); + config->srcid =3D BMVAL(val, 16, 27); + } + + if (BMVAL(val, 28, 31) !=3D config->srcb) { + dev_warn(dev, "trTeSrcBits %#x is not supported\n", + config->srcb); + config->srcb =3D BMVAL(val, 28, 31); + } + + /* Configure Trace Encoder control register */ + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + val |=3D (FIELD_PREP(RVTRACE_ENCODER_INSTMODE, ENCODER_INSTMODE_HTM) | + FIELD_PREP(RVTRACE_ENCODER_CONTEXT, config->context) | + FIELD_PREP(RVTRACE_ENCODER_INSTTRIGEN, config->inst_trigger) | + FIELD_PREP(RVTRACE_ENCODER_INST_STALL_EN, config->inst_stall) | + FIELD_PREP(RVTRACE_ENCODER_INHBSRC, config->inhb_src) | + FIELD_PREP(RVTRACE_ENCODER_INSTSYNC_MODE, config->inst_syncmode) | + FIELD_PREP(RVTRACE_ENCODER_INSTSYNC_MAX, config->inst_syncmax)); + + + writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + + /* Check Trace Encoder control register WARL bits could be set */ + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + + if (BMVAL(val, 9, 9) !=3D config->context) { + dev_warn(dev, "trTeContext %#x is not supported\n", + config->context); + config->context =3D BMVAL(val, 9, 9); + } + + if (BMVAL(val, 11, 11) !=3D config->inst_trigger) { + dev_warn(dev, "trTeInstTrigEnable %#x is not supported\n", + config->inst_trigger); + config->inst_trigger =3D BMVAL(val, 11, 11); + } + + if (BMVAL(val, 13, 13) !=3D config->inst_stall) { + dev_warn(dev, "trTeInstStallEna %#x is not supported\n", + config->inst_stall); + config->inst_stall =3D BMVAL(val, 13, 13); + } + + if (BMVAL(val, 15, 15) !=3D config->inhb_src) { + dev_warn(dev, "trTeInhibitSrc %#x is not supported\n", + config->inhb_src); + config->inhb_src =3D BMVAL(val, 15, 15); + } + + if (BMVAL(val, 20, 23) !=3D config->inst_syncmax) { + dev_warn(dev, "trTeInstSyncMax %#x is not supported\n", + config->inst_syncmax); + config->inst_syncmax =3D BMVAL(val, 20, 23); + } +} + +static int encoder_enable_hw(struct rvtrace_component *comp) +{ + u32 val; + int ret; + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + if (!comp->was_reset) { + ret =3D rvtrace_component_reset(comp); + if (ret) + goto done; + } + + encoder_set_config(comp); + + ret =3D rvtrace_enable_component(comp); + if (ret) + goto done; + + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + val |=3D RVTRACE_ENCODER_ITRACE; + writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + ret =3D rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 1); + +done: + dev_dbg(&encoder_data->csdev->dev, "cpu: %d enable smp call done: %d\n", + comp->cpu, ret); + return ret; +} + +static void encoder_enable_hw_smp_call(void *info) +{ + struct component_enable_arg *arg =3D info; + + if (WARN_ON(!arg)) + return; + arg->rc =3D encoder_enable_hw(arg->comp); +} + +static int encoder_enable_perf(struct coresight_device *csdev) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(comp->cpu !=3D smp_processor_id())) + return -EINVAL; + + return encoder_enable_hw(comp); +} + +static int encoder_enable_sysfs(struct coresight_device *csdev) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + struct component_enable_arg arg =3D { }; + int ret; + + spin_lock(&encoder_data->spinlock); + + /* + * Executing encoder_enable_hw on the cpu whose trace encoder is being + * enabled ensures that register writes occur when cpu is powered. + */ + arg.comp =3D comp; + ret =3D smp_call_function_single(comp->cpu, + encoder_enable_hw_smp_call, &arg, 1); + if (!ret) + ret =3D arg.rc; + if (!ret) + encoder_data->sticky_enable =3D true; + + spin_unlock(&encoder_data->spinlock); + + if (!ret) + dev_dbg(&csdev->dev, "Trace Encoder tracing enabled\n"); + return ret; +} + +static int encoder_enable(struct coresight_device *csdev, struct perf_even= t *event, + enum cs_mode mode, __maybe_unused struct coresight_path *path) +{ + int ret; + + if (!coresight_take_mode(csdev, mode)) { + /* Someone is already using the tracer */ + return -EBUSY; + } + + switch (mode) { + case CS_MODE_SYSFS: + ret =3D encoder_enable_sysfs(csdev); + break; + case CS_MODE_PERF: + ret =3D encoder_enable_perf(csdev); + break; + default: + ret =3D -EINVAL; + } + + /* The tracer didn't start */ + if (ret) + coresight_set_mode(csdev, CS_MODE_DISABLED); + + return ret; +} + +static void encoder_disable_hw(struct rvtrace_component *comp) +{ + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + if (rvtrace_disable_component(comp)) + dev_err(&encoder_data->csdev->dev, + "timeout while waiting for Trace Encoder become disabled\n"); + + if (rvtrace_comp_is_empty(comp)) + dev_err(&encoder_data->csdev->dev, + "timeout while waiting for all generated trace have been emitted\n"); + + dev_dbg(&encoder_data->csdev->dev, "cpu: %d disable smp call done\n", com= p->cpu); +} + +static void encoder_disable_sysfs_smp_call(void *info) +{ + struct rvtrace_component *comp =3D info; + + encoder_disable_hw(comp); +} + +static void encoder_disable_sysfs(struct coresight_device *csdev) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + /* + * Taking hotplug lock here protects from clocks getting disabled + * with tracing being left on (crash scenario) if user disable occurs + * after cpu online mask indicates the cpu is offline but before the + * DYING hotplug callback is serviced by the trace encoder driver. + */ + cpus_read_lock(); + spin_lock(&encoder_data->spinlock); + + /* + * Executing encoder_disable_hw on the cpu whose trace encoder is being + * disabled ensures that register writes occur when cpu is powered. + */ + smp_call_function_single(comp->cpu, encoder_disable_sysfs_smp_call, comp,= 1); + + spin_unlock(&encoder_data->spinlock); + cpus_read_unlock(); + + dev_dbg(&csdev->dev, "Trace Encoder tracing disabled\n"); +} + +static void encoder_disable_perf(struct coresight_device *csdev) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(comp->cpu !=3D smp_processor_id())) + return; + + encoder_disable_hw(comp); +} + +static void encoder_disable(struct coresight_device *csdev, + struct perf_event *event) +{ + enum cs_mode mode; + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + mode =3D coresight_get_mode(csdev); + + switch (mode) { + case CS_MODE_DISABLED: + break; + case CS_MODE_SYSFS: + encoder_disable_sysfs(csdev); + break; + case CS_MODE_PERF: + encoder_disable_perf(csdev); + break; + default: + WARN_ON_ONCE(mode); + return; + } + + if (mode) + coresight_set_mode(csdev, CS_MODE_DISABLED); +} + +static const struct coresight_ops_source encoder_source_ops =3D { + .cpu_id =3D encoder_cpu_id, + .enable =3D encoder_enable, + .disable =3D encoder_disable, +}; + +static const struct coresight_ops encoder_cs_ops =3D { + .source_ops =3D &encoder_source_ops, +}; + +void encoder_set_default(struct rvtrace_component *comp) +{ + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + if (WARN_ON_ONCE(!encoder_data)) + return; + + struct encoder_config *config =3D &encoder_data->config; + + /* Enable sending trace messages/fields with scontext/mcontext values + * and/or privilege levels + */ + config->context =3D true; + + /* Allows trTeInstTracing to be set or cleared by Trace-on and Trace- + * off signals generated by the corresponding trigger module. + */ + config->inst_trigger =3D true; + + /* Enable periodic instruction trace synchronization */ + config->inst_syncmode =3D ENCODER_SYNCMODE_CLOCK; + config->inst_syncmax =3D 0x6; + + /* trace source ID*/ + config->srcid =3D encoder_cpu_id(encoder_data->csdev); + config->srcb =3D 0xc; +} + +static int encoder_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev =3D &pdev->dev; + struct coresight_platform_data *pdata; + struct encoder_data *encoder_data; + struct rvtrace_component *comp; + struct coresight_desc desc =3D { 0 }; + + comp =3D rvtrace_register_component(pdev); + if (IS_ERR(comp)) + return PTR_ERR(comp); + + encoder_data =3D devm_kzalloc(dev, sizeof(*encoder_data), GFP_KERNEL); + if (!encoder_data) + return -ENOMEM; + + spin_lock_init(&encoder_data->spinlock); + + pdata =3D coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + pdev->dev.platform_data =3D pdata; + + platform_set_drvdata(pdev, comp); + + desc.name =3D devm_kasprintf(dev, GFP_KERNEL, "encoder%d", comp->cpu); + if (!desc.name) + return -ENOMEM; + + desc.access =3D CSDEV_ACCESS_IOMEM(comp->base); + desc.type =3D CORESIGHT_DEV_TYPE_SOURCE; + desc.subtype.source_subtype =3D CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; + desc.ops =3D &encoder_cs_ops; + desc.pdata =3D pdata; + desc.dev =3D dev; + desc.groups =3D trace_encoder_groups; + encoder_data->csdev =3D coresight_register(&desc); + if (IS_ERR(encoder_data->csdev)) + return PTR_ERR(encoder_data->csdev); + + ret =3D etm_perf_symlink(encoder_data->csdev, true); + if (ret) { + coresight_unregister(encoder_data->csdev); + return ret; + } + + comp->id.data =3D encoder_data; + + rvtrace_cpu_encoder[comp->cpu] =3D comp; + + encoder_set_default(comp); + + dev_info(dev, "CPU%d: Trace Encoder initialized\n", comp->cpu); + + if (boot_enable) { + coresight_enable_sysfs(encoder_data->csdev); + encoder_data->boot_enable =3D true; + } + + return 0; +} + +static void clear_encodata(void *info) +{ + int cpu =3D *(int *)info; + + rvtrace_cpu_encoder[cpu]->id.data =3D NULL; + rvtrace_cpu_encoder[cpu] =3D NULL; +} + +static void encoder_remove(struct platform_device *pdev) +{ + struct rvtrace_component *comp =3D platform_get_drvdata(pdev); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + etm_perf_symlink(encoder_data->csdev, false); + + /* + * Taking hotplug lock here to avoid racing between encoder_remove and + * CPU hotplug call backs. + */ + cpus_read_lock(); + /* + * The readers for encodata[] are CPU hotplug call backs + * and PM notification call backs. Change encodata[i] on + * CPU i ensures these call backs has consistent view + * inside one call back function. + */ + if (smp_call_function_single(comp->cpu, clear_encodata, &comp->cpu, 1)) { + rvtrace_cpu_encoder[comp->cpu]->id.data =3D NULL; + rvtrace_cpu_encoder[comp->cpu] =3D NULL; + } + + cpus_read_unlock(); + + coresight_unregister(encoder_data->csdev); +} + +static const struct of_device_id encoder_match[] =3D { + {.compatible =3D "riscv,trace-encoder"}, + {}, +}; + +static struct platform_driver encoder_driver =3D { + .probe =3D encoder_probe, + .remove =3D encoder_remove, + .driver =3D { + .name =3D "trace-encoder", + .of_match_table =3D encoder_match, + }, +}; + +module_platform_driver(encoder_driver); + +MODULE_DESCRIPTION("RISC-V Trace Encoder Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c b/drivers/= hwtracing/coresight/rvtrace-encoder-sysfs.c new file mode 100644 index 000000000000..c89af4cb591a --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include "rvtrace-encoder.h" +#include "coresight-priv.h" + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + + val =3D comp->cpu; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(cpu); + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&encoder_data->spinlock); + + if (val) { + encoder_set_default(comp); + if (rvtrace_component_reset(comp)) { + comp->was_reset =3D false; + spin_unlock(&encoder_data->spinlock); + return -EINVAL; + } + } + + spin_unlock(&encoder_data->spinlock); + + return size; +} +static DEVICE_ATTR_WO(reset); + +static struct attribute *trace_encoder_attrs[] =3D { + &dev_attr_cpu.attr, + &dev_attr_reset.attr, + NULL, +}; + +static struct attribute *trace_encoder_mgmt_attrs[] =3D { + coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET), + coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET), + coresight_simple_reg32(features, RVTRACE_ENCODER_INST_FTRS_OFFSET), + NULL, +}; + +#define encoder_simple_rw(name) \ +static ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + unsigned long val; \ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); \ + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); \ + struct encoder_config *config =3D &encoder_data->config; \ + \ + val =3D config->name; \ + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); \ +} \ + \ +static ssize_t name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + unsigned long val; \ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); \ + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); \ + struct encoder_config *config =3D &encoder_data->config; \ + \ + if (kstrtoul(buf, 16, &val)) \ + return -EINVAL; \ + \ + spin_lock(&encoder_data->spinlock); \ + config->name =3D val; \ + spin_unlock(&encoder_data->spinlock); \ + \ + return size; \ +} \ +static DEVICE_ATTR_RW(name) + +static const char *const instmodes_str[] =3D { + [ENCODER_INSTMODE_OFF] =3D "off", + [ENCODER_INSTMODE_RESV1] =3D "resv1", + [ENCODER_INSTMODE_BTM] =3D "btm", + [ENCODER_INSTMODE_RESV4] =3D "resv4", + [ENCODER_INSTMODE_RESV5] =3D "resv5", + [ENCODER_INSTMODE_HTM] =3D "htm", + [ENCODER_INSTMODE_RESV7] =3D "resv7", +}; + +static ssize_t instmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + val =3D BMVAL(val, 4, 6); + + return sysfs_emit(buf, "%s\n", instmodes_str[val]); +} +static DEVICE_ATTR_RO(instmode); + +static const char *const inst_syncmodes_str[] =3D { + [ENCODER_SYNCMODE_OFF] =3D "off", + [ENCODER_SYNCMODE_MESSAGES] =3D "messages", + [ENCODER_SYNCMODE_CLOCK] =3D "clock", + [ENCODER_SYNCMODE_INSTRUCTION] =3D "instruction" +}; + +static ssize_t inst_syncmodes_available_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%s %s %s %s\n", + inst_syncmodes_str[ENCODER_SYNCMODE_OFF], + inst_syncmodes_str[ENCODER_SYNCMODE_MESSAGES], + inst_syncmodes_str[ENCODER_SYNCMODE_CLOCK], + inst_syncmodes_str[ENCODER_SYNCMODE_INSTRUCTION]); +} +static DEVICE_ATTR_RO(inst_syncmodes_available); + +static ssize_t inst_syncmode_preferred_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + struct encoder_config *config =3D &encoder_data->config; + + return sysfs_emit(buf, "%s\n", inst_syncmodes_str[config->inst_syncmode]); +} + +static ssize_t inst_syncmode_preferred_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + struct encoder_config *config =3D &encoder_data->config; + + if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_OFF])) + config->inst_syncmode =3D ENCODER_SYNCMODE_OFF; + else if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_MESSAGES])) + config->inst_syncmode =3D ENCODER_SYNCMODE_MESSAGES; + else if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_CLOCK])) + config->inst_syncmode =3D ENCODER_SYNCMODE_CLOCK; + else if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_INSTRUCTION= ])) + config->inst_syncmode =3D ENCODER_SYNCMODE_INSTRUCTION; + else + return -EINVAL; + + return size; +} +static DEVICE_ATTR_RW(inst_syncmode_preferred); + +static ssize_t format_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + val =3D BMVAL(val, 24, 26); + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(format); + +encoder_simple_rw(context); +encoder_simple_rw(inst_trigger); +encoder_simple_rw(inst_stall); +encoder_simple_rw(inhb_src); +encoder_simple_rw(inst_syncmax); + +static struct attribute *trace_encoder_control_attrs[] =3D { + &dev_attr_instmode.attr, + &dev_attr_context.attr, + &dev_attr_inst_trigger.attr, + &dev_attr_inst_stall.attr, + &dev_attr_inhb_src.attr, + &dev_attr_inst_syncmodes_available.attr, + &dev_attr_inst_syncmode_preferred.attr, + &dev_attr_inst_syncmax.attr, + &dev_attr_format.attr, + NULL, +}; + +static const char *const inst_implicit_return_modes_str[] =3D { + [ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED] =3D "off", + [ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING] =3D "simple", + [ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS] =3D "partial", + [ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS] =3D "full" +}; + +static ssize_t inst_implicit_return_modes_available_show(struct device *de= v, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%s %s %s %s\n", + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED= ], + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTI= NG], + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRE= SS], + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS]= ); +} +static DEVICE_ATTR_RO(inst_implicit_return_modes_available); + + +static ssize_t inst_implicit_return_mode_preferred_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + struct encoder_config *config =3D &encoder_data->config; + + return sysfs_emit(buf, "%s\n", inst_implicit_return_modes_str[config->imp= l_ret_mode]); +} + +static ssize_t inst_implicit_return_mode_preferred_store(struct device *de= v, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + struct encoder_config *config =3D &encoder_data->config; + + if (sysfs_streq(buf, + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED]= )) { + config->impl_ret_mode =3D ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED; + } else if (sysfs_streq(buf, + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTIN= G])) { + config->impl_ret_mode =3D ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING; + } else if (sysfs_streq(buf, + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRES= S])) { + config->impl_ret_mode =3D ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS; + } else if (sysfs_streq(buf, + inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS])= ) { + config->impl_ret_mode =3D ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS; + } else { + return -EINVAL; + } + return size; +} +static DEVICE_ATTR_RW(inst_implicit_return_mode_preferred); + +encoder_simple_rw(inst_na_diff); +encoder_simple_rw(inst_nt_addr); +encoder_simple_rw(seq_jump); +encoder_simple_rw(impl_ret); +encoder_simple_rw(branch_pred); +encoder_simple_rw(jump_target_cache); +encoder_simple_rw(rep_hist); +encoder_simple_rw(all_jumps); +encoder_simple_rw(ext_msb); +encoder_simple_rw(srcid); +encoder_simple_rw(srcb); + +static struct attribute *trace_encoder_features_attrs[] =3D { + &dev_attr_inst_na_diff.attr, + &dev_attr_inst_nt_addr.attr, + &dev_attr_seq_jump.attr, + &dev_attr_impl_ret.attr, + &dev_attr_branch_pred.attr, + &dev_attr_jump_target_cache.attr, + &dev_attr_inst_implicit_return_modes_available.attr, + &dev_attr_inst_implicit_return_mode_preferred.attr, + &dev_attr_rep_hist.attr, + &dev_attr_all_jumps.attr, + &dev_attr_ext_msb.attr, + &dev_attr_srcid.attr, + &dev_attr_srcb.attr, + NULL, +}; + +static const struct attribute_group trace_encoder_group =3D { + .attrs =3D trace_encoder_attrs, +}; + +static const struct attribute_group trace_encoder_mgmt_group =3D { + .attrs =3D trace_encoder_mgmt_attrs, + .name =3D "mgmt", +}; + +static const struct attribute_group trace_encoder_control_group =3D { + .attrs =3D trace_encoder_control_attrs, + .name =3D "control", +}; + +static const struct attribute_group trace_encoder_features_group =3D { + .attrs =3D trace_encoder_features_attrs, + .name =3D "features", +}; + +const struct attribute_group *trace_encoder_groups[] =3D { + &trace_encoder_group, + &trace_encoder_mgmt_group, + &trace_encoder_control_group, + &trace_encoder_features_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/rvtrace-encoder.h b/drivers/hwtrac= ing/coresight/rvtrace-encoder.h new file mode 100644 index 000000000000..44f2066ad869 --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-encoder.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#ifndef _RVTRACE_ENCODER_H +#define _RVTRACE_ENCODER_H + +#include +#include +#include "coresight-priv.h" + +/* Trace Encoder Control Register */ +#define RVTRACE_ENCODER_ITRACE BIT(2) +#define RVTRACE_ENCODER_INSTMODE GENMASK(6, 4) +#define RVTRACE_ENCODER_CONTEXT BIT(9) +#define RVTRACE_ENCODER_INSTTRIGEN BIT(11) +#define RVTRACE_ENCODER_INST_STALL_OR_OVERFLOW BIT(12) +#define RVTRACE_ENCODER_INST_STALL_EN BIT(13) +#define RVTRACE_ENCODER_INHBSRC BIT(15) +#define RVTRACE_ENCODER_INSTSYNC_MODE GENMASK(17, 16) +#define RVTRACE_ENCODER_INSTSYNC_MAX GENMASK(23, 20) + +#define RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT 2 + +/* Trace Encoder Implementation Register */ + +/* Trace Instruction Features Register */ +#define RVTRACE_ENCODER_INST_FTRS_OFFSET 0x008 +#define RVTRACE_ENCODER_INST_NO_ADDR_DIFF BIT(0) +#define RVTRACE_ENCODER_INST_NO_TRAP_ADDR BIT(1) +#define RVTRACE_ENCODER_INST_EN_SEQUENTIAL_JUMP BIT(2) +#define RVTRACE_ENCODER_INST_EN_IMPLICIT_RETURN BIT(3) +#define RVTRACE_ENCODER_INST_EN_BRANCH_PREDICTION BIT(4) +#define RVTRACE_ENCODER_INST_JUMP_TARGET_CACHE BIT(5) +#define RVTRACE_ENCODER_INST_IMPLICIT_RETURN_MODE GENMASK(7, 6) +#define RVTRACE_ENCODER_INST_EN_REPEQTED_HISTORT BIT(8) +#define RVTRACE_ENCODER_INST_EN_ALL_JUMPS BIT(9) +#define RVTRACE_ENCODER_INST_EXTEND_ADDR_MSB BIT(10) +#define RVTRACE_ENCODER_SRCID GENMASK(27, 16) +#define RVTRACE_ENCODER_SRCBITS GENMASK(31, 28) + +enum encoder_format { + ENCODER_FORMAT_ETRACE, + ENCODER_FORMAT_NTRACE, + ENCODER_FORMAT_UNKNOWN =3D 7 +}; + +enum encoder_instmode { + ENCODER_INSTMODE_OFF, + ENCODER_INSTMODE_RESV1, + ENCODER_INSTMODE_RESV2, + ENCODER_INSTMODE_BTM, + ENCODER_INSTMODE_RESV4, + ENCODER_INSTMODE_RESV5, + ENCODER_INSTMODE_HTM, + ENCODER_INSTMODE_RESV7 +}; + +enum encoder_syncmode { + ENCODER_SYNCMODE_OFF, + ENCODER_SYNCMODE_MESSAGES, + ENCODER_SYNCMODE_CLOCK, + ENCODER_SYNCMODE_INSTRUCTION +}; + +enum encoder_implicit_return_mode { + ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED, + ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING, + ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS, + ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS +}; + +/** + * struct encoder_config - configuration information related to an trace e= ncoder + * @context: Controls sending trace messages/fields with scontext/mcontext + * values and/or privilege levels. + * @inst_trig: Controls trTeInstTracing to be set or cleared by Trace-on = and + * Trace-off signals generated by the corresponding trigger module. + * @inst_stall: Controls whether hart is stalled until TE can send a mess= age. + * @inhb_src: Controls whether include source field in trace messages/pac= kets. + * @inst_syncmode: Periodic instruction trace synchronization message/pack= et + * generation mechanism. + * @inst_syncmax: The maximum interval between instruction trace synchroni= zation + * messages/packets. + * @trc_fmt: Trace recording/protocol format. + * @inst_na_diff: Controls whether trace messages/packets always carry a f= ull address. + * @inst_nt_addr: Controls whether include trap handler address in trap me= ssages/packets. + * @seq_jump: Controls the sequential jump optimization. + * @impl_ret: Controls the implicit return optimization. + * @branch_pred: Branch Predictor based compression. + * @jump_target_cache: Jump Target Cache based compression. + * @impl_ret_mode: Controls how the decoder is handling stack of return ad= dresses. + * @rep_hist: Controls the repeated history optimization. + * @all_jumps: Controls the emitting of trace message or add history/map = bit for + * direct unconditional/inferable control flow changes. + * @ext_msb: Controls the extended handing of MSB address bits. + * @srcid: Trace source ID assigned to this trace encoder. + * @srcb: The number of bits in the trace source field, unless disabled by + * inhibit_src. + */ +struct encoder_config { + bool context; + bool inst_trigger; + bool inst_stall; + bool inhb_src; + u8 inst_syncmode; + u8 inst_syncmax; + bool inst_na_diff; + bool inst_nt_addr; + bool seq_jump; + bool impl_ret; + bool branch_pred; + bool jump_target_cache; + u8 impl_ret_mode; + bool rep_hist; + bool all_jumps; + bool ext_msb; + u16 srcid; + u8 srcb; +}; + +/** + * struct encoder_drvdata - specifics associated to an Trace Encoder compo= nent + * @csdev: Component vitals needed by the framework. + * @spinlock: Only one at a time pls. + * @sticky_enable: True if trace encoder base configuration has been done. + * @boot_enable: True if we should start tracing at boot time. + * @config: Structure holding configuration parameters. + */ + +struct encoder_data { + struct coresight_device *csdev; + spinlock_t spinlock; + bool sticky_enable; + bool boot_enable; + struct encoder_config config; +}; + +extern const struct attribute_group *trace_encoder_groups[]; +void encoder_set_default(struct rvtrace_component *comp); + +#endif --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbg150.qq.com (smtpbg150.qq.com [18.132.163.193]) (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 5565D1DA23 for ; Tue, 14 Apr 2026 03:43:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=18.132.163.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138221; cv=none; b=XDDpb4EEwoEB6SgY36eM05yEvXmhWHSVLwXLxyJ51410I0jiC3yWyf9ytAVbR4MANxW7gPDi7tSJVGUxVlf++X8awazCVHhZoge/400rDTyTyXtq6ALHCdPUoHhmXbEpZcckhmYFRovVdRITyqqxptw9Xo2NXrsO/H1gTwqFqyc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138221; c=relaxed/simple; bh=828WWat1Xj4zjsKFUNTU5lpw2X6njlApBMRUGcspKNE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZTRJqgjQZ5uxpq5bact4IFNyIA6iTVMOMt5/CzejWW94gA/tQ6f4RxAXrGh2vAmxfPHCDfOiEFRw4RPn8fpdciX74y5wHrxkV/em02eJdP3xZIjxlJbglexhbSLjASHAtwLLJtCcWpLqGcdPN+uxjwXVAh3cuBCQ0IV2eU6KnbY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=wjkF2TKO; arc=none smtp.client-ip=18.132.163.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="wjkF2TKO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138137; bh=fiydWjDmo6mxg8g0lJHP5DHClz75B9Sfp0H5veYkkTE=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=wjkF2TKOjii5WBzudnU+nCSkqQXx8fqjYyK8lFJvuDHM0THLdGR8chkYwBmcDb2tz iXupKu9e7s3lcB0tlsGnUmYzLWFkp3s1CYqLKOTFwFl4oNvFgbdbF7nT+MwsIkUr0s 7KifWNqT8QCoRWg7WoqDmEuTV272wItOK0R8SgDg= X-QQ-mid: zesmtpsz5t1776138131t86ef943c X-QQ-Originating-IP: 57ysNez3/Fjz0GpYSZnkWeRb8mTL8sRweOCs59F/pfk= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:09 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 8828968199206424823 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver Date: Tue, 14 Apr 2026 11:41:45 +0800 Message-Id: <20260414034153.3272485-5-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: Nao9u9SdMvQSTMSWKjgfgwG97sPlh9bnkM9KUKvw/nkwVkF4dwhXe23T zj3KBfQdcVntDhK61BTRDRv3oG9gkQshRcO61y38y6hdo+3DaReZmK+0sAI0qgJcXJVYQHP SZbBbJslgPhkVeOc+nyyshbqtcr6noHkyLIuI9XLcOv88UQOBtqfhbnQEk41ISjNILN1E7G F9YTRMveCeEoa8OgJwMfVhrnGv4q39jwe4uyInUHHgt0Bhkddc2i9neESG/xWe8PxzGo3MM OolTxQGW/dXUan0O0Wuo6wrHk0SgVjPcy8/4T0C4wSyLQP28lVCKPbAAJXBZ/1Gqx899dnL 7YU9SZBfCgl6zEvcJ6pXBGgyWGbEgpOVm1Ojg0989lDQIpcsFxWTp1kG8In2Xx16uyY8iFv ZQyMBXuyzjORC0hSjp5SQMVWsoHtKRa96CoTj8w6z1U20+0qAbQwgrr9Upq9HcILbPLaiyM y1uBhXx8Raf7ViYwGntgl9Obwt2i+xXXK/rIrvbKBje+6bk/2ZxjmdO09JdlkJy/WwpKe/T vs7NyKPMp3MmugScs1xR6+6jcextWKZSHPBruJqotV4gSFnxF8Fy+QTb3OHoJMJznmPDazx 9XbVBUYVdflejWECipgSm56MEphlzAtHwoe4hB0XGR6pLhDYOymqw3er5E986Z5C0n0jKeB h7GxBMOUpEBX9Gq1qRB422Y5JNTd477FlhRfrj6NckfU83DMEscWQcxmSwJZbccpGizYENe N3BOZJXfqD8Rmqr9d99kE6Ta/91CEjRTo5qE311fRuf/22S3PAqX0XHLpIyYQBIj35ztmXg 54rpCZN5XJzRdVICcMtcT/CcN9Vbw4E7l3e+qdJnxkQbSE5Q5Di60Ocur7WkiVM747vcYSM CIcyVNHl0zvS2cmQZhCF+r0miPV1ROllFSL/NyDjH+KTD8vVWdC2ZIqAjYfixfEqYhwA6BI e+ZAlei0w7u0jHQMDGb5gDObx1SZuI/JDoFhQpoaODw4R2gNRz2Sbj7VT3ceFxWqzkOvUAQ sod2Pe3za5+cxyui+rUX70DwvS93I4LHQ9+tUFFZGoO5+0h6hqn/hvZXdj91bNtQg2bzc2N +oL2VCSz2yEZAo9ymnijTyYkIqmbz4+DRuuUXv4Fy4wCnOoJDivUIrYGxhEMGyqPMVgPbmQ ypZJ X-QQ-XMRINFO: OD9hHCdaPRBwH5bRRRw8tsiH4UAatJqXfg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Add initial implementation of RISC-V trace funnel driver. The funnel is defined in the RISC-V Trace Control Interface specification. Signed-off-by: liangzhen --- drivers/hwtracing/coresight/Kconfig | 12 + drivers/hwtracing/coresight/Makefile | 1 + drivers/hwtracing/coresight/rvtrace-funnel.c | 244 +++++++++++++++++++ drivers/hwtracing/coresight/rvtrace-funnel.h | 31 +++ 4 files changed, 288 insertions(+) create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresi= ght/Kconfig index 3e46728f2482..8381a84e2699 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -288,9 +288,21 @@ config RVTRACE dynamically aggregated with CoreSight trace infrastructure at run time to form a complete trace path. =20 +config RVTRACE_FUNNEL + tristate "RISCV Trace Funnel driver" + depends on RVTRACE + help + his driver provides support for the Trace Funnel driver. The Funnel + aggregates the trace from each of its inputs and sends the combined + trace stream to its designated Trace Sink or ATB Bridge. + + To compile this driver as a module, choose M here: the module + will be called rvtrace-funnel. + config RVTRACE_ENCODER tristate "RISCV Trace Encoder driver" depends on RVTRACE + select RVTRACE_FUNNEL help This driver provides support for the Trace Encoder module, tracing the instructions that a processor is executing. This is primarily diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/cores= ight/Makefile index 9a526b1fb95a..409a0abf0642 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -59,5 +59,6 @@ coresight-ctcu-y :=3D coresight-ctcu-core.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) +=3D coresight-kunit-tests.o obj-$(CONFIG_RVTRACE) +=3D rvtrace.o rvtrace-y :=3D rvtrace-core.o +obj-$(CONFIG_RVTRACE_FUNNEL) +=3D rvtrace-funnel.o obj-$(CONFIG_RVTRACE_ENCODER) +=3D rvtrace-encoder.o rvtrace-encoder-y :=3D rvtrace-encoder-core.o rvtrace-encoder-sysfs.o diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.c b/drivers/hwtraci= ng/coresight/rvtrace-funnel.c new file mode 100644 index 000000000000..0dc7799a64ac --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-funnel.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include + +#include "coresight-priv.h" +#include "rvtrace-funnel.h" + +DEFINE_CORESIGHT_DEVLIST(funnel_devs, "rvtrace_funnel"); + +static int funnel_enable_hw(struct rvtrace_component *comp, int port) +{ + u32 disinput; + int ret =3D 0; + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + if (!comp->was_reset) { + ret =3D rvtrace_component_reset(comp); + if (ret) + goto done; + } + + disinput =3D readl_relaxed(comp->base + RVTRACE_FUNNEL_DISINPUT_OFFSET); + disinput &=3D ~(1 << port); + writel_relaxed(disinput, comp->base + RVTRACE_FUNNEL_DISINPUT_OFFSET); + ret =3D rvtrace_poll_bit(comp, RVTRACE_FUNNEL_DISINPUT_OFFSET, port, 0); + if (ret) + goto done; + + if (!funnel_data->was_enabled) { + ret =3D rvtrace_enable_component(comp); + if (ret) + goto done; + } + +done: + return ret; +} + +static int funnel_enable(struct coresight_device *csdev, + struct coresight_connection *in, + struct coresight_connection *out) +{ + int ret =3D 0; + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + unsigned long flags; + bool first_enable =3D false; + + spin_lock_irqsave(&funnel_data->spinlock, flags); + if (in->dest_refcnt =3D=3D 0) { + ret =3D funnel_enable_hw(comp, in->dest_port); + if (!ret) + first_enable =3D true; + } + if (!ret) { + in->dest_refcnt++; + funnel_data->input_refcnt++; + } + + if (first_enable) + dev_dbg(&csdev->dev, "Trace funnel inport %d enabled\n", + in->dest_port); + + spin_unlock_irqrestore(&funnel_data->spinlock, flags); + + return ret; +} + +static void funnel_disable_hw(struct rvtrace_component *comp, int inport) +{ + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + if (--funnel_data->input_refcnt !=3D 0) + return; + + writel_relaxed(RVTRACE_FUNNEL_DISINPUT_MASK, comp->base + RVTRACE_FUNNEL_= DISINPUT_OFFSET); + + if (rvtrace_disable_component(comp)) + dev_err(&funnel_data->csdev->dev, + "timeout while waiting for Trace Funnel to be disabled\n"); + + if (rvtrace_comp_is_empty(comp)) + dev_err(&funnel_data->csdev->dev, + "timeout while waiting for Trace Funnel internal buffers become empty\n= "); +} + +static void funnel_disable(struct coresight_device *csdev, + struct coresight_connection *in, + struct coresight_connection *out) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + unsigned long flags; + bool last_disable =3D false; + + spin_lock_irqsave(&funnel_data->spinlock, flags); + if (--in->dest_refcnt =3D=3D 0) { + funnel_disable_hw(comp, in->dest_port); + last_disable =3D true; + } + spin_unlock_irqrestore(&funnel_data->spinlock, flags); + + if (last_disable) + dev_dbg(&csdev->dev, "Trace funnel inport %d disabled\n", + in->dest_port); + + if (funnel_data->input_refcnt =3D=3D 0) + dev_dbg(&csdev->dev, "Trace funnel disabled\n"); +} + +static const struct coresight_ops_link funnel_link_ops =3D { + .enable =3D funnel_enable, + .disable =3D funnel_disable, +}; + +static const struct coresight_ops funnel_cs_ops =3D { + .link_ops =3D &funnel_link_ops, +}; + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + unsigned long flags; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock_irqsave(&funnel_data->spinlock, flags); + + if (val) { + if (rvtrace_component_reset(comp)) { + comp->was_reset =3D false; + spin_unlock_irqrestore(&funnel_data->spinlock, flags); + return -EINVAL; + } + } + + spin_unlock_irqrestore(&funnel_data->spinlock, flags); + + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + + val =3D comp->cpu; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(cpu); + +static struct attribute *trace_funnel_attrs[] =3D { + coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET), + coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET), + coresight_simple_reg32(disinput, RVTRACE_FUNNEL_DISINPUT_OFFSET), + &dev_attr_reset.attr, + &dev_attr_cpu.attr, + NULL, +}; +ATTRIBUTE_GROUPS(trace_funnel); + +static int funnel_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct coresight_platform_data *pdata; + struct funnel_data *funnel_data; + struct rvtrace_component *comp; + struct coresight_desc desc =3D { 0 }; + + comp =3D rvtrace_register_component(pdev); + if (IS_ERR(comp)) + return PTR_ERR(comp); + + funnel_data =3D devm_kzalloc(dev, sizeof(*funnel_data), GFP_KERNEL); + if (!funnel_data) + return -ENOMEM; + + spin_lock_init(&funnel_data->spinlock); + + pdata =3D coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + pdev->dev.platform_data =3D pdata; + + platform_set_drvdata(pdev, comp); + + desc.name =3D coresight_alloc_device_name(&funnel_devs, dev); + desc.access =3D CSDEV_ACCESS_IOMEM(comp->base); + desc.type =3D CORESIGHT_DEV_TYPE_LINK; + desc.subtype.link_subtype =3D CORESIGHT_DEV_SUBTYPE_LINK_MERG; + desc.ops =3D &funnel_cs_ops; + desc.pdata =3D pdata; + desc.dev =3D dev; + desc.groups =3D trace_funnel_groups; + funnel_data->csdev =3D coresight_register(&desc); + if (IS_ERR(funnel_data->csdev)) + return PTR_ERR(funnel_data->csdev); + + comp->id.data =3D funnel_data; + + dev_dbg(dev, "Trace Funnel initialized\n"); + + return 0; +} + +static void funnel_remove(struct platform_device *pdev) +{ + struct rvtrace_component *comp =3D platform_get_drvdata(pdev); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + coresight_unregister(funnel_data->csdev); +} + +static const struct of_device_id funnel_match[] =3D { + {.compatible =3D "riscv,trace-funnel"}, + {}, +}; + +static struct platform_driver funnel_driver =3D { + .probe =3D funnel_probe, + .remove =3D funnel_remove, + .driver =3D { + .name =3D "trace-funnel", + .of_match_table =3D funnel_match, + }, +}; + +module_platform_driver(funnel_driver); + +MODULE_DESCRIPTION("RISC-V Trace funnel driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.h b/drivers/hwtraci= ng/coresight/rvtrace-funnel.h new file mode 100644 index 000000000000..e5247e6c1bf5 --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-funnel.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#ifndef _RVTRACE_FUNNEL_H +#define _RVTRACE_FUNNEL_H + +#include +#include + +/* Disable Individual Funnel Inputs */ +#define RVTRACE_FUNNEL_DISINPUT_OFFSET 0x008 +#define RVTRACE_FUNNEL_DISINPUT_MASK 0xffff + +/** + * struct funnel_data - specifics associated to a Trace Funnel component + * @csdev: Component vitals needed by the framework. + * @spinlock: Only one at a time pls. + * @was_enabled: Flag showing whether the Trace Funnel was enabled. + * @input_refcnt: Record the number of funnel inputs + */ +struct funnel_data { + struct coresight_device *csdev; + spinlock_t spinlock; + bool was_enabled; + u32 input_refcnt; + u32 disintput; +}; + +#endif --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbguseast1.qq.com (smtpbguseast1.qq.com [54.204.34.129]) (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 9F7412FE07D for ; Tue, 14 Apr 2026 03:43:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.204.34.129 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138222; cv=none; b=YNpFfKGioRWU7A1Te1yNUY2IxFqeffuLAosjmrf81qnUxeDNfqaEms96Ho46scQHRDErp5c1dSk8bYoya/Xh/vwOf+WdiL7VIvEoGN4444MjO7HJUr2PlycntXQpbe/El+Kc/COchBnVx7YNiOjp7oYlF/sHBqV/FiHMC822IqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138222; c=relaxed/simple; bh=DCXW0+IK3XUcRpizq+bCxJiOtTsfU7QsumESqCN7fqI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XhRgv2+xaUQCgJrNHDlZqFqxRvyc/B2+LwUUsl2UZBF61Y8vJCYdB5bz1ih6uuWWQxf0ifsO3nzw4ZsLZuE4YASlYLvXrvxN4jYcuhijVhdFyZOExBAv3ugdE5aTN2kwZqFRQwMF7au0shfvMphiL3kcXicdYBetRaCMDTaHcxk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=U6pv2oEb; arc=none smtp.client-ip=54.204.34.129 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="U6pv2oEb" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138142; bh=P3GVjBCXRU7adb+cVqE903QHuJnoDiq/UFyGJ9773gI=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=U6pv2oEb/VH7XWiii/fIaFBYnaGVSkbMLtlNVFVF0CStVBfFsa4PU/fNnhPKL/a7f /NSzt5hamAYNSOJlD++iGE6sEMBr5NjR0hk3PqH0JDfH7cDZh1l0G6HBld+h/Ti6/J 26IVuMV6kXGAbkFrj4kpe8EMzwxBihQs/344ru6w= X-QQ-mid: zesmtpsz5t1776138135tb7d97203 X-QQ-Originating-IP: A4h7SvXs75OmbzLY+NVyMu9/GokZCmGtd1XspNpuU8g= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:13 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 1804587517364881883 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver Date: Tue, 14 Apr 2026 11:41:46 +0800 Message-Id: <20260414034153.3272485-6-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: MJsUkRPIFlJqGbRLbAHkPi0YYfAcjp5XDk/ZvfNah5muU0GU7AN8rtbL T5e5Ait4QyPzPuxJwxP/5XbUHdmmq03m+h9Ja5AJBbjnbY9qRoQQnYCBdYTCQlg4xP4P3e+ BwRw7+Dlp9ZOqGLt5uH71P9GGEe+LW5BF4ceyH0Zb3cumKzf+f9od/nszhm910eMVey+HVu gg/bb5jl1GM8MgOWesSAo9YPuWN47GaL8Srk9VBPtFoXg6AYUT+Kd8JzalwTfS5hi/ec1dN ey0TMLGKzNF0zreyW134W4fvEGAmkFtMh09Uu7jygMf+jag7D6Sq/cL423Yefw/cN0fcI1K NiRHnmJOJkTmX+rON4weN0ecx+QOSkRs+4TxQENs7Wn8Rs8a5ZcCA10v6SNzw8Y/KtYp0zC ao21VmrFw0OC7EQEkRv/Ys4l9Ez2hmz5dTV+D7JbfIzO/W5JJCLsVU2HLZYBd3qykrMgSqQ fOs5fZbzg1WZEYToAt9TCo7l1vuyfTecfK1hWAuk0VujFaAbnW895G1yfhWFRfNYELtsqE7 rfrfAh43m18xL08OK0iUHqvNSF8D0TuZxhjedsG3tTn722QXRJsKSY3iIFwaaGSjLVYL+WX sGb95TjCPK1gBYKjOD0KiPGauMlqtg6tTL7wNm5SB6gduArC64YVJjO/+IwOBACjYY1YFUf Gi/mVXTvfRcfuczQ1YX2jsRKbvO0SExp2zeyIQ5zEwdpETb4Cwlikf3/2URAd0JRey2FyhI hXDQdzLLpe9yK3zAG8wk9SToAI9V8Hell+hb2U7reWrtu1VPsRgz+rkUVoF7FvPp+D1Q8dT uaPXbz9WyH4kgIkRiOifYDlN/8hSftPxV+rS8gt7Sio3FU/Hy64of7UUM2iRnzRjfeuwUY0 Jkwz2mkztAx4Ce0RQWYlhMMf1LwdtKzOdKlxzNaEof9KOvsE0TiZ6H9cTYWEt9xr+q3nbLx rYtnV8srUJBSGMDO52I1HJqvRnDegJncyKJ5ptZj3kjg8MhoHWERoXfxbE2tMyeU61OGZ5R 6C+plzew1sQIjGTqeBp2pu7R+rzSO3Yi0mFGQP7KaL+PTy6lKs+1g+63qMDbtpynQWEYFqO fGJNF6emADeD9Uznbe2dn7DSN+GvQDCihrswVT6fCwq X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Add initial implementation of RISC-V trace ATB Bridge driver. The ATB Bridge is defined in the RISC-V Trace Control Interface specification. Signed-off-by: liangzhen --- drivers/hwtracing/coresight/Kconfig | 12 + drivers/hwtracing/coresight/Makefile | 1 + .../hwtracing/coresight/rvtrace-atbbridge.c | 239 ++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresi= ght/Kconfig index 8381a84e2699..1cd88a1a7d08 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -288,6 +288,17 @@ config RVTRACE dynamically aggregated with CoreSight trace infrastructure at run time to form a complete trace path. =20 +config RVTRACE_ATBBRIDGE + tristate "RISCV Trace ATB Bridge driver" + depends on RVTRACE + help + This driver provides support for the ATB Bridge driver. The ATB + Bridge allows sending RISC-V trace to Arm CoreSight infrastructure + (instead of RISC-V compliant sink) as an ATB initiator. + + To compile this driver as a module, choose M here: the module + will be called rvtrace-atbbridge. + config RVTRACE_FUNNEL tristate "RISCV Trace Funnel driver" depends on RVTRACE @@ -303,6 +314,7 @@ config RVTRACE_ENCODER tristate "RISCV Trace Encoder driver" depends on RVTRACE select RVTRACE_FUNNEL + select RVTRACE_ATBBRIDGE help This driver provides support for the Trace Encoder module, tracing the instructions that a processor is executing. This is primarily diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/cores= ight/Makefile index 409a0abf0642..2264a313a773 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -59,6 +59,7 @@ coresight-ctcu-y :=3D coresight-ctcu-core.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) +=3D coresight-kunit-tests.o obj-$(CONFIG_RVTRACE) +=3D rvtrace.o rvtrace-y :=3D rvtrace-core.o +obj-$(CONFIG_RVTRACE_ATBBRIDGE) +=3D rvtrace-atbbridge.o obj-$(CONFIG_RVTRACE_FUNNEL) +=3D rvtrace-funnel.o obj-$(CONFIG_RVTRACE_ENCODER) +=3D rvtrace-encoder.o rvtrace-encoder-y :=3D rvtrace-encoder-core.o rvtrace-encoder-sysfs.o diff --git a/drivers/hwtracing/coresight/rvtrace-atbbridge.c b/drivers/hwtr= acing/coresight/rvtrace-atbbridge.c new file mode 100644 index 000000000000..a931be322a2f --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-atbbridge.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include "coresight-priv.h" +#include "coresight-trace-id.h" + +#define RVTRACE_ATBBRIDGE_CONTROL_ID_MASK GENMASK(14, 8) +/** + * struct atbbridge_data - specifics associated to a ATB bridge component + * @csdev: Component vitals needed by the framework. + * @spinlock: Only one at a time pls. + * @traceid: Value of the current ID for this component. + */ +struct atbbridge_data { + struct coresight_device *csdev; + spinlock_t spinlock; + u8 traceid; +}; + +DEFINE_CORESIGHT_DEVLIST(atbbridge_devs, "atbbridge"); + +static int atbbridge_enable(struct coresight_device *csdev, + struct coresight_connection *in, + struct coresight_connection *out) +{ + u32 control; + int ret =3D 0; + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct atbbridge_data *atbbridge_data =3D rvtrace_component_data(comp); + unsigned long flags; + + spin_lock_irqsave(&atbbridge_data->spinlock, flags); + if (!comp->was_reset) { + ret =3D rvtrace_component_reset(comp); + if (ret) + goto done; + } + + control =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + control |=3D FIELD_PREP(RVTRACE_ATBBRIDGE_CONTROL_ID_MASK, atbbridge_data= ->traceid); + writel_relaxed(control, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); + + ret =3D rvtrace_enable_component(comp); + if (ret) + goto done; + + spin_unlock_irqrestore(&atbbridge_data->spinlock, flags); + + dev_dbg(&csdev->dev, "Trace ATB bridge enabled\n"); +done: + return ret; +} + +static void atbbridge_disable(struct coresight_device *csdev, + struct coresight_connection *in, + struct coresight_connection *out) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct atbbridge_data *atbbridge_data =3D rvtrace_component_data(comp); + unsigned long flags; + + spin_lock_irqsave(&atbbridge_data->spinlock, flags); + + if (rvtrace_disable_component(comp)) + dev_err(&csdev->dev, + "timeout while waiting for Trace ATB Bridge to be disabled\n"); + + if (rvtrace_comp_is_empty(comp)) + dev_err(&csdev->dev, + "timeout while waiting for Trace ATB Bridge internal buffers become emp= ty\n"); + + spin_unlock_irqrestore(&atbbridge_data->spinlock, flags); + + dev_dbg(&csdev->dev, "Trace ATB bridge disabled\n"); +} + +static int atbbridge_link_trace_id(struct coresight_device *csdev, __maybe= _unused enum cs_mode mode, + __maybe_unused struct coresight_device *sink) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(csdev->dev.parent); + struct atbbridge_data *atbbridge_data =3D rvtrace_component_data(comp); + + return atbbridge_data->traceid; +} + +static const struct coresight_ops_link atbbridge_link_ops =3D { + .enable =3D atbbridge_enable, + .disable =3D atbbridge_disable, +}; + +static const struct coresight_ops atbbridge_cs_ops =3D { + .trace_id =3D atbbridge_link_trace_id, + .link_ops =3D &atbbridge_link_ops, +}; + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct atbbridge_data *atbbridge_data =3D rvtrace_component_data(comp); + unsigned long flags; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + if (val) { + if (rvtrace_component_reset(comp)) { + comp->was_reset =3D false; + spin_unlock_irqrestore(&atbbridge_data->spinlock, flags); + return -EINVAL; + } + } + + spin_unlock_irqrestore(&atbbridge_data->spinlock, flags); + + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + + val =3D comp->cpu; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(cpu); + +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct atbbridge_data *atbbridge_data =3D rvtrace_component_data(comp); + + val =3D atbbridge_data->traceid; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(traceid); + +static struct attribute *trace_atbbridge_attrs[] =3D { + coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET), + coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET), + &dev_attr_reset.attr, + &dev_attr_cpu.attr, + &dev_attr_traceid.attr, + NULL, +}; +ATTRIBUTE_GROUPS(trace_atbbridge); + +static int atbbridge_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct coresight_platform_data *pdata; + struct atbbridge_data *atbbridge_data; + struct rvtrace_component *comp; + struct coresight_desc desc =3D { 0 }; + int trace_id =3D 0; + + comp =3D rvtrace_register_component(pdev); + if (IS_ERR(comp)) + return PTR_ERR(comp); + + atbbridge_data =3D devm_kzalloc(dev, sizeof(*atbbridge_data), GFP_KERNEL); + if (!atbbridge_data) + return -ENOMEM; + + spin_lock_init(&atbbridge_data->spinlock); + + pdata =3D coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + pdev->dev.platform_data =3D pdata; + + platform_set_drvdata(pdev, comp); + + desc.name =3D coresight_alloc_device_name(&atbbridge_devs, dev); + desc.access =3D CSDEV_ACCESS_IOMEM(comp->base); + desc.type =3D CORESIGHT_DEV_TYPE_LINK; + desc.subtype.link_subtype =3D CORESIGHT_DEV_SUBTYPE_LINK_FIFO; + desc.ops =3D &atbbridge_cs_ops; + desc.pdata =3D pdata; + desc.dev =3D dev; + desc.groups =3D trace_atbbridge_groups; + atbbridge_data->csdev =3D coresight_register(&desc); + if (IS_ERR(atbbridge_data->csdev)) + return PTR_ERR(atbbridge_data->csdev); + + trace_id =3D coresight_trace_id_get_system_id(); + if (trace_id < 0) + return -EINVAL; + atbbridge_data->traceid =3D (u8)trace_id; + + comp->id.data =3D atbbridge_data; + + dev_dbg(dev, "Trace ATB Bridge initialized\n"); + + return 0; +} + +static void atbbridge_remove(struct platform_device *pdev) +{ + struct rvtrace_component *comp =3D platform_get_drvdata(pdev); + struct atbbridge_data *atbbridge_data =3D rvtrace_component_data(comp); + + coresight_trace_id_put_system_id(atbbridge_data->traceid); + coresight_unregister(atbbridge_data->csdev); +} + +static const struct of_device_id atbbridge_match[] =3D { + {.compatible =3D "riscv,trace-atbbridge"}, + {}, +}; + +static struct platform_driver atbbridge_driver =3D { + .probe =3D atbbridge_probe, + .remove =3D atbbridge_remove, + .driver =3D { + .name =3D "trace-atbbridge", + .of_match_table =3D atbbridge_match, + }, +}; + +module_platform_driver(atbbridge_driver); + +MODULE_DESCRIPTION("RISC-V Trace ATB Bridge driver"); +MODULE_LICENSE("GPL"); --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbguseast3.qq.com (smtpbguseast3.qq.com [54.243.244.52]) (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 2CA1A30F95C for ; Tue, 14 Apr 2026 03:43:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.243.244.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138223; cv=none; b=a96PSA9aIixqfVvLJBh7cqX65w6j5ornGzyV/UMR8eM2PtZcf3x469yAaRhqiGjuszQFPpVQGBDsoOyhhGrFo7yf8WmPWVqG9PC5z0ERhdyDT+RWnbQP43oqKNXyvBryVWoS9+kil6DDOuaxNRZts5Ykmf0IpCUOOdeiWLG/RkM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138223; c=relaxed/simple; bh=BzY1x47t53mq5jnbFHX4yi4MiUpSl5ER/q/0770N1Go=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Dc1bA2d7kZ5BzV3oLYlOs3QghojyeGuf9bnAQMu4MhN+GZcNxtzy8pE+wKYZFHJaotFR86yze+QnCgrOj2WUpdY12faTzJRH0YEfyPJFe22c2MS2SJqkEtbreT6TYyW1S6aNtcLVxk5VctDJgSi08PQULD7fZGdljmh9z1cSlvU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=tIdj9fRq; arc=none smtp.client-ip=54.243.244.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="tIdj9fRq" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138144; bh=WMK2YSo3F/K+10mV2+1NOqzt6aQdzqbeUoYc7D0Ih4A=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=tIdj9fRql6OnS1w8ZXVaqwo76UkAlAXexnoK5AssLsDfoi6i35NJQUHP/lgBBsXci OgbnRrgqTfExfyd7Fs9B5F9QqmkZ3IMqd0Cqd/dfRz/DWGPtsT51O4A9BLNQ/3/DPY s1IzhWt5lWJIzY7tdhixlfeqf061rkgYiX7IUixY= X-QQ-mid: zesmtpsz5t1776138139t8864a011 X-QQ-Originating-IP: zNpQbJroXY1pqGXRu3jMICbBBcqFR/tlOHp9X6TarP8= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:16 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 14641806201959485395 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel Date: Tue, 14 Apr 2026 11:41:47 +0800 Message-Id: <20260414034153.3272485-7-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: OUhmF50wCBE8ISHpSUYUUSogzgncXjHBtRkB1ctKh+fPTbKrXoqjqhpx XfKFfO6gxuobQiOA9dBUwtkMz+68VBY0R8QTMvOjVszV8RiOzBn3MKFGhOoAjL7+Pw/C+2K C13ZrxNg1ivneNXQKyOcqFbmmnOVhKTLewp6hmERqrXPwyfPVoGYlN5cVS1CzQ6ZosPdXeZ WToS8/rZ65ZZ1NLEjoCChP87aWpoFRS77El6v1bKgvpHJY9ZcoxsAOrsmjD9OOVprHDwx/u 5SZJuvMMw7rPO+Cxkel0cnnd7k4MkgKCTEFbBWuPucHXk6JwnswCcsu8NJUxDtst9Jdol/s OeQtEnCP9uMHNmb9n8C3BLFrIn9OF3wedEYW9njKD97byqIz3ym11MTfl3npFxc1XpWuQ+G v9QvFFT/xTSlkCG+UxXkKmy/1MUMFtYBKVfK+eaVywMQnmeG5JmI1aszw1k7eLP2XcwJ/yK fUuyeP9l8yQQItzI0dTWM2jWK8wwviB+L7hVOGi33bNnSk70BMY7M8nB5IdchPRnmZIJNRO ik8y99KzAgBvu0mlY1VgRqUkptAugtSlKYsnO/T/cvs+YfhYytW2/j1UD5gAQ2DfC9OqLEH xiUgXoME4x3jSsRJJ1Fq4Ue5UJCbS1Mu4nu6kmngObnFK6Qqw2hww2Kn9fe/XsONk8kuDk7 /Ynpr1aNAliQ5UXjL6seCnoMpVFopgbXarRbIUmUL8sV/78qi4gLB2bVga9CA07NR0WBq6Z D3XFuGe2FzKeHXuk9IakrUZ8UZOqasOpnkzAzhurNXfNtk7eErOzaEEWqCy/XE6qh1HVU4H elmmhZey75phYmATa+dncBNiwlMYIqsXbRwKSEkMdkTanc9HvFwh+tls8I2h4Lx5I0WO7Ll L09Q9PoDLMjKHPKaTnYI4yQZJjBeyf600vyRE9iftr0hl0Lb8pwKI/ohNQxSX3R1a9Fnw33 4HyS1Dt1IvMTf1uxMiBI1JkVGbxsmt3Y+aSnD2dGlcikuTBen7ZkVwMIx0OJsXOXYU3ccoh UicgMfkDvl++56vmreRDC3Jbf7QAsgxoNxbCHxRt0rvZNCdaDkIp9VS57a+l8+WPxG5IGD7 Niw3MV6L6YIVYoLs3AKR48Xv8+3Lck3+koN6ZDSiIwhklF1UpAa/zc1wNmp8PiNgQ== X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Implement timestamp as a configurable sub-component for both encoder and funnel drivers. Signed-off-by: liangzhen --- drivers/hwtracing/coresight/Makefile | 2 +- .../coresight/rvtrace-encoder-core.c | 42 ++- .../coresight/rvtrace-encoder-sysfs.c | 50 ++++ drivers/hwtracing/coresight/rvtrace-encoder.h | 8 + drivers/hwtracing/coresight/rvtrace-funnel.c | 95 +++++- drivers/hwtracing/coresight/rvtrace-funnel.h | 8 + .../hwtracing/coresight/rvtrace-timestamp.c | 278 ++++++++++++++++++ .../hwtracing/coresight/rvtrace-timestamp.h | 64 ++++ 8 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/cores= ight/Makefile index 2264a313a773..3ad1b0399f86 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -58,7 +58,7 @@ obj-$(CONFIG_CORESIGHT_CTCU) +=3D coresight-ctcu.o coresight-ctcu-y :=3D coresight-ctcu-core.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) +=3D coresight-kunit-tests.o obj-$(CONFIG_RVTRACE) +=3D rvtrace.o -rvtrace-y :=3D rvtrace-core.o +rvtrace-y :=3D rvtrace-core.o rvtrace-timestamp.o obj-$(CONFIG_RVTRACE_ATBBRIDGE) +=3D rvtrace-atbbridge.o obj-$(CONFIG_RVTRACE_FUNNEL) +=3D rvtrace-funnel.o obj-$(CONFIG_RVTRACE_ENCODER) +=3D rvtrace-encoder.o diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-core.c b/drivers/h= wtracing/coresight/rvtrace-encoder-core.c index 149a3cd97602..7bd075b8ce4f 100644 --- a/drivers/hwtracing/coresight/rvtrace-encoder-core.c +++ b/drivers/hwtracing/coresight/rvtrace-encoder-core.c @@ -14,9 +14,11 @@ #include #include #include +#include #include =20 #include "rvtrace-encoder.h" +#include "rvtrace-timestamp.h" #include "coresight-etm-perf.h" =20 static int boot_enable; @@ -172,6 +174,10 @@ static void encoder_set_config(struct rvtrace_componen= t *comp) config->inst_syncmax); config->inst_syncmax =3D BMVAL(val, 20, 23); } + + /* Configure timestamp only if encoder has timestamp component */ + if (encoder_data->has_timestamp && encoder_data->ts_ctrl) + timestamp_set_config(comp, dev, &encoder_data->ts_config); } =20 static int encoder_enable_hw(struct rvtrace_component *comp) @@ -192,6 +198,16 @@ static int encoder_enable_hw(struct rvtrace_component = *comp) if (ret) goto done; =20 + /* Enable timestamp only if encoder has timestamp component */ + if (encoder_data->has_timestamp && encoder_data->ts_ctrl) { + ret =3D timestamp_enable(comp); + if (ret) { + dev_warn(&encoder_data->csdev->dev, + "Failed to enable timestamp\n"); + ret =3D 0; /* Don't fail encoder enable if timestamp fails */ + } + } + val =3D readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); val |=3D RVTRACE_ENCODER_ITRACE; writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET); @@ -283,6 +299,10 @@ static void encoder_disable_hw(struct rvtrace_componen= t *comp) { struct encoder_data *encoder_data =3D rvtrace_component_data(comp); =20 + /* Disable timestamp only if encoder has timestamp component */ + if (encoder_data->has_timestamp && encoder_data->ts_ctrl) + timestamp_disable(comp); + if (rvtrace_disable_component(comp)) dev_err(&encoder_data->csdev->dev, "timeout while waiting for Trace Encoder become disabled\n"); @@ -431,6 +451,26 @@ static int encoder_probe(struct platform_device *pdev) =20 platform_set_drvdata(pdev, comp); =20 + /* Check if encoder has timestamp component from device tree early */ + encoder_data->has_timestamp =3D fwnode_property_present(dev->fwnode, + "riscv,timestamp-present"); + if (encoder_data->has_timestamp) { + if (rvtrace_init_timestamp(comp, &encoder_data->ts_config)) { + dev_err(dev, "Timestamp initialization failed\n"); + return -EINVAL; + } + + /* TODO: Default to enabling timestamp control if present, as + * encoder_data->ts_ctrl can be configured via sysfs attribute, + * but not through perf_event at this time. Future versions may + * add support for configuring timestamps via perf_event. + */ + encoder_data->ts_ctrl =3D true; + } + + /* Set component data before registration so is_visible callbacks can acc= ess it */ + comp->id.data =3D encoder_data; + desc.name =3D devm_kasprintf(dev, GFP_KERNEL, "encoder%d", comp->cpu); if (!desc.name) return -ENOMEM; @@ -452,8 +492,6 @@ static int encoder_probe(struct platform_device *pdev) return ret; } =20 - comp->id.data =3D encoder_data; - rvtrace_cpu_encoder[comp->cpu] =3D comp; =20 encoder_set_default(comp); diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c b/drivers/= hwtracing/coresight/rvtrace-encoder-sysfs.c index c89af4cb591a..520f5a16f9a0 100644 --- a/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c +++ b/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c @@ -7,6 +7,7 @@ #include #include #include "rvtrace-encoder.h" +#include "rvtrace-timestamp.h" #include "coresight-priv.h" =20 static ssize_t cpu_show(struct device *dev, @@ -48,9 +49,38 @@ static ssize_t reset_store(struct device *dev, } static DEVICE_ATTR_WO(reset); =20 +static ssize_t ts_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + return scnprintf(buf, PAGE_SIZE, "%u\n", encoder_data->ts_ctrl); +} + +static ssize_t ts_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + spin_lock(&encoder_data->spinlock); + encoder_data->ts_ctrl =3D !!val; + spin_unlock(&encoder_data->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ts_ctrl); + static struct attribute *trace_encoder_attrs[] =3D { &dev_attr_cpu.attr, &dev_attr_reset.attr, + &dev_attr_ts_ctrl.attr, NULL, }; =20 @@ -285,6 +315,19 @@ static struct attribute *trace_encoder_features_attrs[= ] =3D { NULL, }; =20 +static umode_t timestamp_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev =3D kobj_to_dev(kobj); + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + if (encoder_data->has_timestamp) + return attr->mode; + + return 0; +} + static const struct attribute_group trace_encoder_group =3D { .attrs =3D trace_encoder_attrs, }; @@ -304,10 +347,17 @@ static const struct attribute_group trace_encoder_fea= tures_group =3D { .name =3D "features", }; =20 +static const struct attribute_group trace_encoder_timestamp_group =3D { + .attrs =3D (struct attribute **)timestamp_attrs, + .name =3D "timestamp", + .is_visible =3D timestamp_attr_is_visible, +}; + const struct attribute_group *trace_encoder_groups[] =3D { &trace_encoder_group, &trace_encoder_mgmt_group, &trace_encoder_control_group, &trace_encoder_features_group, + &trace_encoder_timestamp_group, NULL, }; diff --git a/drivers/hwtracing/coresight/rvtrace-encoder.h b/drivers/hwtrac= ing/coresight/rvtrace-encoder.h index 44f2066ad869..7a0ada11de8d 100644 --- a/drivers/hwtracing/coresight/rvtrace-encoder.h +++ b/drivers/hwtracing/coresight/rvtrace-encoder.h @@ -9,6 +9,7 @@ #include #include #include "coresight-priv.h" +#include "rvtrace-timestamp.h" =20 /* Trace Encoder Control Register */ #define RVTRACE_ENCODER_ITRACE BIT(2) @@ -126,7 +127,10 @@ struct encoder_config { * @spinlock: Only one at a time pls. * @sticky_enable: True if trace encoder base configuration has been done. * @boot_enable: True if we should start tracing at boot time. + * @has_timestamp: True if this encoder has timestamp component. + * @ts_ctrl: Controls the insertion of global timestamps in the tra= ce streams. * @config: Structure holding configuration parameters. + * @ts_config: Timestamp configuration. */ =20 struct encoder_data { @@ -134,10 +138,14 @@ struct encoder_data { spinlock_t spinlock; bool sticky_enable; bool boot_enable; + bool has_timestamp; + bool ts_ctrl; struct encoder_config config; + struct timestamp_config ts_config; }; =20 extern const struct attribute_group *trace_encoder_groups[]; +extern const struct attribute *timestamp_attrs[]; void encoder_set_default(struct rvtrace_component *comp); =20 #endif diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.c b/drivers/hwtraci= ng/coresight/rvtrace-funnel.c index 0dc7799a64ac..ab7cdb29e5fd 100644 --- a/drivers/hwtracing/coresight/rvtrace-funnel.c +++ b/drivers/hwtracing/coresight/rvtrace-funnel.c @@ -6,9 +6,11 @@ #include #include #include +#include #include =20 #include "coresight-priv.h" +#include "rvtrace-timestamp.h" #include "rvtrace-funnel.h" =20 DEFINE_CORESIGHT_DEVLIST(funnel_devs, "rvtrace_funnel"); @@ -36,6 +38,16 @@ static int funnel_enable_hw(struct rvtrace_component *co= mp, int port) ret =3D rvtrace_enable_component(comp); if (ret) goto done; + + /* Enable timestamp only if funnel has timestamp component */ + if (funnel_data->has_timestamp && funnel_data->ts_ctrl) { + ret =3D timestamp_enable(comp); + if (ret) { + dev_warn(&funnel_data->csdev->dev, + "Failed to enable timestamp\n"); + ret =3D 0; /* Don't fail funnel enable if timestamp fails */ + } + } } =20 done: @@ -79,6 +91,10 @@ static void funnel_disable_hw(struct rvtrace_component *= comp, int inport) if (--funnel_data->input_refcnt !=3D 0) return; =20 + /* Disable timestamp only if funnel has timestamp component */ + if (funnel_data->has_timestamp && funnel_data->ts_ctrl) + timestamp_disable(comp); + writel_relaxed(RVTRACE_FUNNEL_DISINPUT_MASK, comp->base + RVTRACE_FUNNEL_= DISINPUT_OFFSET); =20 if (rvtrace_disable_component(comp)) @@ -162,15 +178,72 @@ static ssize_t cpu_show(struct device *dev, } static DEVICE_ATTR_RO(cpu); =20 +static ssize_t ts_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + return scnprintf(buf, PAGE_SIZE, "%u\n", funnel_data->ts_ctrl); +} + +static ssize_t ts_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + spin_lock(&funnel_data->spinlock); + funnel_data->ts_ctrl =3D !!val; + spin_unlock(&funnel_data->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ts_ctrl); + static struct attribute *trace_funnel_attrs[] =3D { coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET), coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET), coresight_simple_reg32(disinput, RVTRACE_FUNNEL_DISINPUT_OFFSET), &dev_attr_reset.attr, &dev_attr_cpu.attr, + &dev_attr_ts_ctrl.attr, + NULL, +}; + +static umode_t timestamp_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev =3D kobj_to_dev(kobj); + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + if (funnel_data->has_timestamp) + return attr->mode; + + return 0; +} + +static struct attribute_group trace_funnel_group =3D { + .attrs =3D trace_funnel_attrs, +}; + +static struct attribute_group trace_funnel_timestamp_group =3D { + .attrs =3D (struct attribute **)timestamp_attrs, + .name =3D "timestamp", + .is_visible =3D timestamp_attr_is_visible, +}; + +const struct attribute_group *trace_funnel_groups[] =3D { + &trace_funnel_group, + &trace_funnel_timestamp_group, NULL, }; -ATTRIBUTE_GROUPS(trace_funnel); =20 static int funnel_probe(struct platform_device *pdev) { @@ -197,6 +270,26 @@ static int funnel_probe(struct platform_device *pdev) =20 platform_set_drvdata(pdev, comp); =20 + /* Check if funnel has timestamp component from device tree */ + funnel_data->has_timestamp =3D fwnode_property_present(dev->fwnode, + "riscv,timestamp-present"); + if (funnel_data->has_timestamp) { + if (rvtrace_init_timestamp(comp, &funnel_data->ts_config)) { + dev_err(dev, "Timestamp initialization failed\n"); + return -EINVAL; + } + + /* TODO: Default to enabling timestamp control if present, as + * encoder_data->ts_ctrl can be configured via sysfs attribute, + * but not through perf_event at this time. Future versions may + * add support for configuring timestamps via perf_event. + */ + funnel_data->ts_ctrl =3D true; + } + + /* Set component data before registration so is_visible callbacks can acc= ess it */ + comp->id.data =3D funnel_data; + desc.name =3D coresight_alloc_device_name(&funnel_devs, dev); desc.access =3D CSDEV_ACCESS_IOMEM(comp->base); desc.type =3D CORESIGHT_DEV_TYPE_LINK; diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.h b/drivers/hwtraci= ng/coresight/rvtrace-funnel.h index e5247e6c1bf5..34394f775faa 100644 --- a/drivers/hwtracing/coresight/rvtrace-funnel.h +++ b/drivers/hwtracing/coresight/rvtrace-funnel.h @@ -19,6 +19,9 @@ * @spinlock: Only one at a time pls. * @was_enabled: Flag showing whether the Trace Funnel was enabled. * @input_refcnt: Record the number of funnel inputs + * @has_timestamp: True if this funnel has timestamp component. + * @ts_ctrl: Controls the insertion of global timestamps in the trace= streams. + * @ts_config: Timestamp configuration. */ struct funnel_data { struct coresight_device *csdev; @@ -26,6 +29,11 @@ struct funnel_data { bool was_enabled; u32 input_refcnt; u32 disintput; + bool has_timestamp; + bool ts_ctrl; + struct timestamp_config ts_config; }; =20 +extern const struct attribute *timestamp_attrs[]; + #endif diff --git a/drivers/hwtracing/coresight/rvtrace-timestamp.c b/drivers/hwtr= acing/coresight/rvtrace-timestamp.c new file mode 100644 index 000000000000..a82714f43d7c --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-timestamp.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include "rvtrace-timestamp.h" +#include "rvtrace-encoder.h" +#include "rvtrace-funnel.h" +#include "coresight-priv.h" + +int timestamp_enable(struct rvtrace_component *comp) +{ + u32 val; + + val =3D readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + val |=3D (RVTRACE_TIMESTAMP_ENABLE | RVTRACE_TIMESTAMP_COUNT); + + writel_relaxed(val, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + + return rvtrace_poll_bit(comp, RVTRACE_TIMESTAMP_CTRL_OFFSET, + RVTRACE_TIMESTAMP_ENABLE_SHIFT, 1); +} +EXPORT_SYMBOL_GPL(timestamp_enable); + +void timestamp_disable(struct rvtrace_component *comp) +{ + u32 val; + + val =3D readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + val &=3D ~(RVTRACE_TIMESTAMP_ENABLE | RVTRACE_TIMESTAMP_COUNT); + writel_relaxed(val, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); +} +EXPORT_SYMBOL_GPL(timestamp_disable); + +struct timestamp_config *timestamp_get_config(struct rvtrace_component *co= mp) +{ + if (comp->id.type =3D=3D RVTRACE_COMPONENT_TYPE_ENCODER) { + struct encoder_data *encoder_data =3D rvtrace_component_data(comp); + + return &encoder_data->ts_config; + } else if (comp->id.type =3D=3D RVTRACE_COMPONENT_TYPE_FUNNEL) { + struct funnel_data *funnel_data =3D rvtrace_component_data(comp); + + return &funnel_data->ts_config; + } else { + return NULL; + } +} +EXPORT_SYMBOL_GPL(timestamp_get_config); + +void timestamp_set_config(struct rvtrace_component *comp, struct device *d= ev, + struct timestamp_config *config) +{ + u32 val; + + val =3D readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + + val |=3D (FIELD_PREP(RVTRACE_TIMESTAMP_RUN_IN_DEBUG, config->run_in_debug= ) | + FIELD_PREP(RVTRACE_TIMESTAMP_MODE, config->mode) | + FIELD_PREP(RVTRACE_TIMESTAMP_PRESCALE, config->prescale)); + + writel_relaxed(val, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + + /* Verify configuration was applied */ + val =3D readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + + if (BMVAL(val, 3, 3) !=3D config->run_in_debug) { + dev_warn(dev, "timestamp run_in_debug %#x not supported\n", + config->run_in_debug); + config->run_in_debug =3D BMVAL(val, 3, 3); + } + + if (BMVAL(val, 4, 6) !=3D config->mode) { + dev_warn(dev, "timestamp mode %#x not supported\n", + config->mode); + config->mode =3D BMVAL(val, 4, 6); + } + + if (BMVAL(val, 8, 9) !=3D config->prescale) { + dev_warn(dev, "timestamp prescale %#x not supported\n", + config->prescale); + config->prescale =3D BMVAL(val, 8, 9); + } +} +EXPORT_SYMBOL_GPL(timestamp_set_config); + +static int rvtrace_timestamp_reset(struct rvtrace_component *comp) +{ + int ret; + + writel_relaxed(0, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + ret =3D rvtrace_poll_bit(comp, RVTRACE_TIMESTAMP_CTRL_OFFSET, + RVTRACE_TIMESTAMP_ACTIVE_SHIFT, 0); + + if (ret) + return ret; + + writel_relaxed(RVTRACE_TIMESTAMP_ACTIVE, + comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + return rvtrace_poll_bit(comp, RVTRACE_TIMESTAMP_CTRL_OFFSET, + RVTRACE_TIMESTAMP_ACTIVE_SHIFT, 1); +} + +int rvtrace_init_timestamp(struct rvtrace_component *comp, + struct timestamp_config *config) +{ + u32 val; + int ret; + + val =3D readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + if (!FIELD_GET(RVTRACE_TIMESTAMP_ACTIVE, val)) { + ret =3D rvtrace_timestamp_reset(comp); + if (ret) + return ret; + } + + val =3D readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET); + + config->run_in_debug =3D !!(val & RVTRACE_TIMESTAMP_RUN_IN_DEBUG); + config->mode =3D BMVAL(val, 4, 6); + config->prescale =3D BMVAL(val, 8, 9); + config->width =3D BMVAL(val, 24, 29); + + return 0; +} +EXPORT_SYMBOL_GPL(rvtrace_init_timestamp); + +static ssize_t run_in_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + + if (!config) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%u\n", config->run_in_debug); +} + +static ssize_t run_in_debug_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + unsigned long val; + + if (!config) + return -EINVAL; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + config->run_in_debug =3D !!val; + + return size; +} +static DEVICE_ATTR_RW(run_in_debug); + +static const char * const modes_str[] =3D { + [TIMESTAMP_MODE_NONE] =3D "none", + [TIMESTAMP_MODE_EXTERNAL] =3D "external", + [TIMESTAMP_MODE_INTERNAL_SYSTEM] =3D "internal_system", + [TIMESTAMP_MODE_INTERNAL_CORE] =3D "internal_core", + [TIMESTAMP_MODE_SHARED] =3D "shared", +}; + +static ssize_t modes_available_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%s %s %s %s %s\n", + modes_str[TIMESTAMP_MODE_NONE], + modes_str[TIMESTAMP_MODE_EXTERNAL], + modes_str[TIMESTAMP_MODE_INTERNAL_SYSTEM], + modes_str[TIMESTAMP_MODE_INTERNAL_CORE], + modes_str[TIMESTAMP_MODE_SHARED]); +} +static DEVICE_ATTR_RO(modes_available); + +static ssize_t mode_preferred_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + + if (!config) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%s\n", modes_str[config->mode]); +} + +static ssize_t mode_preferred_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + + if (!config) + return -EINVAL; + + if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_NONE])) + config->mode =3D TIMESTAMP_MODE_NONE; + else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_EXTERNAL])) + config->mode =3D TIMESTAMP_MODE_EXTERNAL; + else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_INTERNAL_SYSTEM])) + config->mode =3D TIMESTAMP_MODE_INTERNAL_SYSTEM; + else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_INTERNAL_CORE])) + config->mode =3D TIMESTAMP_MODE_INTERNAL_CORE; + else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_SHARED])) + config->mode =3D TIMESTAMP_MODE_SHARED; + else + return -EINVAL; + return size; +} +static DEVICE_ATTR_RW(mode_preferred); + +static ssize_t prescale_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + + if (!config) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%u\n", config->prescale); +} + +static ssize_t prescale_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + + if (!config) + return -EINVAL; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + config->prescale =3D val; + + return size; +} +static DEVICE_ATTR_RW(prescale); + +static ssize_t width_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rvtrace_component *comp =3D dev_get_drvdata(dev->parent); + struct timestamp_config *config =3D timestamp_get_config(comp); + + if (!config) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%u\n", config->width); +} +static DEVICE_ATTR_RO(width); + +const struct attribute *timestamp_attrs[] =3D { + &dev_attr_run_in_debug.attr, + &dev_attr_modes_available.attr, + &dev_attr_mode_preferred.attr, + &dev_attr_prescale.attr, + &dev_attr_width.attr, + NULL, +}; +EXPORT_SYMBOL_GPL(timestamp_attrs); diff --git a/drivers/hwtracing/coresight/rvtrace-timestamp.h b/drivers/hwtr= acing/coresight/rvtrace-timestamp.h new file mode 100644 index 000000000000..13cecddbd82c --- /dev/null +++ b/drivers/hwtracing/coresight/rvtrace-timestamp.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + * Author: liangzhen + */ + +#ifndef _RVTRACE_TIMESTAMP_H +#define _RVTRACE_TIMESTAMP_H + +#include + +/* Timestamp Control Register */ +#define RVTRACE_TIMESTAMP_CTRL_OFFSET 0x040 +#define RVTRACE_TIMESTAMP_ACTIVE BIT(0) +#define RVTRACE_TIMESTAMP_COUNT BIT(1) +#define RVTRACE_TIMESTAMP_RESET BIT(2) +#define RVTRACE_TIMESTAMP_RUN_IN_DEBUG BIT(3) +#define RVTRACE_TIMESTAMP_MODE GENMASK(6, 4) +#define RVTRACE_TIMESTAMP_PRESCALE GENMASK(9, 8) +#define RVTRACE_TIMESTAMP_ENABLE BIT(15) +#define RVTRACE_TIMESTAMP_WIDTH GENMASK(29, 24) + +#define RVTRACE_TIMESTAMP_ACTIVE_SHIFT 0 +#define RVTRACE_TIMESTAMP_ENABLE_SHIFT 15 + +/* Timestamp Counter Lower Bits */ +#define RVTRACE_TIMESTAMP_COUNTER_LOW 0x048 +/* Timestamp Counter Upper Bits */ +#define RVTRACE_TIMESTAMP_COUNTER_HIGH 0x04C + +enum timestamp_mode { + TIMESTAMP_MODE_NONE =3D 0, + TIMESTAMP_MODE_EXTERNAL =3D 1, + TIMESTAMP_MODE_INTERNAL_SYSTEM =3D 2, + TIMESTAMP_MODE_INTERNAL_CORE =3D 3, + TIMESTAMP_MODE_SHARED =3D 4 +}; + +/** + * struct timestamp_config - timestamp configuration for encoder/funnel + * @run_in_debug: Continue timestamp counting in debug mode + * @mode: Timestamp generation mode (periodic, event-triggered) + * @prescale: Clock prescale factor (1, 4, 16, 64) + * @width: Timestamp counter width in bits (0-63) + */ +struct timestamp_config { + bool run_in_debug; + u8 mode; + u8 prescale; + u8 width; +}; + +struct rvtrace_component; + +/* Timestamp control functions */ +int timestamp_enable(struct rvtrace_component *comp); +void timestamp_disable(struct rvtrace_component *comp); +struct timestamp_config *timestamp_get_config(struct rvtrace_component *co= mp); +void timestamp_set_config(struct rvtrace_component *comp, struct device *d= ev, + struct timestamp_config *config); +int rvtrace_init_timestamp(struct rvtrace_component *comp, + struct timestamp_config *config); + +#endif --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbgsg1.qq.com (smtpbgsg1.qq.com [54.254.200.92]) (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 4EF7034250E for ; Tue, 14 Apr 2026 03:43:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.254.200.92 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138225; cv=none; b=ghRr04Zuxzn9U21AbL27XwqD2VvQiYSPnl8V7oF8QdDGrglY9atT0dFBzL6R3/QVnQ0bKPiFzG8EGApGsb3ZZUm9BVyDHNarK4O/yV+npJzg9LrIilV900PFPNH+WIDA8CSIkTGnXJpRJFk/+ctJPwzy2FAIZZJrgmL2WAcPwKA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138225; c=relaxed/simple; bh=UgIJpNWeTYdvIHIohhLicaa3G0TZFtw5ylW0c8fiuQg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IAj/9dhPiY9l4OmhUAJ7i29kOEtCogPcW2ITCvVL9PWeju0/WRraLBft3SJ5pYui806h+nt2KmsNzDV0M4OjBAs9OxD2KlVtR3B/Rcyoz4iJX8NZD9JTSxvv36tqIQWoQvXjTycN3t3AjrreB5gA6kAuYmAiqjJByBJ67GWPeZU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=TmeSOEoy; arc=none smtp.client-ip=54.254.200.92 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="TmeSOEoy" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138148; bh=8WlX9LAMAHxENQE0GOfbl38nX4Nv/bfYwJG7tb5ZltI=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=TmeSOEoyr/9zkUOR7hJmbOQzZqLL9ph9vkgtNOUvDLG9zTMweXLaVzX8RQaMRlzNV +ti6Or5LXHCcOg+qTYt4SoJ+2PPAMA6woEwbaGuX8XY0Rh7ttF3BBvr0AihonFAFs4 oOMPimDweaV1yEqSMcA0V+r7Vf8b/1DHuMomdfao= X-QQ-mid: zesmtpsz5t1776138142t10c812b8 X-QQ-Originating-IP: t9+OZIjuuPh1IAnGOvXUomf06KYoebma1TmgOFpT1H8= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:20 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 18336862824767481091 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 07/12] coresight: Add RISC-V PMU name support Date: Tue, 14 Apr 2026 11:41:48 +0800 Message-Id: <20260414034153.3272485-8-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: NEDcVTmWjWOdlefMV3jLBDkYLdjbGfBs6ALjUNyowk1MnkYrGE882REH 2QlptrLdsMvzljka926oa8epcKk4CKVb0bG10JGcs2mXNve7S3/fh0sa55dq1hNdew2/RWT hOIOFP5p+upqABv0VfRFpZKjaBSuFvxiz2pt5+Ku4VaJVuCyyNEjT8vOjMLa+U5xtQvPdFc nKl33zGmtD7kVfGpQ7oc8Dgw9SK8jeYkndJU3pw8iBc9J5GCe8P4+PX3J7dg6o/jq35lhNJ S/QOiOgLACDVmRPQ5awNCivqe2UbqqBRSy4F+lFSiqA4N/ts8l+PemAI11IkIRnuscfZUGa ju27NOoXfGBPrM6N9GqOv2g7HQ6nKGjP/UPVRnsqfmEJb0svMLB8qC0VGaP8/bFt0L1tjad wYZHY7E5ANYmCQwJgvD6aP9GtvwvB5sAu/r7nB5S282rSxRFXu+q6JqZh1zov2DPvuz8L8N ZN2KQe1tyhFsgrHZoK6Sc0dZOKZGoM/gKaaa/vpKXBPKuamjT1UkS78SFUhwuvV3lSyqwsg NRC0lFkucMHgZdlot4MfRVZpLxfoj1heNf50CuST+n2NJlaMYrqE/V3h3Mv4zdZPm1UZwbQ Ygv2MXhumR2kGBaQ4HVPbghE5efZn7n/50ucfh9vqyZmjxTEkA+W6LgWzGJ5TNlobx888uf n6Q026yN2walILhGUyZVe4c6AedGkT92I26BFEVGpUDMyKkkMVUtr64YDYpBSUOz65xyrbm wvaHkVH871dUHtwot9ls5EBmRCqgDiWKqBBwm63NuFciB4Pr0zNcT3puLpkmO/wjKvAJq0k pI8vzBfQpjhgoaweCJziwNrScflxGqYX9+YiO10d/yi8TomQPokVRu0avJ7tgfYKPlYdelL lQSgFmTnsS7n3LSPaWIimw4iJ09lNRAI1GESBIAy5r2gM+r62Z1BgcXuDRyMChc6vuSV1Ur PRffUdK1eAnHnNJ6y+07akATcPE5N1q5g3eb6PWBddYVuPj+YhxqCnEt9goxPf3H4yMbRuY CXCrsSpYoka3xge3fW1nuPQz3kx0lGINBk0yg9+/fP2jFNCwoOHfGrBtdDK9BCmSRjZkEj8 iBXzEDqCaOLkfBMXkS37cc1gclKQzenFY2kKzTW1Kxl X-QQ-XMRINFO: NS+P29fieYNwqS3WCnRCOn9D1NpZuCnCRA== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Define CORESIGHT_ETM_PMU_NAME based on architecture: - Set to "rvtrace" when CONFIG_RVTRACE is enabled - Default to "cs_etm" for ARM/ARM64 systems Signed-off-by: liangzhen --- include/linux/coresight-pmu.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 2e179abe472a..119565500ec5 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -9,7 +9,11 @@ =20 #include =20 +#if IS_ENABLED(CONFIG_RVTRACE) +#define CORESIGHT_ETM_PMU_NAME "rvtrace" +#else #define CORESIGHT_ETM_PMU_NAME "cs_etm" +#endif =20 /* * The legacy Trace ID system based on fixed calculation from the cpu --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbguseast3.qq.com (smtpbguseast3.qq.com [54.243.244.52]) (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 8066A30B529 for ; Tue, 14 Apr 2026 03:43:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.243.244.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138220; cv=none; b=e2Wn0LrOHkWAc5hSkHIiiJ6NLT/AOk8x09rckCEFdsdsqxlk3+g9/0CMJHnFKkRRygqeb3d9ztNB9wFne67FPUC6jwIyKFkLQG0y6W6gPrMtApt0FbgJKD5vfnXsy2eNogGT8+Q7c1jW531zbXDciOKybTEUUjKA2FVSOJgd4nc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138220; c=relaxed/simple; bh=9BPx2M5Z97RkjGLF1gm6Jox/UmTMEm1nTqDV3v7K0Kw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oD/Suf8cNs9EKCxlcB9rMC//K5CAguMXJ1WohR4yzVM7ZlVcIT9vo3P1y17lTKMF0+HxKdqfDrEo/vcbpnfKjyZcSw/OdlS/QGuudCd12TAsdZFgaBOChL2/BHVC/62wW0gvV3ypksG9Xq6+fUGtjwn3UksBeP0FQwC/MpsHeLk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=XhRQYaf8; arc=none smtp.client-ip=54.243.244.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="XhRQYaf8" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138152; bh=hJlG+Wz9JKH3QRYT/KnvDDklIqbK7yJhX6cMo2Xrc5U=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=XhRQYaf8tvaBnFtIZidKdowLtIvnlV5rImGaYyhdhyxKrwIi9CVJTcV/qbhFtoIDs ajix5uAdfW6Qy2nPO6E1GZZ9ghHPXLic21r+Y1js433KkgElLVtN7qYw7fD/zE+sh3 7tSQnwaRYUp3hbLXqWNmN3gaPBdZ3yz2lF6m2W0U= X-QQ-mid: zesmtpsz5t1776138146t253d7502 X-QQ-Originating-IP: rnR3Nd9FAESxstt5RG9UujjCB3hlzi1zwI6uqv5e/m4= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:24 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 1839721278540660278 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable Date: Tue, 14 Apr 2026 11:41:49 +0800 Message-Id: <20260414034153.3272485-9-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: MszC0HoiBtNS/7vjCpAzhEFqu0pjOA0QEkRD1h3hDcYcYgNSnbrA5LRm J5QvRd+XRaaXd/2BUaFESicwCxc1enhYHiUFeMlv9iENt5dkB8++NVW6r+GZiaQ8jUfn0Jk Ncibj8hPk22am8bmZGmd7fKTAD1uk9+NSBbI4xujfpfkhLjc3ILL5QKdd7r5MDITfMSsppO NsQt4o2yPbjTDOW9mKYZH4N0cH7MYRCtkEiAXkSK2bWPjPPQzDSL4u3Ea9np4G/wsY7jlms cYoN3KL4up1Jctlv/0FrdsNmY8s7703SnnqS7yR8kadKgxtml0RgiJh1e59lEmj8JELhb6l ZTd8TbMhyHIxAcl/woO27YqXxpYJlIN3oB/IC1fK+paBU3NthoxAM0nwdLE/uxRlg3+NViB 90R6y6v557q4E/6/ZUDADRshOc/ZnEXDPZjfIE/V0/5x5RHL8L8AUEyyaedFwPoJpV6DHVS 8+JNt59yUZHmkMYBiHzLBwh5bbqZt/d29NeA8k4o4ORJ28cC/LDPnxROKYP++AX3I/9qYJ2 6AIKSNrw5HOee6Yu2KFrHE+2Ir5XdwP8e3Ie6m35gHx5ijyEwQqG9lguXlgRjnfo9oZiTCI 4ygoy2qRdoqRDYcUEdnoP5jYCqWH+v3TPKViajAw3aNoB4IbqC0vJYXaeNOfNR/zdxnFRkN slFbA4TaV023Sm09qVeKxuRdfa6PaRlAEjx/qMXLLyHC7upJJrLXlDHGy761yTAJ9sXTwtY eLaLfTX0Ps7FPaKO329US0xaR0WVG3TYzmme2Rd3ZgYC0fxN0CMglJiLl9mD2dZOXNaWNqg eZQGtBji/mVf781hTaAK155obQ1RAMFBL4pDjuWFoArLDkGzWntvo/IS6X16pyP/ahvnors syCJYT6zYCuCn1ZoY+69FozVYZsjnM0ZPdHH7+SvRUX32myeuuzKJk9YrgXPB3DWco9jrmN 5qUmMuNn45m/sXKOiRXVrYIoBX0INCTMu8Jzz4nvlomIbNtaOZxnGjLPTAyXEcVuRcez4+N yvuNa7XSoP0c2SPLTIakRH7SFMa8zpI8b2xds0kv6mVn7XuvNrLaTq77v5fQQAtCJFkODEw Pyw+gx4rbxdyaM0uLfOtY9F/56UhstHBA2ETx0dxisNzytPFVUxDK4= X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen This commit enhances the RISC-V performance tools by allowing the rvtrace PMU to be listed and selected when auxiliary trace support is enabled. Signed-off-by: liangzhen --- tools/perf/arch/riscv/util/Build | 2 ++ tools/perf/arch/riscv/util/pmu.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tools/perf/arch/riscv/util/pmu.c diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/= Build index 2328fb9a30a3..748068a3a5c5 100644 --- a/tools/perf/arch/riscv/util/Build +++ b/tools/perf/arch/riscv/util/Build @@ -1 +1,3 @@ perf-util-y +=3D header.o + +perf-util-y +=3D pmu.o diff --git a/tools/perf/arch/riscv/util/pmu.c b/tools/perf/arch/riscv/util/= pmu.c new file mode 100644 index 000000000000..71ad1c8884d0 --- /dev/null +++ b/tools/perf/arch/riscv/util/pmu.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include + +#include "../../../util/pmu.h" + +#define RVTRACE_PMU_NAME "rvtrace" + +void perf_pmu__arch_init(struct perf_pmu *pmu) +{ + if (!strcmp(pmu->name, RVTRACE_PMU_NAME)) { + pmu->auxtrace =3D true; + pmu->selectable =3D true; + } +} + --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbguseast3.qq.com (smtpbguseast3.qq.com [54.243.244.52]) (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 82F9C3168EB for ; Tue, 14 Apr 2026 03:43:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.243.244.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138223; cv=none; b=JyZUVa9LOxFB5KWxlFbnP8nLNvwaKRf3yBVLwZRq+SNQTDjKyL15L0MexkIaQq/H0/3PLJaI4uQkhWnoF68GpEheuOVApCoA9god5tkgwOCaMtMajOwtN5gkoedrOLacu/I8qIB5g8CBfbZbl6XOUgx49Zwg4lZiS1+r2XQ7Usw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138223; c=relaxed/simple; bh=t04m/B7Xj+rZN29PfWxsKZ4sTwf7jkqQSYRgGeUimHw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XHdPLKXSKhZZhNepSbtO7+kNZnxpkBl5lmQjBeSEc2rN14jFov/qXsh7yqvJDV2ozdbfCxXoBUy79YbDfuDSR0ExfSoHRQsc7M+wZLkGXkOnFJKXmzV2WXgqJ8vfPCWxgHbFdU5gtaWKEtIDEr9pIC71OfHX6cg+sz4jyWk4ZSA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=W6TnWFRR; arc=none smtp.client-ip=54.243.244.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="W6TnWFRR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138156; bh=vwV8U5nePLhsGJNgEaagtT6TMD03SbaAsVQl0DHbh5c=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=W6TnWFRRfbLGgZvUCeAC7ej8uF93GNAEBf/b9H2a/ug8XpnN8X7zxockPLr5paZE5 E1M7Aa5/vxrI7+huMCYLqGli84nn9nk9rMsIoXqNRDAhEfWnfZo1dphm03SEFXEpui WUA3xrxaW1tubIfu7C5nThgylp5iJzDmprv83PY4= X-QQ-mid: zesmtpsz5t1776138150t749d9950 X-QQ-Originating-IP: /JIWyjmHvjx2KWmNpLuZqOjdsDCC8TgLFnXm8KMJAOE= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:27 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 16457555842091747368 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Date: Tue, 14 Apr 2026 11:41:50 +0800 Message-Id: <20260414034153.3272485-10-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: NV4uMztytJrvKrep/hsPnfHN7qU9Tlel3LayVBsVuRamPXpQzYLERGvp k4OSsqEDjYpsHQk9fbMXBqLgecfSpQq+ed3oUP5vnFXpvSkTTKimVYcdLEzJ+z03841XnaG brHT5ekgscUpaihycrOhXD+mucZTnqzNIDE8wMglV04/Fi4CZ+IWh7+yt8x0MdR9hIuK8S/ Oc24n0aNUXrrgszygZjeZZgxLGoF1UfVQo5u8kIxo5jvUdhyK4+nZu5DIUImOwLB/eStDJx cG71LEetfRWtsqnwh8KpoWdNYsywcL6DJS9mpnKcg9rFydE5V6O096bzPhdah0GJoHSDWX5 nFwD1ISGef+BB1vVhN5z419NQN5k5N5iezmnZ4qZq/MpOYpPwRSRPxIeANuISOPfKEEWOyV wCcylbNyTGp1fPqipktQV0BykxqQEmV9yWDl9GtZvZnQlhkb1IFQS4DYI2K/uBGgCiHmVXL p30RVunOP1S2pmS4FWDY9fOYhMPgrCK46RI0vq3IPJZlZCPRv+mP7LBmqxqr7fKnsym1X7m uaAo7tAPiMrbldCWaK+SXGkHEfZGyehqwYrIKGhOJPzGaqh/lyyppsfKrqs9NqOowuR7X7W 4JwRpHdf7UPDD51VPaLxH3voGwttP8V5mTcH8GtaKNXMNqdDFH5fyOC592782vTSpsKAL97 jJCrEwoKrKllhLrZgNAx+uXcr2e60uXIK+Zge6HxEysqqhv4munzVSCCc8XZwa2wNSdtGbK 9cZ7dRRnnLMxBpVHlCcKajI2NoqJl06dQLXk7cUy7uGpETTEOU5aNhUfPiJ0uQERdUSPW1F y6ZTU/NehPVk/iNwrRy4clt1p5HoP9nSkJZLbJuXib3oWaRs3huMFn7upc40dT7bJmHaSLB 4g8fLxlIMsC6cfJpOkMgc/Mgj9Mt0q8UGM76cFvSz3zsqK7dW9p9K10k9NbvfOOACp9Gdj4 32jwOHfyyA9fgshA2fI6VNmq69BBIeKKo/veqz7qmNI0u1I8VrIOkeR6yBcYuNzjh3r9KPF o3qYN709ruHgAaBffzLAcR6NtJ6mIYU8f2nvezoPRRIJ9Sq4e2lECz64OZztdeDhZVJbsGX ym1vs3DNGPQ2nLAOvKOaMeoBr2635vcYg== X-QQ-XMRINFO: OWPUhxQsoeAVwkVaQIEGSKwwgKCxK/fD5g== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Introduce the required auxiliary API functions allowing the perf core to interact with RISC-V trace perf driver. Signed-off-by: liangzhen --- tools/perf/arch/riscv/util/Build | 2 +- tools/perf/arch/riscv/util/auxtrace.c | 489 ++++++++++++++++++++++++++ tools/perf/util/auxtrace.c | 1 + tools/perf/util/auxtrace.h | 1 + tools/perf/util/rvtrace.h | 38 ++ 5 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 tools/perf/arch/riscv/util/auxtrace.c create mode 100644 tools/perf/util/rvtrace.h diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/= Build index 748068a3a5c5..10f41f97bc2e 100644 --- a/tools/perf/arch/riscv/util/Build +++ b/tools/perf/arch/riscv/util/Build @@ -1,3 +1,3 @@ perf-util-y +=3D header.o =20 -perf-util-y +=3D pmu.o +perf-util-y +=3D pmu.o auxtrace.o diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/= util/auxtrace.c new file mode 100644 index 000000000000..5feee198ef97 --- /dev/null +++ b/tools/perf/arch/riscv/util/auxtrace.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include // page_size +#include "../../../util/auxtrace.h" +#include "../../../util/cpumap.h" +#include "../../../util/debug.h" +#include "../../../util/event.h" +#include "../../../util/evlist.h" +#include "../../../util/evsel.h" +#include "../../../util/rvtrace.h" +#include "../../../util/pmu.h" +#include "../../../util/record.h" +#include "../../../util/session.h" +#include "../../../util/tsc.h" +#include "../../../util/evsel_config.h" + +#define RVTRACE_PMU_NAME "rvtrace" +#define KiB(x) ((x) * 1024) +#define MiB(x) ((x) * 1024 * 1024) + +static const char * const metadata_encoder_ro[] =3D { + [RVTRACE_ENCODER_FORMAT] =3D "control/format", + [RVTRACE_ENCODER_CONTEXT] =3D "control/context", + [RVTRACE_ENCODER_INHB_SRC] =3D "control/inhb_src", + [RVTRACE_ENCODER_SRCBITS] =3D "features/srcb", + [RVTRACE_ENCODER_SRCID] =3D "features/srcid" +}; + +struct rvtrace_recording { + struct auxtrace_record itr; + struct perf_pmu *rvtrace_pmu; + struct evlist *evlist; + bool snapshot_mode; + size_t snapshot_size; +}; + +static int rvtrace_parse_snapshot_options(struct auxtrace_record *itr, + struct record_opts *opts, + const char *str) +{ + struct rvtrace_recording *ptr =3D + container_of(itr, struct rvtrace_recording, itr); + unsigned long long snapshot_size =3D 0; + char *endptr; + + if (str) { + snapshot_size =3D strtoull(str, &endptr, 0); + if (*endptr || snapshot_size > SIZE_MAX) + return -1; + } + + opts->auxtrace_snapshot_mode =3D true; + opts->auxtrace_snapshot_size =3D snapshot_size; + ptr->snapshot_size =3D snapshot_size; + + return 0; +} + +static size_t rvtrace_info_priv_size(struct auxtrace_record *itr __maybe_u= nused, + struct evlist *evlist __maybe_unused) +{ + int encoder; + struct perf_cpu_map *event_cpus =3D evlist->core.user_requested_cpus; + struct perf_cpu_map *intersect_cpus; + + if (!perf_cpu_map__has_any_cpu(event_cpus)) { + /* cpu map is not "any" CPU , we have specific CPUs to work with */ + struct perf_cpu_map *online_cpus =3D perf_cpu_map__new_online_cpus(); + + intersect_cpus =3D perf_cpu_map__intersect(event_cpus, online_cpus); + perf_cpu_map__put(online_cpus); + } else { + /* Event can be "any" CPU so count all online CPUs. */ + intersect_cpus =3D perf_cpu_map__new_online_cpus(); + } + + encoder =3D perf_cpu_map__nr(intersect_cpus); + perf_cpu_map__put(intersect_cpus); + + return (RVTRACE_HEADER_SIZE + encoder * RVTRACE_ENCODER_PRIV_SIZE); +} + +static int rvtrace_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const= char *path, __u64 *val) +{ + char pmu_path[PATH_MAX]; + int scan; + + /* Get RO metadata from sysfs */ + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path); + + scan =3D perf_pmu__scan_file(pmu, pmu_path, "%llx", val); + if (scan !=3D 1) { + pr_err("%s: error reading: %s\n", __func__, pmu_path); + return -EINVAL; + } + + return 0; +} + +static void rvtrace_get_metadata(struct perf_cpu cpu, u32 *offset, + struct auxtrace_record *itr, + struct perf_record_auxtrace_info *info) +{ + struct rvtrace_recording *ptr =3D container_of(itr, struct rvtrace_record= ing, itr); + struct perf_pmu *rvtrace_pmu =3D ptr->rvtrace_pmu; + + info->priv[*offset + RVTRACE_ENCODER_CPU] =3D cpu.cpu; + info->priv[*offset + RVTRACE_ENCODER_NR_TRC_PARAMS] =3D RVTRACE_ENCODER_N= R_TRC_PARAMS_LENGTH; + + /* Get read-only information from sysFS */ + rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_FORM= AT], + &info->priv[*offset + RVTRACE_ENCODER_FORMAT]); + rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_CONT= EXT], + &info->priv[*offset + RVTRACE_ENCODER_CONTEXT]); + rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_INHB= _SRC], + &info->priv[*offset + RVTRACE_ENCODER_INHB_SRC]); + rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCB= ITS], + &info->priv[*offset + RVTRACE_ENCODER_SRCBITS]); + rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCI= D], + &info->priv[*offset + RVTRACE_ENCODER_SRCID]); + + /* Where the next CPU entry should start from */ + *offset +=3D RVTRACE_ENCODER_PRIV_MAX; +} + +static int rvtrace_info_fill(struct auxtrace_record *itr, struct perf_sess= ion *session, + struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size) +{ + int i; + u32 offset; + u64 nr_cpu, type; + struct perf_cpu_map *cpu_map; + struct perf_cpu_map *event_cpus =3D session->evlist->core.user_requested_= cpus; + struct perf_cpu_map *online_cpus =3D perf_cpu_map__new_online_cpus(); + struct rvtrace_recording *ptr =3D container_of(itr, struct rvtrace_record= ing, itr); + struct perf_pmu *rvtrace_pmu =3D ptr->rvtrace_pmu; + struct perf_cpu cpu; + + if (priv_size !=3D rvtrace_info_priv_size(itr, session->evlist)) + return -EINVAL; + + if (!session->evlist->core.nr_mmaps) + return -EINVAL; + + /* If the cpu_map has the "any" CPU all online CPUs are involved */ + if (perf_cpu_map__has_any_cpu(event_cpus)) { + cpu_map =3D online_cpus; + } else { + /* Make sure all specified CPUs are online */ + perf_cpu_map__for_each_cpu(cpu, i, event_cpus) { + if (!perf_cpu_map__has(online_cpus, cpu)) + return -EINVAL; + } + + cpu_map =3D event_cpus; + } + + nr_cpu =3D perf_cpu_map__nr(cpu_map); + type =3D rvtrace_pmu->type; + + /* First fill out the session header */ + auxtrace_info->type =3D PERF_AUXTRACE_RISCV_TRACE; + auxtrace_info->priv[RVTRACE_PMU_TYPE_CPUS] =3D type << 32; + auxtrace_info->priv[RVTRACE_PMU_TYPE_CPUS] |=3D nr_cpu; + + offset =3D RVTRACE_HEADER_MAX; + + perf_cpu_map__for_each_cpu(cpu, i, cpu_map) { + assert(offset < priv_size); + rvtrace_get_metadata(cpu, &offset, itr, auxtrace_info); + } + + perf_cpu_map__put(online_cpus); + + return 0; +} + +static int rvtrace_set_sink_attr(struct perf_pmu *pmu, + struct evsel *evsel) +{ + char msg[BUFSIZ], path[PATH_MAX], *sink; + struct evsel_config_term *term; + int ret =3D -EINVAL; + u32 hash; + + if (evsel->core.attr.config2 & GENMASK(31, 0)) + return 0; + + list_for_each_entry(term, &evsel->config_terms, list) { + if (term->type !=3D EVSEL__CONFIG_TERM_DRV_CFG) + continue; + + sink =3D term->val.str; + snprintf(path, PATH_MAX, "sinks/%s", sink); + + ret =3D perf_pmu__scan_file(pmu, path, "%x", &hash); + if (ret !=3D 1) { + if (errno =3D=3D ENOENT) + pr_err("Couldn't find sink \"%s\" on event %s\n" + "Missing kernel or device support?\n\n" + "Hint: An appropriate sink will be picked automatically if one = isn't specified.\n", + sink, evsel__name(evsel)); + else + pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n", + sink, evsel__name(evsel), errno, + str_error_r(errno, msg, sizeof(msg))); + return ret; + } + + evsel->core.attr.config2 |=3D hash; + return 0; + } + + /* + * No sink was provided on the command line - allow the CoreSight + * system to look for a default + */ + return 0; +} + +static int rvtrace_recording_options(struct auxtrace_record *itr, struct e= vlist *evlist, + struct record_opts *opts) +{ + struct rvtrace_recording *ptr =3D container_of(itr, struct rvtrace_record= ing, itr); + struct perf_pmu *rvtrace_pmu =3D ptr->rvtrace_pmu; + struct evsel *evsel, *rvtrace_evsel =3D NULL; + struct perf_cpu_map *cpus =3D evlist->core.user_requested_cpus; + bool privileged =3D perf_event_paranoid_check(-1); + struct evsel *tracking_evsel; + int err; + + ptr->evlist =3D evlist; + ptr->snapshot_mode =3D opts->auxtrace_snapshot_mode; + evlist__for_each_entry(evlist, evsel) { + if (evsel->core.attr.type =3D=3D rvtrace_pmu->type) { + if (rvtrace_evsel) { + pr_err("There may be only one " RVTRACE_PMU_NAME "x event\n"); + return -EINVAL; + } + evsel->core.attr.freq =3D 0; + evsel->core.attr.sample_period =3D 1; + evsel->needs_auxtrace_mmap =3D true; + rvtrace_evsel =3D evsel; + opts->full_auxtrace =3D true; + } + } + + if (!opts->full_auxtrace) + return 0; + + err =3D rvtrace_set_sink_attr(rvtrace_pmu, rvtrace_evsel); + if (err) + return err; + + /* we are in snapshot mode */ + if (opts->auxtrace_snapshot_mode) { + /* + * No size were given to '-S' or '-m,', so go with + * the default + */ + if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) { + if (privileged) { + opts->auxtrace_mmap_pages =3D MiB(4) / page_size; + } else { + opts->auxtrace_mmap_pages =3D KiB(128) / page_size; + if (opts->mmap_pages =3D=3D UINT_MAX) + opts->mmap_pages =3D KiB(256) / page_size; + } + } else if (!opts->auxtrace_mmap_pages && !privileged && + opts->mmap_pages =3D=3D UINT_MAX) { + opts->mmap_pages =3D KiB(256) / page_size; + } + + /* + * '-m,xyz' was specified but no snapshot size, so make the + * snapshot size as big as the auxtrace mmap area. + */ + if (!opts->auxtrace_snapshot_size) { + opts->auxtrace_snapshot_size =3D + opts->auxtrace_mmap_pages * (size_t)page_size; + } + + /* + * -Sxyz was specified but no auxtrace mmap area, so make the + * auxtrace mmap area big enough to fit the requested snapshot + * size. + */ + if (!opts->auxtrace_mmap_pages) { + size_t sz =3D opts->auxtrace_snapshot_size; + + sz =3D round_up(sz, page_size) / page_size; + opts->auxtrace_mmap_pages =3D roundup_pow_of_two(sz); + } + + /* Snapshot size can't be bigger than the auxtrace area */ + if (opts->auxtrace_snapshot_size > + opts->auxtrace_mmap_pages * (size_t)page_size) { + pr_err("Snapshot size %zu must not be greater than AUX area tracing mma= p size %zu\n", + opts->auxtrace_snapshot_size, + opts->auxtrace_mmap_pages * (size_t)page_size); + return -EINVAL; + } + + /* Something went wrong somewhere - this shouldn't happen */ + if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) { + pr_err("Failed to calculate default snapshot size and/or AUX area traci= ng mmap pages\n"); + return -EINVAL; + } + + pr_debug2("%s snapshot size: %zu\n", RVTRACE_PMU_NAME, + opts->auxtrace_snapshot_size); + } + + /* Buffer sizes weren't specified with '-m,xyz' so give some defaults */ + if (!opts->auxtrace_mmap_pages) { + if (privileged) { + opts->auxtrace_mmap_pages =3D MiB(4) / page_size; + } else { + opts->auxtrace_mmap_pages =3D KiB(128) / page_size; + if (opts->mmap_pages =3D=3D UINT_MAX) + opts->mmap_pages =3D KiB(256) / page_size; + } + } + + /* Validate auxtrace_mmap_pages */ + if (opts->auxtrace_mmap_pages) { + size_t sz =3D opts->auxtrace_mmap_pages * (size_t)page_size; + size_t min_sz; + + if (opts->auxtrace_snapshot_mode) + min_sz =3D KiB(4); + else + min_sz =3D KiB(8); + + if (sz < min_sz || !is_power_of_2(sz)) { + pr_err("Invalid mmap size for Intel Processor Trace: must be at least %= zuKiB and a power of 2\n", + min_sz / 1024); + return -EINVAL; + } + } + + /* + * To obtain the auxtrace buffer file descriptor, the auxtrace event + * must come first. + */ + evlist__to_front(evlist, rvtrace_evsel); + + /* + * get the CPU on the sample - need it to associate trace ID in the + * AUX_OUTPUT_HW_ID event, and the AUX event for per-cpu mmaps. + */ + evsel__set_sample_bit(rvtrace_evsel, CPU); + + /* Add dummy event to keep tracking */ + err =3D parse_event(evlist, "dummy:u"); + if (err) + return err; + + tracking_evsel =3D evlist__last(evlist); + evlist__set_tracking_event(evlist, tracking_evsel); + + tracking_evsel->core.attr.freq =3D 0; + tracking_evsel->core.attr.sample_period =3D 1; + + /* In per-cpu case, always need the time of mmap events etc */ + if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) + evsel__set_sample_bit(tracking_evsel, TIME); + + return 0; +} + +static int rvtrace_snapshot_start(struct auxtrace_record *itr) +{ + struct rvtrace_recording *ptr =3D + container_of(itr, struct rvtrace_recording, itr); + struct evsel *evsel; + + evlist__for_each_entry(ptr->evlist, evsel) { + if (evsel->core.attr.type =3D=3D ptr->rvtrace_pmu->type) + return evsel__disable(evsel); + } + return -EINVAL; +} + +static int rvtrace_snapshot_finish(struct auxtrace_record *itr) +{ + struct rvtrace_recording *ptr =3D + container_of(itr, struct rvtrace_recording, itr); + struct evsel *evsel; + + evlist__for_each_entry(ptr->evlist, evsel) { + if (evsel->core.attr.type =3D=3D ptr->rvtrace_pmu->type) + return evsel__enable(evsel); + } + return -EINVAL; +} + +static u64 rvtrace_reference(struct auxtrace_record *itr __maybe_unused) +{ + return rdtsc(); +} + +static void rvtrace_recording_free(struct auxtrace_record *itr) +{ + struct rvtrace_recording *ptr =3D + container_of(itr, struct rvtrace_recording, itr); + + free(ptr); +} + +static struct auxtrace_record *rvtrace_recording_init(int *err, struct per= f_pmu *rvtrace_pmu) +{ + struct rvtrace_recording *ptr; + + if (!rvtrace_pmu) { + *err =3D -ENODEV; + return NULL; + } + + ptr =3D zalloc(sizeof(*ptr)); + if (!ptr) { + *err =3D -ENOMEM; + return NULL; + } + + ptr->rvtrace_pmu =3D rvtrace_pmu; + ptr->itr.parse_snapshot_options =3D rvtrace_parse_snapshot_options; + ptr->itr.recording_options =3D rvtrace_recording_options; + ptr->itr.info_priv_size =3D rvtrace_info_priv_size; + ptr->itr.info_fill =3D rvtrace_info_fill; + ptr->itr.snapshot_start =3D rvtrace_snapshot_start; + ptr->itr.snapshot_finish =3D rvtrace_snapshot_finish; + ptr->itr.free =3D rvtrace_recording_free; + ptr->itr.reference =3D rvtrace_reference; + ptr->itr.read_finish =3D auxtrace_record__read_finish; + ptr->itr.alignment =3D 0; + + *err =3D 0; + return &ptr->itr; +} + +static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus, + int pmu_nr, struct evsel *evsel) +{ + int i; + + if (!pmus) + return NULL; + + for (i =3D 0; i < pmu_nr; i++) { + if (evsel->core.attr.type =3D=3D pmus[i]->type) + return pmus[i]; + } + + return NULL; +} + +struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, int *= err) +{ + struct perf_pmu *rvtrace_pmu =3D NULL; + struct perf_pmu *found_rvtrace =3D NULL; + struct evsel *evsel; + + if (!evlist) + return NULL; + + rvtrace_pmu =3D perf_pmus__find(RVTRACE_PMU_NAME); + evlist__for_each_entry(evlist, evsel) { + if (rvtrace_pmu && !found_rvtrace) + found_rvtrace =3D find_pmu_for_event(&rvtrace_pmu, 1, evsel); + } + + if (found_rvtrace) + return rvtrace_recording_init(err, rvtrace_pmu); + + *err =3D 0; + return NULL; +} diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index a224687ffbc1..944a43d48739 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1411,6 +1411,7 @@ int perf_event__process_auxtrace_info(const struct pe= rf_tool *tool __maybe_unuse case PERF_AUXTRACE_VPA_DTL: err =3D powerpc_vpadtl_process_auxtrace_info(event, session); break; + case PERF_AUXTRACE_RISCV_TRACE: case PERF_AUXTRACE_UNKNOWN: default: return -EINVAL; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 6947f3f284c0..4f4714c1b53f 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -46,6 +46,7 @@ enum auxtrace_type { PERF_AUXTRACE_S390_CPUMSF, PERF_AUXTRACE_HISI_PTT, PERF_AUXTRACE_VPA_DTL, + PERF_AUXTRACE_RISCV_TRACE, }; =20 enum itrace_period_type { diff --git a/tools/perf/util/rvtrace.h b/tools/perf/util/rvtrace.h new file mode 100644 index 000000000000..1e48ed989dd7 --- /dev/null +++ b/tools/perf/util/rvtrace.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#ifndef INCLUDE__UTIL_PERF_RVTRACE_H__ +#define INCLUDE__UTIL_PERF_RVTRACE_H__ + +#include "debug.h" +#include "auxtrace.h" +#include "util/event.h" +#include "util/session.h" +#include + +enum { + /* PMU->type (32 bit), total # of CPUs (32 bit) */ + RVTRACE_PMU_TYPE_CPUS, + RVTRACE_HEADER_MAX, +}; + +/* Trace Encoder metadata */ +enum { + RVTRACE_ENCODER_CPU, + RVTRACE_ENCODER_NR_TRC_PARAMS, + RVTRACE_ENCODER_FORMAT, + RVTRACE_ENCODER_CONTEXT, + RVTRACE_ENCODER_INHB_SRC, + RVTRACE_ENCODER_SRCBITS, + RVTRACE_ENCODER_SRCID, + RVTRACE_ENCODER_PRIV_MAX, +}; + +#define RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH (RVTRACE_ENCODER_PRIV_MAX - R= VTRACE_ENCODER_FORMAT) + +#define RVTRACE_HEADER_SIZE (RVTRACE_HEADER_MAX * sizeof(u64)) +#define RVTRACE_ENCODER_PRIV_SIZE (RVTRACE_ENCODER_PRIV_MAX * sizeof(u64)) + +#endif --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbgsg2.qq.com (smtpbgsg2.qq.com [54.254.200.128]) (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 8269D346A0A for ; Tue, 14 Apr 2026 03:44:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.254.200.128 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138261; cv=none; b=iugEdE89FKdQU89lTTr2zWjWvDH7FD2HHW7GyLbxSPNz2xTBFUaDUrUx9NmntQRJGHyotHq2o2ts9k87yLSeL6DpAOaBD2/tyiOiYm4cHq8/p5K0VlxE85yeVGkUTXj1hKEKFfUIp74K3KBMmse8H62+JK4aPOUusjf9Yc5zvwk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138261; c=relaxed/simple; bh=QM1lc0A27KOxTyZHJXjjQIcTeXI6gyrjfIl7a+meIt8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aZWoLov6AhdZg5P7em4BUJHPwCsiK9Revv1WQoEpTltpxsaJMwV1j3dgENdR7HBxWl7zfJmDwf5fpNv0JDoqiUctwLnWb8suoGKqSDFifyKz2Lfr70S6d+Asr5g7Egs0b1SVeUe9K7HQgjCHblkKAwcO8PkvBeXxToJBk8Kw3oM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=ojJ2bZIg; arc=none smtp.client-ip=54.254.200.128 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="ojJ2bZIg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138233; bh=8yoI0vDgnOZ9Gkp0CETeZI4o3CaN0GwTP6KguPu6pxk=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=ojJ2bZIg0E/hXcsoVbNUIwtfFLoueovtYWs0qkp53d8OjQueZqHnsNy53eDq+hofM lkCRs8qaAQHJYIiH8dfpKOrfU+eAlNVszdzrHvM0Y7YovzmJ7+wNYyIG9tO2i1/qUd w9cFI48szzgA6zP/u+qPTleGy916/6JfHwJKK/a8= X-QQ-mid: zesmtpsz5t1776138153t0c1919cd X-QQ-Originating-IP: 7B3g45zdNn4SZJRwH7kSE2DHV5cULmG9m+oyghyMva4= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:31 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 18256988191164502552 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder Date: Tue, 14 Apr 2026 11:41:51 +0800 Message-Id: <20260414034153.3272485-11-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: NY5P6+qKKu0Hndf/OTe/SutWL7Sl86zYhjbH7m/NrvT0YEUydmRmfXGd e2aIaxXz+xiACtOGll6AnWWaDNhwc8Sn5Iij4Wh34DEJlmxzVk0rD422NmWi7+4K/IUOxJn 0JpRwbnzXsUQinG3MLpBFSLu6kbpE8TPQskvP2l1djZ2ePNyxpjfs5lM2XgqEF3V9KWVQ6W l6GKBaDaov0GFRVaSInGmUiZGrDGcs4uyr905RZL7gUtT+Ey+0KI4lxnNMDxVHfjk1dT7AO LOGl7o0xM+1gwFzPpZzjFKy26WfxUI5sgjkziJOhokX5OQEW/PQb77Vy7yHc8Zc5q9DgPu9 OlrkXJ83aFfGi0jbZjheqpcbAfDScivHVuUmwmFEoLjxrCJt0S/8eXNc9FZQlYcCBDIU2Ss HgrwyJnk3nohOJWZzaUxzdI5kg3Vx6gGs8q5kDUpaBh/hI0/Hk2ghs+0Oa7joEnucag30GI 0kGN9cDdkNvYTyf7l0DrXrUJU1HZWmP6TscTRzwSDfYOxsPwRZgLLuN8L7Xbvv7N1TiHyyh Fqr8WNIlrblc/X4/DZkDGhY5PsuiuF8D2k1VLnM4sZfSQ6rhtrLAhtZQRwVq/zMJUcpoa34 vaiuBmXpNzSY+/2mX9AsWTXSYDpDWgO2Tx+eiPuBW392/PonkAtwJbc4x38aL59AO6ypGWK pTwIo+uKdIXDmTbzcaVnwfEt4hqW+f/MI+/45kcFcyYXMb2383Gy/krLQq/vuOh9Pq9FN0x uLTWPKyqK8k4k5LJPuzcdwaq1GbR3EGoqEYmaNGLacckMSj59Fw5MlT9MEuLccHyAn58UI3 zCp78FJu+eiCX1Ve8rGn/NrHrZ6uqCME3tw9qWQjvvGbktzH84WizWecvnS+kF8onr6GorK gWqblW5bJjI3E+RE4H/miKP6qYR49Z3f8+EnQTP8cER/ddzeF6FTY7X+Hb8+mlGQ8ajLsP6 NEENc7y6dqR7H9Xm8fNSG6bmJdcLsVujnFGEUj4ZgreH8xNweGxLVedTWhGdHHEv2EtWNvS EFioasBNwcnlDvsfeFfswTlh2PI5IVAG7rkPFaIzmb4Fgu28MLCJ+N2SYofGI6KcvPLtyr6 eYI/+cMmHH8bRUvv1BEa8FshOQ8zsq3Zg== X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Add support for RISC-V Nexus Trace decoder based on the reference implementation from the RISC-V Nexus Trace specification. This includes a CoreSight frame deformatter to remove the trace formatter overhead. Signed-off-by: liangzhen --- tools/arch/riscv/include/asm/insn.h | 645 ++++++++ tools/perf/arch/riscv/util/auxtrace.c | 1 + tools/perf/util/Build | 2 + tools/perf/util/nexus-rv-decoder/Build | 1 + .../util/nexus-rv-decoder/nexus-rv-decoder.c | 1364 +++++++++++++++++ .../util/nexus-rv-decoder/nexus-rv-decoder.h | 139 ++ .../perf/util/nexus-rv-decoder/nexus-rv-msg.h | 190 +++ 7 files changed, 2342 insertions(+) create mode 100644 tools/arch/riscv/include/asm/insn.h create mode 100644 tools/perf/util/nexus-rv-decoder/Build create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h diff --git a/tools/arch/riscv/include/asm/insn.h b/tools/arch/riscv/include= /asm/insn.h new file mode 100644 index 000000000000..6c020afe926b --- /dev/null +++ b/tools/arch/riscv/include/asm/insn.h @@ -0,0 +1,645 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 SiFive + */ + +#ifndef _ASM_RISCV_INSN_H +#define _ASM_RISCV_INSN_H + +#include + +#define RV_INSN_FUNCT3_MASK GENMASK(14, 12) +#define RV_INSN_FUNCT3_OPOFF 12 +#define RV_INSN_OPCODE_MASK GENMASK(6, 0) +#define RV_INSN_OPCODE_OPOFF 0 +#define RV_INSN_FUNCT12_OPOFF 20 + +#define RV_ENCODE_FUNCT3(f_) (RVG_FUNCT3_##f_ << RV_INSN_FUNCT3_OPOFF) +#define RV_ENCODE_FUNCT12(f_) (RVG_FUNCT12_##f_ << RV_INSN_FUNCT12_OPOFF) + +/* The bit field of immediate value in I-type instruction */ +#define RV_I_IMM_SIGN_OPOFF 31 +#define RV_I_IMM_11_0_OPOFF 20 +#define RV_I_IMM_SIGN_OFF 12 +#define RV_I_IMM_11_0_OFF 0 +#define RV_I_IMM_11_0_MASK GENMASK(11, 0) + +/* The bit field of immediate value in J-type instruction */ +#define RV_J_IMM_SIGN_OPOFF 31 +#define RV_J_IMM_10_1_OPOFF 21 +#define RV_J_IMM_11_OPOFF 20 +#define RV_J_IMM_19_12_OPOFF 12 +#define RV_J_IMM_SIGN_OFF 20 +#define RV_J_IMM_10_1_OFF 1 +#define RV_J_IMM_11_OFF 11 +#define RV_J_IMM_19_12_OFF 12 +#define RV_J_IMM_10_1_MASK GENMASK(9, 0) +#define RV_J_IMM_11_MASK GENMASK(0, 0) +#define RV_J_IMM_19_12_MASK GENMASK(7, 0) + +/* + * U-type IMMs contain the upper 20bits [31:20] of an immediate with + * the rest filled in by zeros, so no shifting required. Similarly, + * bit31 contains the signed state, so no sign extension necessary. + */ +#define RV_U_IMM_SIGN_OPOFF 31 +#define RV_U_IMM_31_12_OPOFF 0 +#define RV_U_IMM_31_12_MASK GENMASK(31, 12) + +/* The bit field of immediate value in B-type instruction */ +#define RV_B_IMM_SIGN_OPOFF 31 +#define RV_B_IMM_10_5_OPOFF 25 +#define RV_B_IMM_4_1_OPOFF 8 +#define RV_B_IMM_11_OPOFF 7 +#define RV_B_IMM_SIGN_OFF 12 +#define RV_B_IMM_10_5_OFF 5 +#define RV_B_IMM_4_1_OFF 1 +#define RV_B_IMM_11_OFF 11 +#define RV_B_IMM_10_5_MASK GENMASK(5, 0) +#define RV_B_IMM_4_1_MASK GENMASK(3, 0) +#define RV_B_IMM_11_MASK GENMASK(0, 0) + +/* The register offset in RVG instruction */ +#define RVG_RS1_OPOFF 15 +#define RVG_RS2_OPOFF 20 +#define RVG_RD_OPOFF 7 +#define RVG_RS1_MASK GENMASK(4, 0) +#define RVG_RS2_MASK GENMASK(4, 0) +#define RVG_RD_MASK GENMASK(4, 0) + +/* The bit field of immediate value in RVC J instruction */ +#define RVC_J_IMM_SIGN_OPOFF 12 +#define RVC_J_IMM_4_OPOFF 11 +#define RVC_J_IMM_9_8_OPOFF 9 +#define RVC_J_IMM_10_OPOFF 8 +#define RVC_J_IMM_6_OPOFF 7 +#define RVC_J_IMM_7_OPOFF 6 +#define RVC_J_IMM_3_1_OPOFF 3 +#define RVC_J_IMM_5_OPOFF 2 +#define RVC_J_IMM_SIGN_OFF 11 +#define RVC_J_IMM_4_OFF 4 +#define RVC_J_IMM_9_8_OFF 8 +#define RVC_J_IMM_10_OFF 10 +#define RVC_J_IMM_6_OFF 6 +#define RVC_J_IMM_7_OFF 7 +#define RVC_J_IMM_3_1_OFF 1 +#define RVC_J_IMM_5_OFF 5 +#define RVC_J_IMM_4_MASK GENMASK(0, 0) +#define RVC_J_IMM_9_8_MASK GENMASK(1, 0) +#define RVC_J_IMM_10_MASK GENMASK(0, 0) +#define RVC_J_IMM_6_MASK GENMASK(0, 0) +#define RVC_J_IMM_7_MASK GENMASK(0, 0) +#define RVC_J_IMM_3_1_MASK GENMASK(2, 0) +#define RVC_J_IMM_5_MASK GENMASK(0, 0) + +/* The bit field of immediate value in RVC B instruction */ +#define RVC_B_IMM_SIGN_OPOFF 12 +#define RVC_B_IMM_4_3_OPOFF 10 +#define RVC_B_IMM_7_6_OPOFF 5 +#define RVC_B_IMM_2_1_OPOFF 3 +#define RVC_B_IMM_5_OPOFF 2 +#define RVC_B_IMM_SIGN_OFF 8 +#define RVC_B_IMM_4_3_OFF 3 +#define RVC_B_IMM_7_6_OFF 6 +#define RVC_B_IMM_2_1_OFF 1 +#define RVC_B_IMM_5_OFF 5 +#define RVC_B_IMM_4_3_MASK GENMASK(1, 0) +#define RVC_B_IMM_7_6_MASK GENMASK(1, 0) +#define RVC_B_IMM_2_1_MASK GENMASK(1, 0) +#define RVC_B_IMM_5_MASK GENMASK(0, 0) + +#define RVC_INSN_FUNCT4_MASK GENMASK(15, 12) +#define RVC_INSN_FUNCT4_OPOFF 12 +#define RVC_INSN_FUNCT3_MASK GENMASK(15, 13) +#define RVC_INSN_FUNCT3_OPOFF 13 +#define RVC_INSN_J_RS1_MASK GENMASK(11, 7) +#define RVC_INSN_J_RS2_MASK GENMASK(6, 2) +#define RVC_INSN_OPCODE_MASK GENMASK(1, 0) +#define RVC_ENCODE_FUNCT3(f_) (RVC_FUNCT3_##f_ << RVC_INSN_FUNCT3_OPOFF) +#define RVC_ENCODE_FUNCT4(f_) (RVC_FUNCT4_##f_ << RVC_INSN_FUNCT4_OPOFF) + +/* The register offset in RVC op=3DC0 instruction */ +#define RVC_C0_RS1_OPOFF 7 +#define RVC_C0_RS2_OPOFF 2 +#define RVC_C0_RD_OPOFF 2 + +/* The register offset in RVC op=3DC1 instruction */ +#define RVC_C1_RS1_OPOFF 7 +#define RVC_C1_RS2_OPOFF 2 +#define RVC_C1_RD_OPOFF 7 + +/* The register offset in RVC op=3DC2 instruction */ +#define RVC_C2_RS1_OPOFF 7 +#define RVC_C2_RS2_OPOFF 2 +#define RVC_C2_RD_OPOFF 7 +#define RVC_C2_RS1_MASK GENMASK(4, 0) + +/* parts of opcode for RVG*/ +#define RVG_OPCODE_FENCE 0x0f +#define RVG_OPCODE_AUIPC 0x17 +#define RVG_OPCODE_BRANCH 0x63 +#define RVG_OPCODE_JALR 0x67 +#define RVG_OPCODE_JAL 0x6f +#define RVG_OPCODE_SYSTEM 0x73 +#define RVG_SYSTEM_CSR_OFF 20 +#define RVG_SYSTEM_CSR_MASK GENMASK(12, 0) + +/* parts of opcode for RVF, RVD and RVQ */ +#define RVFDQ_FL_FS_WIDTH_OFF 12 +#define RVFDQ_FL_FS_WIDTH_MASK GENMASK(2, 0) +#define RVFDQ_FL_FS_WIDTH_W 2 +#define RVFDQ_FL_FS_WIDTH_D 3 +#define RVFDQ_LS_FS_WIDTH_Q 4 +#define RVFDQ_OPCODE_FL 0x07 +#define RVFDQ_OPCODE_FS 0x27 + +/* parts of opcode for RVV */ +#define RVV_OPCODE_VECTOR 0x57 +#define RVV_VL_VS_WIDTH_8 0 +#define RVV_VL_VS_WIDTH_16 5 +#define RVV_VL_VS_WIDTH_32 6 +#define RVV_VL_VS_WIDTH_64 7 +#define RVV_OPCODE_VL RVFDQ_OPCODE_FL +#define RVV_OPCODE_VS RVFDQ_OPCODE_FS + +/* parts of opcode for RVC*/ +#define RVC_OPCODE_C0 0x0 +#define RVC_OPCODE_C1 0x1 +#define RVC_OPCODE_C2 0x2 + +/* parts of funct3 code for I, M, A extension*/ +#define RVG_FUNCT3_JALR 0x0 +#define RVG_FUNCT3_BEQ 0x0 +#define RVG_FUNCT3_BNE 0x1 +#define RVG_FUNCT3_BLT 0x4 +#define RVG_FUNCT3_BGE 0x5 +#define RVG_FUNCT3_BLTU 0x6 +#define RVG_FUNCT3_BGEU 0x7 + +/* parts of funct3 code for C extension*/ +#define RVC_FUNCT3_C_BEQZ 0x6 +#define RVC_FUNCT3_C_BNEZ 0x7 +#define RVC_FUNCT3_C_J 0x5 +#define RVC_FUNCT3_C_JAL 0x1 +#define RVC_FUNCT4_C_JR 0x8 +#define RVC_FUNCT4_C_JALR 0x9 +#define RVC_FUNCT4_C_EBREAK 0x9 + +#define RVG_FUNCT12_ECALL 0x0 +#define RVG_FUNCT12_EBREAK 0x1 +#define RVG_FUNCT12_SRET 0x102 +#define RVG_FUNCT12_MRET 0x303 + +#define RVG_MATCH_AUIPC (RVG_OPCODE_AUIPC) +#define RVG_MATCH_JALR (RV_ENCODE_FUNCT3(JALR) | RVG_OPCODE_JALR) +#define RVG_MATCH_JAL (RVG_OPCODE_JAL) +#define RVG_MATCH_FENCE (RVG_OPCODE_FENCE) +#define RVG_MATCH_BEQ (RV_ENCODE_FUNCT3(BEQ) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BNE (RV_ENCODE_FUNCT3(BNE) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BLT (RV_ENCODE_FUNCT3(BLT) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BGE (RV_ENCODE_FUNCT3(BGE) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BLTU (RV_ENCODE_FUNCT3(BLTU) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BGEU (RV_ENCODE_FUNCT3(BGEU) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_ECALL (RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM) +#define RVG_MATCH_EBREAK (RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM) +#define RVG_MATCH_SRET (RV_ENCODE_FUNCT12(SRET) | RVG_OPCODE_SYSTEM) +#define RVG_MATCH_MRET (RV_ENCODE_FUNCT12(MRET) | RVG_OPCODE_SYSTEM) +#define RVC_MATCH_C_BEQZ (RVC_ENCODE_FUNCT3(C_BEQZ) | RVC_OPCODE_C1) +#define RVC_MATCH_C_BNEZ (RVC_ENCODE_FUNCT3(C_BNEZ) | RVC_OPCODE_C1) +#define RVC_MATCH_C_J (RVC_ENCODE_FUNCT3(C_J) | RVC_OPCODE_C1) +#define RVC_MATCH_C_JAL (RVC_ENCODE_FUNCT3(C_JAL) | RVC_OPCODE_C1) +#define RVC_MATCH_C_JR (RVC_ENCODE_FUNCT4(C_JR) | RVC_OPCODE_C2) +#define RVC_MATCH_C_JALR (RVC_ENCODE_FUNCT4(C_JALR) | RVC_OPCODE_C2) +#define RVC_MATCH_C_EBREAK (RVC_ENCODE_FUNCT4(C_EBREAK) | RVC_OPCODE_C2) + +#define RVG_MASK_AUIPC (RV_INSN_OPCODE_MASK) +#define RVG_MASK_JALR (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_JAL (RV_INSN_OPCODE_MASK) +#define RVG_MASK_FENCE (RV_INSN_OPCODE_MASK) +#define RVC_MASK_C_JALR (RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC= _INSN_OPCODE_MASK) +#define RVC_MASK_C_JR (RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_I= NSN_OPCODE_MASK) +#define RVC_MASK_C_JAL (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_J (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVG_MASK_BEQ (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BNE (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BLT (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BGE (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BLTU (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BGEU (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVC_MASK_C_BEQZ (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_BNEZ (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_EBREAK 0xffff +#define RVG_MASK_EBREAK 0xffffffff +#define RVG_MASK_ECALL 0xffffffff +#define RVG_MASK_SRET 0xffffffff +#define RVG_MASK_MRET 0xffffffff + +#define __INSN_LENGTH_MASK _UL(0x3) +#define __INSN_LENGTH_GE_32 _UL(0x3) +#define __INSN_OPCODE_MASK _UL(0x7F) +#define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH) + +#define __RISCV_INSN_FUNCS(name, mask, val) \ +static __always_inline bool riscv_insn_is_##name(u32 code) \ +{ \ + BUILD_BUG_ON(~(mask) & (val)); \ + return (code & (mask)) =3D=3D (val); \ +} \ + +#if __riscv_xlen =3D=3D 32 +/* C.JAL is an RV32C-only instruction */ +__RISCV_INSN_FUNCS(c_jal, RVC_MASK_C_JAL, RVC_MATCH_C_JAL) +#else +#define riscv_insn_is_c_jal(opcode) 0 +#endif +__RISCV_INSN_FUNCS(auipc, RVG_MASK_AUIPC, RVG_MATCH_AUIPC) +__RISCV_INSN_FUNCS(jalr, RVG_MASK_JALR, RVG_MATCH_JALR) +__RISCV_INSN_FUNCS(jal, RVG_MASK_JAL, RVG_MATCH_JAL) +__RISCV_INSN_FUNCS(c_j, RVC_MASK_C_J, RVC_MATCH_C_J) +__RISCV_INSN_FUNCS(beq, RVG_MASK_BEQ, RVG_MATCH_BEQ) +__RISCV_INSN_FUNCS(bne, RVG_MASK_BNE, RVG_MATCH_BNE) +__RISCV_INSN_FUNCS(blt, RVG_MASK_BLT, RVG_MATCH_BLT) +__RISCV_INSN_FUNCS(bge, RVG_MASK_BGE, RVG_MATCH_BGE) +__RISCV_INSN_FUNCS(bltu, RVG_MASK_BLTU, RVG_MATCH_BLTU) +__RISCV_INSN_FUNCS(bgeu, RVG_MASK_BGEU, RVG_MATCH_BGEU) +__RISCV_INSN_FUNCS(c_beqz, RVC_MASK_C_BEQZ, RVC_MATCH_C_BEQZ) +__RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ) +__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK) +__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK) +__RISCV_INSN_FUNCS(ecall, RVG_MASK_EBREAK, RVG_MATCH_EBREAK) +__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET) +__RISCV_INSN_FUNCS(mret, RVG_MASK_MRET, RVG_MATCH_MRET) +__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE); + +/* special case to catch _any_ system instruction */ +static __always_inline bool riscv_insn_is_system(u32 code) +{ + return (code & RV_INSN_OPCODE_MASK) =3D=3D RVG_OPCODE_SYSTEM; +} + +/* special case to catch _any_ branch instruction */ +static __always_inline bool riscv_insn_is_branch(u32 code) +{ + return (code & RV_INSN_OPCODE_MASK) =3D=3D RVG_OPCODE_BRANCH; +} + +static __always_inline bool riscv_insn_is_c_jr(u32 code) +{ + return (code & RVC_MASK_C_JR) =3D=3D RVC_MATCH_C_JR && + (code & RVC_INSN_J_RS1_MASK) !=3D 0; +} + +static __always_inline bool riscv_insn_is_c_jalr(u32 code) +{ + return (code & RVC_MASK_C_JALR) =3D=3D RVC_MATCH_C_JALR && + (code & RVC_INSN_J_RS1_MASK) !=3D 0; +} + +#define INSN_MATCH_LB 0x3 +#define INSN_MASK_LB 0x707f +#define INSN_MATCH_LH 0x1003 +#define INSN_MASK_LH 0x707f +#define INSN_MATCH_LW 0x2003 +#define INSN_MASK_LW 0x707f +#define INSN_MATCH_LD 0x3003 +#define INSN_MASK_LD 0x707f +#define INSN_MATCH_LBU 0x4003 +#define INSN_MASK_LBU 0x707f +#define INSN_MATCH_LHU 0x5003 +#define INSN_MASK_LHU 0x707f +#define INSN_MATCH_LWU 0x6003 +#define INSN_MASK_LWU 0x707f +#define INSN_MATCH_SB 0x23 +#define INSN_MASK_SB 0x707f +#define INSN_MATCH_SH 0x1023 +#define INSN_MASK_SH 0x707f +#define INSN_MATCH_SW 0x2023 +#define INSN_MASK_SW 0x707f +#define INSN_MATCH_SD 0x3023 +#define INSN_MASK_SD 0x707f + +#define INSN_MATCH_C_LD 0x6000 +#define INSN_MASK_C_LD 0xe003 +#define INSN_MATCH_C_SD 0xe000 +#define INSN_MASK_C_SD 0xe003 +#define INSN_MATCH_C_LW 0x4000 +#define INSN_MASK_C_LW 0xe003 +#define INSN_MATCH_C_SW 0xc000 +#define INSN_MASK_C_SW 0xe003 +#define INSN_MATCH_C_LDSP 0x6002 +#define INSN_MASK_C_LDSP 0xe003 +#define INSN_MATCH_C_SDSP 0xe002 +#define INSN_MASK_C_SDSP 0xe003 +#define INSN_MATCH_C_LWSP 0x4002 +#define INSN_MASK_C_LWSP 0xe003 +#define INSN_MATCH_C_SWSP 0xc002 +#define INSN_MASK_C_SWSP 0xe003 + +#define INSN_OPCODE_MASK 0x007c +#define INSN_OPCODE_SHIFT 2 +#define INSN_OPCODE_SYSTEM 28 + +#define INSN_MASK_WFI 0xffffffff +#define INSN_MATCH_WFI 0x10500073 + +#define INSN_MASK_WRS 0xffffffff +#define INSN_MATCH_WRS 0x00d00073 + +#define INSN_MATCH_CSRRW 0x1073 +#define INSN_MASK_CSRRW 0x707f +#define INSN_MATCH_CSRRS 0x2073 +#define INSN_MASK_CSRRS 0x707f +#define INSN_MATCH_CSRRC 0x3073 +#define INSN_MASK_CSRRC 0x707f +#define INSN_MATCH_CSRRWI 0x5073 +#define INSN_MASK_CSRRWI 0x707f +#define INSN_MATCH_CSRRSI 0x6073 +#define INSN_MASK_CSRRSI 0x707f +#define INSN_MATCH_CSRRCI 0x7073 +#define INSN_MASK_CSRRCI 0x707f + +#define INSN_MATCH_FLW 0x2007 +#define INSN_MASK_FLW 0x707f +#define INSN_MATCH_FLD 0x3007 +#define INSN_MASK_FLD 0x707f +#define INSN_MATCH_FLQ 0x4007 +#define INSN_MASK_FLQ 0x707f +#define INSN_MATCH_FSW 0x2027 +#define INSN_MASK_FSW 0x707f +#define INSN_MATCH_FSD 0x3027 +#define INSN_MASK_FSD 0x707f +#define INSN_MATCH_FSQ 0x4027 +#define INSN_MASK_FSQ 0x707f + +#define INSN_MATCH_C_FLD 0x2000 +#define INSN_MASK_C_FLD 0xe003 +#define INSN_MATCH_C_FLW 0x6000 +#define INSN_MASK_C_FLW 0xe003 +#define INSN_MATCH_C_FSD 0xa000 +#define INSN_MASK_C_FSD 0xe003 +#define INSN_MATCH_C_FSW 0xe000 +#define INSN_MASK_C_FSW 0xe003 +#define INSN_MATCH_C_FLDSP 0x2002 +#define INSN_MASK_C_FLDSP 0xe003 +#define INSN_MATCH_C_FSDSP 0xa002 +#define INSN_MASK_C_FSDSP 0xe003 +#define INSN_MATCH_C_FLWSP 0x6002 +#define INSN_MASK_C_FLWSP 0xe003 +#define INSN_MATCH_C_FSWSP 0xe002 +#define INSN_MASK_C_FSWSP 0xe003 + +#define INSN_MATCH_C_LHU 0x8400 +#define INSN_MASK_C_LHU 0xfc43 +#define INSN_MATCH_C_LH 0x8440 +#define INSN_MASK_C_LH 0xfc43 +#define INSN_MATCH_C_SH 0x8c00 +#define INSN_MASK_C_SH 0xfc43 + +#define INSN_16BIT_MASK 0x3 +#define INSN_IS_16BIT(insn) (((insn) & INSN_16BIT_MASK) !=3D INSN_16BIT_MA= SK) +#define INSN_LEN(insn) (INSN_IS_16BIT(insn) ? 2 : 4) + +#define SHIFT_RIGHT(x, y) \ + ((y) < 0 ? ((x) << -(y)) : ((x) >> (y))) + +#define REG_MASK \ + ((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES)) + +#define REG_OFFSET(insn, pos) \ + (SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK) + +#define REG_PTR(insn, pos, regs) \ + ((ulong *)((ulong)(regs) + REG_OFFSET(insn, pos))) + +#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs)) +#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs)) +#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs)) +#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs)) +#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs)) +#define GET_SP(regs) (*REG_PTR(2, 0, regs)) +#define SET_RD(insn, regs, val) (*REG_PTR(insn, SH_RD, regs) =3D (val)) +#define IMM_I(insn) ((s32)(insn) >> 20) +#define IMM_S(insn) (((s32)(insn) >> 25 << 5) | \ + (s32)(((insn) >> 7) & 0x1f)) + +#define SH_RD 7 +#define SH_RS1 15 +#define SH_RS2 20 +#define SH_RS2C 2 +#define MASK_RX 0x1f + +#if defined(CONFIG_64BIT) +#define LOG_REGBYTES 3 +#else +#define LOG_REGBYTES 2 +#endif + +#define MASK_FUNCT3 0x7000 + +#define GET_FUNCT3(insn) (((insn) >> 12) & 7) + +#define RV_IMM_SIGN(x) (-(((x) >> 31) & 1)) +#define RVC_IMM_SIGN(x) (-(((x) >> 12) & 1)) +#define RV_X_MASK(X, s, mask) (((X) >> (s)) & (mask)) +#define RV_X(X, s, n) RV_X_MASK(X, s, ((1 << (n)) - 1)) +#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \ + (RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 5, 1) << 6)) +#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 5, 2) << 6)) +#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \ + (RV_X(x, 12, 1) << 5) | \ + (RV_X(x, 2, 2) << 6)) +#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \ + (RV_X(x, 12, 1) << 5) | \ + (RV_X(x, 2, 3) << 6)) +#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \ + (RV_X(x, 7, 2) << 6)) +#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 7, 3) << 6)) +#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3)) +#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3)) +#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5) +#define RVC_X(X, s, mask) RV_X_MASK(X, s, mask) + +#define RV_EXTRACT_FUNCT3(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RV_INSN_FUNCT3_OPOFF, \ + RV_INSN_FUNCT3_MASK >> RV_INSN_FUNCT3_OPOFF)); }) + +#define RV_EXTRACT_RS1_REG(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RVG_RS1_OPOFF, RVG_RS1_MASK)); }) + +#define RV_EXTRACT_RS2_REG(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RVG_RS2_OPOFF, RVG_RS2_MASK)); }) + +#define RV_EXTRACT_RD_REG(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RVG_RD_OPOFF, RVG_RD_MASK)); }) + +#define RV_EXTRACT_UTYPE_IMM(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RV_U_IMM_31_12_OPOFF, RV_U_IMM_31_12_MASK)); }) + +#define RV_EXTRACT_JTYPE_IMM(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RV_J_IMM_10_1_OPOFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_= OFF) | \ + (RV_X_MASK(x_, RV_J_IMM_11_OPOFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OFF) |= \ + (RV_X_MASK(x_, RV_J_IMM_19_12_OPOFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_= 12_OFF) | \ + (RV_IMM_SIGN(x_) << RV_J_IMM_SIGN_OFF); }) + +#define RV_EXTRACT_ITYPE_IMM(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RV_I_IMM_11_0_OPOFF, RV_I_IMM_11_0_MASK)) | \ + (RV_IMM_SIGN(x_) << RV_I_IMM_SIGN_OFF); }) + +#define RV_EXTRACT_BTYPE_IMM(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RV_B_IMM_4_1_OPOFF, RV_B_IMM_4_1_MASK) << RV_B_IMM_4_1_OFF= ) | \ + (RV_X_MASK(x_, RV_B_IMM_10_5_OPOFF, RV_B_IMM_10_5_MASK) << RV_B_IMM_10_5_= OFF) | \ + (RV_X_MASK(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) |= \ + (RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); }) + +#define RVC_EXTRACT_C2_RS1_REG(x) \ + ({typeof(x) x_ =3D (x); \ + (RV_X_MASK(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); }) + +#define RVC_EXTRACT_JTYPE_IMM(x) \ + ({typeof(x) x_ =3D (x); \ + (RVC_X(x_, RVC_J_IMM_3_1_OPOFF, RVC_J_IMM_3_1_MASK) << RVC_J_IMM_3_1_OFF)= | \ + (RVC_X(x_, RVC_J_IMM_4_OPOFF, RVC_J_IMM_4_MASK) << RVC_J_IMM_4_OFF) | \ + (RVC_X(x_, RVC_J_IMM_5_OPOFF, RVC_J_IMM_5_MASK) << RVC_J_IMM_5_OFF) | \ + (RVC_X(x_, RVC_J_IMM_6_OPOFF, RVC_J_IMM_6_MASK) << RVC_J_IMM_6_OFF) | \ + (RVC_X(x_, RVC_J_IMM_7_OPOFF, RVC_J_IMM_7_MASK) << RVC_J_IMM_7_OFF) | \ + (RVC_X(x_, RVC_J_IMM_9_8_OPOFF, RVC_J_IMM_9_8_MASK) << RVC_J_IMM_9_8_OFF)= | \ + (RVC_X(x_, RVC_J_IMM_10_OPOFF, RVC_J_IMM_10_MASK) << RVC_J_IMM_10_OFF) | \ + (RVC_IMM_SIGN(x_) << RVC_J_IMM_SIGN_OFF); }) + +#define RVC_EXTRACT_BTYPE_IMM(x) \ + ({typeof(x) x_ =3D (x); \ + (RVC_X(x_, RVC_B_IMM_2_1_OPOFF, RVC_B_IMM_2_1_MASK) << RVC_B_IMM_2_1_OFF)= | \ + (RVC_X(x_, RVC_B_IMM_4_3_OPOFF, RVC_B_IMM_4_3_MASK) << RVC_B_IMM_4_3_OFF)= | \ + (RVC_X(x_, RVC_B_IMM_5_OPOFF, RVC_B_IMM_5_MASK) << RVC_B_IMM_5_OFF) | \ + (RVC_X(x_, RVC_B_IMM_7_6_OPOFF, RVC_B_IMM_7_6_MASK) << RVC_B_IMM_7_6_OFF)= | \ + (RVC_IMM_SIGN(x_) << RVC_B_IMM_SIGN_OFF); }) + +#define RVG_EXTRACT_SYSTEM_CSR(x) \ + ({typeof(x) x_ =3D (x); RV_X_MASK(x_, RVG_SYSTEM_CSR_OFF, RVG_SYSTEM_CSR_= MASK); }) + +#define RVFDQ_EXTRACT_FL_FS_WIDTH(x) \ + ({typeof(x) x_ =3D (x); RV_X_MASK(x_, RVFDQ_FL_FS_WIDTH_OFF, \ + RVFDQ_FL_FS_WIDTH_MASK); }) + +#define RVV_EXTRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x) + +/* + * Get the immediate from a J-type instruction. + * + * @insn: instruction to process + * Return: immediate + */ +static inline s32 riscv_insn_extract_jtype_imm(u32 insn) +{ + return RV_EXTRACT_JTYPE_IMM(insn); +} + +/* + * Update a J-type instruction with an immediate value. + * + * @insn: pointer to the jtype instruction + * @imm: the immediate to insert into the instruction + */ +static inline void riscv_insn_insert_jtype_imm(u32 *insn, s32 imm) +{ + /* drop the old IMMs, all jal IMM bits sit at 31:12 */ + *insn &=3D ~GENMASK(31, 12); + *insn |=3D (RV_X_MASK(imm, RV_J_IMM_10_1_OFF, RV_J_IMM_10_1_MASK) << RV_J= _IMM_10_1_OPOFF) | + (RV_X_MASK(imm, RV_J_IMM_11_OFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OPOFF= ) | + (RV_X_MASK(imm, RV_J_IMM_19_12_OFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19= _12_OPOFF) | + (RV_X_MASK(imm, RV_J_IMM_SIGN_OFF, 1) << RV_J_IMM_SIGN_OPOFF); +} + +/* + * Put together one immediate from a U-type and I-type instruction pair. + * + * The U-type contains an upper immediate, meaning bits[31:12] with [11:0] + * being zero, while the I-type contains a 12bit immediate. + * Combined these can encode larger 32bit values and are used for example + * in auipc + jalr pairs to allow larger jumps. + * + * @utype_insn: instruction containing the upper immediate + * @itype_insn: instruction + * Return: combined immediate + */ +static inline s32 riscv_insn_extract_utype_itype_imm(u32 utype_insn, u32 i= type_insn) +{ + s32 imm; + + imm =3D RV_EXTRACT_UTYPE_IMM(utype_insn); + imm +=3D RV_EXTRACT_ITYPE_IMM(itype_insn); + + return imm; +} + +/* + * Update a set of two instructions (U-type + I-type) with an immediate va= lue. + * + * Used for example in auipc+jalrs pairs the U-type instructions contains + * a 20bit upper immediate representing bits[31:12], while the I-type + * instruction contains a 12bit immediate representing bits[11:0]. + * + * This also takes into account that both separate immediates are + * considered as signed values, so if the I-type immediate becomes + * negative (BIT(11) set) the U-type part gets adjusted. + * + * @utype_insn: pointer to the utype instruction of the pair + * @itype_insn: pointer to the itype instruction of the pair + * @imm: the immediate to insert into the two instructions + */ +static inline void riscv_insn_insert_utype_itype_imm(u32 *utype_insn, u32 = *itype_insn, s32 imm) +{ + /* drop possible old IMM values */ + *utype_insn &=3D ~(RV_U_IMM_31_12_MASK); + *itype_insn &=3D ~(RV_I_IMM_11_0_MASK << RV_I_IMM_11_0_OPOFF); + + /* add the adapted IMMs */ + *utype_insn |=3D (imm & RV_U_IMM_31_12_MASK) + ((imm & BIT(11)) << 1); + *itype_insn |=3D ((imm & RV_I_IMM_11_0_MASK) << RV_I_IMM_11_0_OPOFF); +} + +/* + * Get the immediate from a B-type instruction. + * + * @insn: instruction to process + * Return: immediate + */ +static inline s32 riscv_insn_extract_btype_imm(u32 insn) +{ + return RV_EXTRACT_BTYPE_IMM(insn); +} + +/* + * Get the immediate from a RVC B-type instruction. + * + * @insn: instruction to process + * Return: immediate + */ +static inline s32 riscv_insn_extract_cbtype_imm(u32 insn) +{ + return RVC_EXTRACT_BTYPE_IMM(insn); +} + +/* + * Get the immediate from a RVC J-type instruction. + * + * @insn: instruction to process + * Return: immediate + */ +static inline s32 riscv_insn_extract_cjtype_imm(u16 insn) +{ + return RVC_EXTRACT_JTYPE_IMM(insn); +} + +#endif /* _ASM_RISCV_INSN_H */ diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/= util/auxtrace.c index 5feee198ef97..a5450bef1a8a 100644 --- a/tools/perf/arch/riscv/util/auxtrace.c +++ b/tools/perf/arch/riscv/util/auxtrace.c @@ -10,6 +10,7 @@ #include #include #include +#include =20 #include // page_size #include "../../../util/auxtrace.h" diff --git a/tools/perf/util/Build b/tools/perf/util/Build index bcccad7487a9..648a8552df9e 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -148,6 +148,8 @@ perf-util-y +=3D cs-etm-decoder/ endif perf-util-y +=3D cs-etm-base.o =20 +perf-util-y +=3D nexus-rv-decoder/ + perf-util-y +=3D parse-branch-options.o perf-util-y +=3D dump-insn.o perf-util-y +=3D parse-regs-options.o diff --git a/tools/perf/util/nexus-rv-decoder/Build b/tools/perf/util/nexus= -rv-decoder/Build new file mode 100644 index 000000000000..61cc960b1d04 --- /dev/null +++ b/tools/perf/util/nexus-rv-decoder/Build @@ -0,0 +1 @@ +perf-util-y +=3D nexus-rv-decoder.o diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c b/tools/pe= rf/util/nexus-rv-decoder/nexus-rv-decoder.c new file mode 100644 index 000000000000..2f41d818ca32 --- /dev/null +++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c @@ -0,0 +1,1364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../debug.h" +#include "../color.h" +#include "../intlist.h" +#include "nexus-rv-decoder.h" +#include "nexus-rv-msg.h" +#include "../../../arch/riscv/include/asm/insn.h" + +#define BUFFER_SIZE 1024 + +#define RV_REGNO_ZERO 0 +#define RV_REGNO_RA 1 + +static struct nexus_rv_defmt_buf *nexus_rv_get_buf(struct nexus_rv_defmt_b= uf defmt_bufs[], + unsigned char id) +{ + if (defmt_bufs[id].buf !=3D NULL) + return &defmt_bufs[id]; + + defmt_bufs[id].buf =3D (unsigned char *)malloc(BUFFER_SIZE); + if (!defmt_bufs[id].buf) + return NULL; + + defmt_bufs[id].size =3D 0; + defmt_bufs[id].capacity =3D BUFFER_SIZE; + + return &defmt_bufs[id]; +} + +static int nexus_rv_defmt_buf_append(struct nexus_rv_defmt_buf *defmt_buf,= FILE *nexus, + unsigned char data) +{ + if (defmt_buf->size =3D=3D defmt_buf->capacity) { + defmt_buf->capacity *=3D 2; + defmt_buf->buf =3D realloc(defmt_buf->buf, defmt_buf->capacity); + if (!defmt_buf->buf) + return -ENOMEM; + } + defmt_buf->buf[defmt_buf->size++] =3D data; + + if ((data & 0x3) =3D=3D 0x3) { + size_t n =3D fwrite(defmt_buf->buf, defmt_buf->size, 1, nexus); + + if (n !=3D 1) { + pr_err("Encoder: failed to write nexus data\n"); + return -EINVAL; + } + defmt_buf->size =3D 0; + } + + return 0; +} + +static size_t skip_frame_syncs(const unsigned char *buf, size_t len, size_= t *fsync_count) +{ + const uint32_t FSYNC_PATTERN =3D 0x7FFFFFFF; /* LE host pattern for Fr= ame SYNC */ + size_t skipped_bytes =3D 0; + + while (skipped_bytes + 4 <=3D len) { + if (*((uint32_t *)(buf + skipped_bytes)) =3D=3D FSYNC_PATTERN) { + skipped_bytes +=3D 4; + (*fsync_count)++; + } else { + break; + } + } + + return skipped_bytes; +} + +/* remove coresight formatter frame */ +static int nexus_rv_pkt_defmt(struct nexus_rv_defmt_buf defmt_bufs[], FILE= *nexus, + const unsigned char *buf, size_t len) +{ + int err; + uint8_t flag_bit; + unsigned char data =3D 0; + int cur_id =3D -1, old_id =3D -1, new_id =3D -1; + + /* 16 bytes is output by coresight trace formatter */ + while (len >=3D 16) { + /* some linux drivers (e.g. for perf) will insert FSYNCS to pad or diffe= rentiate + * between blocks of aligned data, always in frame aligned complete 16 b= yte frames. + * we need to skip past these frames, resetting as we go. + */ + size_t fsync_count =3D 0; + size_t fsync_bytes =3D skip_frame_syncs(buf, len, &fsync_count); + + if (fsync_bytes > 0) { + if (fsync_count % 4 =3D=3D 0) { + cur_id =3D -1; + old_id =3D -1; + new_id =3D -1; + } else { + pr_err("Incorrect FSYNC reset pattern\n"); + return -EINVAL; + } + buf +=3D fsync_bytes; + len -=3D fsync_bytes; + continue; + } + + flag_bit =3D 0x1; + for (int i =3D 0; i < 15; i +=3D 2) { + if (buf[i] & 0x1) { + /* it's id */ + old_id =3D new_id; + new_id =3D buf[i] >> 1; /* get new_id */ + cur_id =3D (flag_bit & buf[15]) ? old_id : new_id; + } else { + /* it's data */ + data =3D buf[i] | ((flag_bit & buf[15]) ? 0x1 : 0x0); + } + + if (IS_VALID_ID(cur_id)) { + struct nexus_rv_defmt_buf *defmt_buf =3D + nexus_rv_get_buf(defmt_bufs, cur_id); + + if (!defmt_buf) + return -ENOMEM; + + /* it's data */ + if ((buf[i] & 0x1) =3D=3D 0) { + err =3D nexus_rv_defmt_buf_append(defmt_buf, nexus, data); + if (err) + return err; + } + + /* buf[i+1] is alway data when i !=3D 14 */ + if (i !=3D 14) { + err =3D nexus_rv_defmt_buf_append(defmt_buf, nexus, + buf[i + 1]); + if (err) + return err; + } + } + + if (cur_id !=3D new_id) + cur_id =3D new_id; + + flag_bit <<=3D 1; + } + buf +=3D 16; + len -=3D 16; + } + return 0; +} + +/* dump all nexus messages (from nexus file) */ +static int nexus_rv_pkt_dump(struct nexus_rv_pkt_decoder *decoder, FILE *n= exus) +{ + const char *color =3D PERF_COLOR_BLUE; + int fld_def =3D -1; + int fld_bits =3D 0; + u64 fld_val =3D 0; + + int msg_cnt =3D 0; + int msg_bytes =3D 0; + int msg_errors =3D 0; + int idle_cnt =3D 0; + + bool find_next_package =3D false; + int error_msgs =3D 0; + + unsigned int tcode =3D 0; + unsigned int correlation_hist =3D 0; + unsigned int resourcefull_hrepeat =3D 0; + + unsigned char msg_byte =3D 0; + unsigned char prev_byte =3D 0; + unsigned int mdo =3D 0; + unsigned int mseo =3D 0; + + for (;;) { + prev_byte =3D msg_byte; + if (fread(&msg_byte, 1, 1, nexus) !=3D 1) + break; /* EOF */ + + if (find_next_package) { + if ((prev_byte & 0x3) =3D=3D 0x3) { /* last byte */ + error_msgs++; + find_next_package =3D false; + + /* reinit some val */ + fld_def =3D -1; + fld_bits =3D 0; + fld_val =3D 0; + } else { + continue; + } + } + + /* This will skip long sequnece of idles (visible in true captures ...) = */ + if (msg_byte =3D=3D 0xFF && prev_byte =3D=3D 0xFF) + continue; + + mdo =3D msg_byte >> 2; + mseo =3D msg_byte & 0x3; + + if (mseo =3D=3D 0x2) { + pr_err(" ERROR: At offset %d: MSEO=3D'10' is not allowed\n", + msg_bytes + idle_cnt); + find_next_package =3D true; + continue; + } + + if (fld_def < 0) { + if (mseo =3D=3D 0x3) { + color_fprintf(stdout, color, "MSG #%d +%d - IDLE\n", + msg_cnt, msg_bytes); + msg_cnt++; + msg_bytes++; + idle_cnt++; + continue; + } + + if (mseo !=3D 0x0) { + pr_err(" ERROR: At offset %d: Message must start from MSEO=3D'00'\n", + msg_bytes + idle_cnt); + find_next_package =3D true; + continue; + } + + for (int d =3D 0; NEXUS_MSG_DEF[d].def !=3D 0; d++) { + if ((NEXUS_MSG_DEF[d].def & 0x100) =3D=3D 0) + continue; + if ((NEXUS_MSG_DEF[d].def & 0xFF) =3D=3D mdo) { + fld_def =3D d; /* Found TCODE */ + tcode =3D mdo; + break; + } + } + + if (fld_def < 0) { + pr_err(" ERROR: At offset %d: Message with TCODE=3D%d is not defined f= or N-Trace\n", + msg_bytes + idle_cnt, mdo); + find_next_package =3D true; + continue; + } + + color_fprintf(stdout, color, "MSG #%d +%d - %s TCODE[6]=3D%d", + msg_cnt, msg_bytes, NEXUS_MSG_DEF[fld_def].name, mdo); + msg_cnt++; + msg_bytes++; + + if (mdo =3D=3D NEXUS_TCODE_Error) + msg_errors++; + + fld_def++; + fld_bits =3D 0; + fld_val =3D 0; + continue; + } + + /* Accumulate 'mdo' to field value */ + fld_val |=3D (((u64)mdo) << fld_bits); + fld_bits +=3D 6; + + msg_bytes++; + + /* Process fixed size fields (there may be more than one in one MDO reco= rd) */ + while (NEXUS_MSG_DEF[fld_def].def & 0x200) { + int fld_size =3D NEXUS_MSG_DEF[fld_def].def & 0xFF; + + if (fld_size & 0x80) { + /* Size of this field is defined by parameter ... */ + fld_size =3D decoder->src_bits; + } + if (fld_bits < fld_size) + break; /* Not enough bits for this field */ + color_fprintf(stdout, color, " %s[%d]=3D0x%lx", + NEXUS_MSG_DEF[fld_def].name, fld_size, + fld_val & ((((u64)1) << fld_size) - 1)); + fld_def++; + fld_val >>=3D fld_size; + fld_bits -=3D fld_size; + } + + if (mseo =3D=3D 0x0) + continue; + + if (NEXUS_MSG_DEF[fld_def].def & 0x400) { + /* Process ResourceFull cfg HREPEAT field */ + if (tcode =3D=3D NEXUS_TCODE_ResourceFull) { + if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "RCODE") + && fld_val =3D=3D 0x2) + resourcefull_hrepeat =3D 1; + + if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "HREPEAT") + && resourcefull_hrepeat !=3D 1) { + /* no HREPEAT field */ + fld_def++; + resourcefull_hrepeat =3D 0; + } + } + + /* Process ProgTraceCorrelation cfg HIST field */ + if (tcode =3D=3D NEXUS_TCODE_ProgTraceCorrelation) { + if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "CDF") && fld_val =3D=3D 0x2) + correlation_hist =3D 1; + + if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "HIST") + && correlation_hist !=3D 1) { + /* no HIST field */ + fld_def++; + correlation_hist =3D 0; + } + } + + /* Variable size field */ + color_fprintf(stdout, color, " %s[%d]=3D0x%lx", + NEXUS_MSG_DEF[fld_def].name, fld_bits, fld_val); + + if (mseo =3D=3D 3) { + printf("\n"); + fld_def =3D -1; + } else { + fld_def++; + } + fld_bits =3D 0; + fld_val =3D 0; + continue; + } + + if (fld_bits > 0) { + pr_err(" ERROR: At offset %d: Not enough bits for non-variable field\n", + msg_bytes + idle_cnt); + find_next_package =3D true; + continue; + } + } + + color_fprintf(stdout, color, + "\nStat: %d bytes, %d idles, %d messages, %d error messages, %d in= valid messages", + msg_bytes, idle_cnt, msg_cnt, msg_errors, error_msgs); + if (msg_cnt > 0) + color_fprintf(stdout, color, "%.2lf bytes/message", ((double)msg_bytes) = / msg_cnt); + + printf("\n"); + + return 0; +} + +struct nexus_rv_pkt_decoder *nexus_rv_pkt_decoder_new(struct nexus_rv_pkt_= decoder_params *params) +{ + struct nexus_rv_pkt_decoder *decoder; + + if (!params) + return NULL; + + decoder =3D zalloc(sizeof(struct nexus_rv_pkt_decoder)); + if (!decoder) + return NULL; + + decoder->formatted =3D params->formatted; + decoder->src_bits =3D params->src_bits; + + return decoder; +} + +void nexus_rv_pkt_decoder_free(struct nexus_rv_pkt_decoder *decoder) +{ + for (int i =3D 0; i < MAX_ID; ++i) + free(decoder->defmt_bufs[i].buf); + + free(decoder); +} + +int nexus_rv_pkt_desc(struct nexus_rv_pkt_decoder *decoder, const unsigned= char *buf, size_t len) +{ + int err; + char filename[PATH_MAX]; + FILE *nexus; + char *dir =3D getenv("PERF_BUILDID_DIR"); + + /* TODO: The filename here is always trace.bin, which results in + * only the last Nexus trace data being retained. A corresponding + * Nexus filename should be generated for each AUX data. + */ + snprintf(filename, sizeof(filename), "%s/trace.bin", dir); + nexus =3D fopen(filename, "w+"); + + if (decoder->formatted) { + err =3D nexus_rv_pkt_defmt(decoder->defmt_bufs, nexus, buf, len); + if (err) { + pr_err("Encoder: failed to remove coresight trace formatter\n"); + return err; + } + } else { + size_t n =3D fwrite(buf, len, 1, nexus); + + if (n !=3D 1) { + pr_err("Encoder: failed to write nexus data\n"); + fclose(nexus); + return -EINVAL; + } + } + + fseek(nexus, 0, SEEK_SET); + err =3D nexus_rv_pkt_dump(decoder, nexus); + + fclose(nexus); + + return err; +} + +static int nexus_rv_init_packet_buffer(struct nexus_rv_packet_buffer *pack= et_buffer) +{ + if (!packet_buffer->packets) { + packet_buffer->packets =3D calloc(BUFFER_SIZE, sizeof(struct nexus_rv_pa= cket)); + if (!packet_buffer->packets) + return -ENOMEM; + } + + packet_buffer->size =3D 0; + packet_buffer->capacity =3D BUFFER_SIZE; + + return 0; +} + +static int nexus_rv_packet_buffer_append(struct nexus_rv_packet_buffer *pa= cket_buffer, + struct nexus_rv_packet packet) +{ + if (packet_buffer->size =3D=3D packet_buffer->capacity) { + packet_buffer->capacity *=3D 2; + packet_buffer->packets =3D realloc(packet_buffer->packets, + sizeof(struct nexus_rv_packet) * packet_buffer->capacity); + if (!packet_buffer->packets) + return -ENOMEM; + } + packet_buffer->packets[packet_buffer->size++] =3D packet; + + return 0; +} + +static void nexus_rv_free_packet_buffer(struct nexus_rv_packet_buffer *pac= ket_buffer) +{ + free(packet_buffer->packets); +} + +static int nexus_rv_init_stack(struct nexus_rv_stack *stack) +{ + if (!stack->data) { + stack->data =3D (u64 *)malloc(sizeof(u64) * BUFFER_SIZE); + if (!stack->data) + return -ENOMEM; + } + + stack->top =3D -1; + stack->capacity =3D BUFFER_SIZE; + + return 0; +} + +static int nexus_rv_stack_push(struct nexus_rv_stack *stack, u64 value) +{ + if (stack->top =3D=3D stack->capacity - 1) { + stack->capacity *=3D 2; + stack->data =3D (u64 *)realloc(stack->data, sizeof(u64) * stack->capacit= y); + if (!stack->data) + return -ENOMEM; + } + stack->data[++stack->top] =3D value; + + return 0; +} + +static int nexus_rv_stack_pop(struct nexus_rv_stack *stack) +{ + u64 value; + + if (stack->top =3D=3D -1) + return 1; /* Empty */ + + value =3D stack->data[stack->top--]; + return value; +} + +static void nexus_rv_free_stack(struct nexus_rv_stack *stack) +{ + free(stack->data); +} + +static struct nexus_rv_src_context *nexus_rv_get_src_context(struct nexus_= rv_insn_decoder *decoder, + int src_id) +{ + struct int_node *node; + struct nexus_rv_src_context *ctx; + + node =3D intlist__find(decoder->src_contexts, src_id); + if (node) + return (struct nexus_rv_src_context *)node->priv; + + ctx =3D zalloc(sizeof(struct nexus_rv_src_context)); + if (!ctx) + return NULL; + + ctx->src_id =3D src_id; + ctx->nexdeco_pc =3D 1; + ctx->nexdeco_lastaddr =3D 1; + ctx->prv =3D 0; + ctx->v =3D 0; + ctx->timestamp =3D 0; + ctx->context =3D -1; + ctx->resourcefull_icnt =3D 0; + + if (nexus_rv_init_stack(&ctx->stack)) { + free(ctx); + return NULL; + } + + /* Add to RB Tree */ + node =3D intlist__findnew(decoder->src_contexts, src_id); + if (!node) { + nexus_rv_free_stack(&ctx->stack); + free(ctx); + return NULL; + } + + node->priv =3D ctx; + + return ctx; +} + +static void nexus_rv_free_src_contexts(struct intlist *src_contexts) +{ + struct int_node *node; + + intlist__for_each_entry(node, src_contexts) { + struct nexus_rv_src_context *ctx =3D (struct nexus_rv_src_context *)node= ->priv; + + nexus_rv_free_stack(&ctx->stack); + free(ctx); + } + intlist__delete(src_contexts); +} + +static int handle_error_msg(struct nexus_rv_src_context *ctx, const char *= err) +{ + pr_err("\nERROR: %s\n", err); + ctx->nexdeco_pc =3D 1; /* 1 means, that last address is unknown */ + nexus_rv_init_stack(&ctx->stack); + return -EINVAL; +} + +static int nexus_rv_insn_info_get(struct nexus_rv_insn_decoder *decoder, u= 8 *info, u64 *dest) +{ + u32 insn; + struct nexus_rv_src_context *ctx =3D decoder->current_src_ctx; + u64 addr =3D ctx->nexdeco_pc; + + if (!decoder->mem_access(decoder->data, addr, ctx->prv, ctx->context, + sizeof(insn), (u8 *)&insn)) + return -EINVAL; + + *info =3D INFO_LINEAR; + if ((insn & (__INSN_LENGTH_MASK)) !=3D (__INSN_LENGTH_GE_32)) { + if (riscv_insn_is_c_beqz(insn) || riscv_insn_is_c_bnez(insn)) { + *info =3D INFO_BRANCH; + *dest =3D addr + riscv_insn_extract_cbtype_imm(insn); + } else if (riscv_insn_is_c_j(insn)) { + /* c.j offset */ + *info =3D INFO_JUMP; + *dest =3D addr + riscv_insn_extract_cjtype_imm(insn); + } else if (riscv_insn_is_c_jr(insn)) { + *info =3D INFO_JUMP | INFO_INDIRECT; + /* ret =3D> c.jr x1 */ + if (RVC_EXTRACT_C2_RS1_REG(insn) =3D=3D RV_REGNO_RA) + *info |=3D INFO_RET; + } else if (riscv_insn_is_c_jalr(insn)) { + *info =3D INFO_JUMP | INFO_CALL | INFO_INDIRECT; + } else { + *info =3D INFO_LINEAR; + } + } else { + if (riscv_insn_is_branch(insn)) { + *info =3D INFO_BRANCH; + *dest =3D addr + riscv_insn_extract_btype_imm(insn); + } else if (riscv_insn_is_jalr(insn)) { + *info =3D INFO_JUMP | INFO_INDIRECT; + if (RV_EXTRACT_RD_REG(insn) =3D=3D RV_REGNO_ZERO + && riscv_insn_extract_jtype_imm(insn) =3D=3D 0) { + /* ret =3D> jalr x0,x1,0 */ + if (RV_EXTRACT_RS1_REG(insn) =3D=3D RV_REGNO_RA) + *info |=3D INFO_RET; + else + /* jr rs1 =3D> jalr x0,rs1,0 */ + *info |=3D INFO_CALL; + } + } else if (riscv_insn_is_jal(insn)) { + *info =3D INFO_JUMP; + /* j offset =3D> jal x0,offset */ + if (RV_EXTRACT_RD_REG(insn) !=3D RV_REGNO_ZERO) + *info |=3D INFO_CALL; + *dest =3D addr + riscv_insn_extract_jtype_imm(insn); + } else if (riscv_insn_is_sret(insn) || riscv_insn_is_mret(insn)) { + *info =3D INFO_JUMP | INFO_INDIRECT | INFO_RET; + } else if (riscv_insn_is_ecall(insn)) { + *info =3D INFO_JUMP | INFO_INDIRECT | INFO_CALL; + } else { + *info =3D INFO_LINEAR; + } + *info |=3D INFO_4; + } + + pr_debug2(".nexdeco_pc=3D0x%lx .insn=3D0x%0*x\n", addr, + (*info & INFO_4) ? 8 : 4, + (*info & INFO_4) ? insn : (u16)insn); + return 0; +} + +static int nexus_rv_emit_icnt(struct nexus_rv_insn_decoder *decoder, int n= , u32 hist) +{ + u64 a =3D 0; + u32 hist_mask; + u8 info; + int done_icnt =3D 0; /* Number of done instruction count */ + struct nexus_rv_src_context *ctx =3D decoder->current_src_ctx; + + if (ctx->nexdeco_pc & 1) + return 0; /* Not synchronized ... */ + + pr_debug2(".PC=3D0x%lX, EmitICNT(n=3D%d,hist=3D0x%x)\n", ctx->nexdeco_pc,= n, hist); + + /* + * Adjust ICNT by what was handled by ResourceFull message[s] + * before this message (message with normal ICNT field). + * NOTE: ctx->resourcefull_icnt maybe positive or negative! + */ + if (n >=3D 0 && ctx->resourcefull_icnt !=3D 0) { + n +=3D ctx->resourcefull_icnt; + if (n < 0) + return handle_error_msg(ctx, "ICNT adjustment ERROR"); + + ctx->resourcefull_icnt =3D 0; /* Make adjustment 'consumed' */ + } + + hist_mask =3D 0; /* MSB is first in history, so we need sliding mask */ + if (hist !=3D 0) { + if (hist & (1 << (sizeof(u32) * 8 - 1))) { + hist_mask =3D (1 << (sizeof(u32) * 8 - 2)); + } else { + hist_mask =3D 0x1; + while (hist_mask <=3D hist) + hist_mask <<=3D 1; + hist_mask >>=3D 2; + } + } + + while (n !=3D 0) { + ctx->current_pc =3D ctx->nexdeco_pc; + if (nexus_rv_insn_info_get(decoder, &info, &a)) + return handle_error_msg(ctx, "failed to get insn info"); + + if (n > 0) { + n -=3D (info & INFO_4) ? 2 : 1; + if (n < 0) + return handle_error_msg(ctx, "ICNT too small"); + done_icnt +=3D 1; + } + + if (info & INFO_CALL) { + u64 ret =3D ctx->nexdeco_pc + ((info & INFO_4) ? 4 : 2); + + if (nexus_rv_stack_push(&ctx->stack, ret)) + return handle_error_msg(ctx, "failed to push value to stack"); + } + + if (info & INFO_INDIRECT) { /* Cannot continue over indirect... */ + if (info & INFO_RET) { + u64 ret =3D nexus_rv_stack_pop(&ctx->stack); + + ctx->nexdeco_pc =3D ret; + if (n !=3D 0) { + if (ret =3D=3D 1) + return handle_error_msg(ctx, + "Not enough entries on callstack"); + continue; + } + } + + if (n > 0) + return handle_error_msg(ctx, + "indirect address encountered in ICNT"); + + break; + } + + if (info & INFO_BRANCH) { + if (hist =3D=3D 0) { + /* This is calling as DirectBranch */ + if (n =3D=3D 0) + info |=3D INFO_JUMP; /* Force PC change below */ + } else { + if (hist_mask & hist) + info |=3D INFO_JUMP; /* Force PC change below */ + hist_mask >>=3D 1; + if (hist_mask =3D=3D 0 && n < 0) + n =3D 0; + } + } + + if (info & INFO_JUMP) + ctx->nexdeco_pc =3D a; /* Direct jump/call/branch */ + else if (info & INFO_4) + ctx->nexdeco_pc +=3D 4; + else + ctx->nexdeco_pc +=3D 2; + + } + + return done_icnt; +} + +static u64 nexus_rv_field_get(struct nexus_rv_insn_decoder *decoder, const= char *name) +{ + for (int d =3D decoder->msg_field_pos; NEXUS_MSG_DEF[d].def !=3D 1; d++) { + if (strcmp(NEXUS_MSG_DEF[d].name, name) =3D=3D 0) { + int fi =3D d - decoder->msg_field_pos; + + if (fi <=3D decoder->msg_field_cnt) + return decoder->msg_fields[fi]; + return 0; + } + } + return 0; +} + +#define NEX_FLDGET(n) nexus_rv_field_get(decoder, #n) + +static u64 nexus_rv_calculate_addr(u64 fu_addr, int full, u64 prev_addr) +{ + fu_addr <<=3D 1; /* LSB bit is never sent */ + if ((fu_addr >> 32) & 0x10000) /* Perform MSB extension */ + fu_addr |=3D 0xFFFF000000000000UL; + /* Update (NEW or XOR) */ + if (!full) { + if (prev_addr & 1) /* Not Sync */ + fu_addr =3D prev_addr; + else + fu_addr ^=3D prev_addr; + } + return fu_addr; +} + +static int nexus_rv_msg_handle(struct nexus_rv_insn_decoder *decoder) +{ + int ret =3D 0; + u64 addr; + struct nexus_rv_packet packet; + int n =3D 0; + int new_src =3D 0; + u64 start_addr =3D 0; + int TCODE =3D decoder->msg_fields[0]; + + switch (TCODE) { + case NEXUS_TCODE_Ownership: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + decoder->current_src_ctx->prv =3D NEX_FLDGET(PRV); + decoder->current_src_ctx->v =3D NEX_FLDGET(V); + if (NEX_FLDGET(FORMAT) && NEX_FLDGET(CONTEXT)) + decoder->current_src_ctx->context =3D NEX_FLDGET(CONTEXT); + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_DirectBranch: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, 0x0); + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_IndirectBranch: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, 0x0); + + addr =3D NEX_FLDGET(UADDR); + decoder->current_src_ctx->nexdeco_lastaddr =3D nexus_rv_calculate_addr(a= ddr, 0, + decoder->current_src_ctx->nexdeco_lastaddr); + pr_debug2(".nexdeco_lastaddr=3D0x%lx\n", decoder->current_src_ctx->nexde= co_lastaddr); + decoder->current_src_ctx->nexdeco_pc =3D decoder->current_src_ctx->nexde= co_lastaddr; + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_ProgTraceSync: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, 0x0); + + addr =3D NEX_FLDGET(FADDR); + decoder->current_src_ctx->nexdeco_lastaddr =3D nexus_rv_calculate_addr(a= ddr, 1, + decoder->current_src_ctx->nexdeco_lastaddr); + pr_debug2(".nexdeco_lastaddr=3D0x%lx\n", decoder->current_src_ctx->nexde= co_lastaddr); + decoder->current_src_ctx->nexdeco_pc =3D decoder->current_src_ctx->nexde= co_lastaddr; + decoder->current_src_ctx->timestamp =3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_DirectBranchSync: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, 0x0); + + addr =3D NEX_FLDGET(FADDR); + decoder->current_src_ctx->nexdeco_lastaddr =3D nexus_rv_calculate_addr(a= ddr, 1, + decoder->current_src_ctx->nexdeco_lastaddr); + pr_debug2(".nexdeco_lastaddr=3D0x%lx\n", decoder->current_src_ctx->nexde= co_lastaddr); + decoder->current_src_ctx->nexdeco_pc =3D decoder->current_src_ctx->nexde= co_lastaddr; + decoder->current_src_ctx->timestamp =3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_IndirectBranchSync: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, 0x0); + + addr =3D NEX_FLDGET(FADDR); + decoder->current_src_ctx->nexdeco_lastaddr =3D nexus_rv_calculate_addr(a= ddr, 1, + decoder->current_src_ctx->nexdeco_lastaddr); + pr_debug2(".nexdeco_lastaddr=3D0x%lx\n", decoder->current_src_ctx->nexde= co_lastaddr); + decoder->current_src_ctx->nexdeco_pc =3D decoder->current_src_ctx->nexde= co_lastaddr; + decoder->current_src_ctx->timestamp =3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_IndirectBranchHist: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, NEX_FLDGET(HIST)); + + addr =3D NEX_FLDGET(UADDR); + decoder->current_src_ctx->nexdeco_lastaddr =3D nexus_rv_calculate_addr(a= ddr, 0, + decoder->current_src_ctx->nexdeco_lastaddr); + pr_debug2(".nexdeco_lastaddr=3D0x%lx\n", decoder->current_src_ctx->nexde= co_lastaddr); + decoder->current_src_ctx->nexdeco_pc =3D decoder->current_src_ctx->nexde= co_lastaddr; + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_IndirectBranchHistSync: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, NEX_FLDGET(HIST)); + + addr =3D NEX_FLDGET(FADDR); + decoder->current_src_ctx->nexdeco_lastaddr =3D nexus_rv_calculate_addr(a= ddr, 1, + decoder->current_src_ctx->nexdeco_lastaddr); + pr_debug2(".nexdeco_lastaddr=3D0x%lx\n", decoder->current_src_ctx->nexde= co_lastaddr); + decoder->current_src_ctx->nexdeco_pc =3D decoder->current_src_ctx->nexde= co_lastaddr; + decoder->current_src_ctx->timestamp =3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_ResourceFull: + /* Determine repeat count (for RCODE=3D2) */ + int hrepeat =3D 0; + int rcode =3D NEX_FLDGET(RCODE); + + if (rcode =3D=3D 2) + hrepeat =3D NEX_FLDGET(HREPEAT); + + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + if (rcode =3D=3D 1 || rcode =3D=3D 2) { + int rdata =3D NEX_FLDGET(RDATA); + + if (rdata > 1) { + /* Special calling to emit HIST only ... */ + if (decoder->disp_hist_repeat) { + pr_debug2("RepeatHIST,0x%x,%d\n", + rdata, decoder->disp_hist_repeat); + decoder->disp_hist_repeat =3D 0; + } + do { + /* ICNT is unknown (-1), what will process only HIST bits */ + ret =3D nexus_rv_emit_icnt(decoder, -1, rdata); + if (ret < 0) + break; + + /* Consume, so next time ICNT will be adjusted */ + decoder->current_src_ctx->resourcefull_icnt -=3D ret; + hrepeat--; + } while (hrepeat > 0); + } + } else if (rcode =3D=3D 0) { + decoder->current_src_ctx->resourcefull_icnt +=3D NEX_FLDGET(RDATA); + } + break; + + case NEXUS_TCODE_ProgTraceCorrelation: + int hist =3D 0; + int cdf =3D NEX_FLDGET(CDF); + + if (cdf =3D=3D 1) + hist =3D NEX_FLDGET(HIST); + + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + + start_addr =3D decoder->current_src_ctx->nexdeco_pc; + n =3D NEX_FLDGET(ICNT); + ret =3D nexus_rv_emit_icnt(decoder, n, hist); + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_Error: + new_src =3D NEX_FLDGET(SRC); + if (new_src !=3D decoder->current_src) { + struct nexus_rv_src_context *ctx =3D nexus_rv_get_src_context(decoder, + new_src); + if (!ctx) + return -ENOMEM; + + decoder->current_src =3D new_src; + decoder->current_src_ctx =3D ctx; + } + decoder->current_src_ctx->timestamp +=3D NEX_FLDGET(TSTAMP); + break; + + case NEXUS_TCODE_RepeatBranch: /* Handled differently! */ + default: + return -EINVAL; + } + + if (ret >=3D 0) { + if (NEXUS_TCODE_Error =3D=3D TCODE) + packet.sample_type =3D RVTRACE_LOSS; + else + packet.sample_type =3D ret ? RVTRACE_RANGE : RVTRACE_EMPTY; + packet.start_addr =3D start_addr; + packet.end_addr =3D decoder->current_src_ctx->current_pc; + packet.insn_cnt =3D ret; + packet.cpu =3D new_src; + packet.prv =3D decoder->current_src_ctx->prv; + packet.v =3D decoder->current_src_ctx->v; + packet.context =3D decoder->current_src_ctx->context; + packet.timestamp =3D decoder->current_src_ctx->timestamp; + } else { + packet.sample_type =3D RVTRACE_ERROR; + } + + ret =3D nexus_rv_packet_buffer_append(&decoder->packet_buffer, packet); + + return ret; +} + +static int nexus_rv_insn_decode(struct nexus_rv_insn_decoder *decoder, FIL= E *nexus) +{ + int fld_def =3D -1; + int fld_bits =3D 0; + u64 fld_val =3D 0; + + int msg_cnt =3D 0; + int msg_bytes =3D 0; + int msg_errors =3D 0; + + bool find_next_package =3D false; + + unsigned char msg_byte =3D 0; + unsigned char prev_byte =3D 0; + + unsigned int mdo =3D 0; + unsigned int mseo =3D 0; + + decoder->msg_field_cnt =3D 0; /* No fields */ + + for (;;) { + prev_byte =3D msg_byte; + if (fread(&msg_byte, 1, 1, nexus) !=3D 1) + break; /* EOF */ + + if (find_next_package) { + if ((prev_byte & 0x3) =3D=3D 0x3) { /* last byte */ + find_next_package =3D false; + + /* reinit some val */ + fld_def =3D -1; + fld_bits =3D 0; + fld_val =3D 0; + } else { + continue; + } + } + + /* This will skip long sequnece of idles (visible in true captures ...) = */ + if (msg_byte =3D=3D 0xFF && prev_byte =3D=3D 0xFF) + continue; + + mdo =3D msg_byte >> 2; + mseo =3D msg_byte & 0x3; + + if (mseo =3D=3D 0x2) { + pr_err("ERROR: MSEO=3D'10' is not allowed\n"); + find_next_package =3D true; + continue; + } + + if (fld_def < 0) { + if (mseo =3D=3D 0x3) + continue; /* skip idle */ + + if (mseo !=3D 0x0) { + pr_err("ERROR: Message must start from MSEO=3D'00'\n"); + find_next_package =3D true; + continue; + } + + for (int d =3D 0; NEXUS_MSG_DEF[d].def !=3D 0; d++) { + if ((NEXUS_MSG_DEF[d].def & 0x100) =3D=3D 0) + continue; + if ((NEXUS_MSG_DEF[d].def & 0xFF) =3D=3D mdo) { + fld_def =3D d; /* Found TCODE */ + break; + } + } + + if (fld_def < 0) { + pr_err("ERROR: Message with TCODE=3D%d is not defined for RISC-V\n", + mdo); + find_next_package =3D true; + continue; + } + + /* + * Special handling for RepeatBranch message. + * We want to preserve previous packet, so we can + * repeat it at end of RepeatBranch handling. + */ + if (mdo =3D=3D NEXUS_TCODE_RepeatBranch) { + /* Save previous message fields */ + decoder->saved_fields[0] =3D decoder->msg_field_pos; + decoder->saved_fields[1] =3D decoder->msg_field_cnt; + decoder->saved_fields[2] =3D decoder->msg_fields[0]; + decoder->saved_fields[3] =3D decoder->msg_fields[1]; + decoder->saved_fields[4] =3D decoder->msg_fields[2]; + decoder->saved_fields[5] =3D decoder->msg_fields[3]; + decoder->saved_fields[6] =3D decoder->msg_fields[4]; + } + + /* Save to allow later decoding */ + decoder->msg_field_pos =3D fld_def; + decoder->msg_field_cnt =3D 0; + decoder->msg_fields[decoder->msg_field_cnt++] =3D mdo; + + msg_cnt++; + msg_bytes++; + + if (mdo =3D=3D NEXUS_TCODE_Error) + msg_errors++; + + fld_def++; + fld_bits =3D 0; + fld_val =3D 0; + continue; + } + + /* Accumulate 'mdo' to field value */ + fld_val |=3D (((u64)mdo) << fld_bits); + fld_bits +=3D 6; + + msg_bytes++; + + /* Process fixed size fields (there may be more than one in one MDO reco= rd) */ + while (NEXUS_MSG_DEF[fld_def].def & 0x200) { + int fld_size =3D NEXUS_MSG_DEF[fld_def].def & 0xFF; + + if (fld_size & 0x80) + fld_size =3D decoder->src_bits; + if (fld_bits < fld_size) + break; /* Not enough bits for this field */ + + /* Save field */ + decoder->msg_fields[decoder->msg_field_cnt++] =3D + fld_val & ((((u64)1) << fld_size) - 1); + + fld_def++; + fld_val >>=3D fld_size; + fld_bits -=3D fld_size; + } + + if (mseo =3D=3D 0x0) + continue; + + if (NEXUS_MSG_DEF[fld_def].def & 0x400) { + /* Variable size field */ + decoder->msg_fields[decoder->msg_field_cnt++] =3D fld_val; /* Save fiel= iid */ + + if (mseo =3D=3D 3) { + int cnt =3D 1; + + decoder->disp_hist_repeat =3D 0; + + if (decoder->msg_fields[0] =3D=3D NEXUS_TCODE_RepeatBranch) { + /* Special handling for repeat branch (which only + * has 1 field!) + */ + /* Counter set in RepeatBranch message */ + cnt =3D decoder->msg_fields[1]; + decoder->msg_field_pos =3D decoder->saved_fields[0]; + decoder->msg_field_cnt =3D decoder->saved_fields[1]; + decoder->msg_fields[0] =3D decoder->saved_fields[2]; + decoder->msg_fields[1] =3D decoder->saved_fields[3]; + decoder->msg_fields[2] =3D decoder->saved_fields[4]; + decoder->msg_fields[3] =3D decoder->saved_fields[5]; + decoder->msg_fields[4] =3D decoder->saved_fields[6]; + decoder->disp_hist_repeat =3D cnt; + } + + while (cnt > 0) { /* Handle (1 or many times ...) */ + int err =3D nexus_rv_msg_handle(decoder); + + if (err < 0) + return err; + cnt--; + } + + fld_def =3D -1; + } else { + fld_def++; + } + fld_bits =3D 0; + fld_val =3D 0; + continue; + } + + if (fld_bits > 0) { + pr_err("Decode: Not enough bits for non-variable field\n"); + find_next_package =3D true; + continue; + } + } + + return 0; +} + +struct nexus_rv_insn_decoder *nexus_rv_insn_decoder_new(struct nexus_rv_in= sn_decoder_params *params) +{ + int err; + struct nexus_rv_insn_decoder *decoder; + + if (!params) + return NULL; + + decoder =3D zalloc(sizeof(struct nexus_rv_insn_decoder)); + if (!decoder) + return NULL; + + decoder->mem_access =3D params->mem_access; + decoder->data =3D params->data; + decoder->formatted =3D params->formatted; + decoder->src_bits =3D params->src_bits; + decoder->current_src =3D -1; + + decoder->src_contexts =3D intlist__new(NULL); + if (!decoder->src_contexts) + goto err_out; + + err =3D nexus_rv_init_packet_buffer(&decoder->packet_buffer); + if (err) + goto err_out; + + return decoder; + +err_out: + nexus_rv_free_src_contexts(decoder->src_contexts); + nexus_rv_free_packet_buffer(&decoder->packet_buffer); + free(decoder); + return NULL; +} + +void nexus_rv_insn_decoder_free(struct nexus_rv_insn_decoder *decoder) +{ + for (int i =3D 0; i < MAX_ID; ++i) + free(decoder->defmt_bufs[i].buf); + + nexus_rv_free_src_contexts(decoder->src_contexts); + nexus_rv_free_packet_buffer(&decoder->packet_buffer); + free(decoder); +} + +int nexus_rv_insn_decoder_reset(struct nexus_rv_insn_decoder *decoder) +{ + int err; + struct int_node *node; + struct nexus_rv_src_context *ctx; + + if (decoder->src_contexts) { + intlist__for_each_entry(node, decoder->src_contexts) { + ctx =3D (struct nexus_rv_src_context *)node->priv; + ctx->nexdeco_pc =3D 1; + ctx->nexdeco_lastaddr =3D 1; + ctx->prv =3D 0; + ctx->v =3D 0; + ctx->timestamp =3D 0; + ctx->context =3D -1; + ctx->resourcefull_icnt =3D 0; + nexus_rv_init_stack(&ctx->stack); + } + } + decoder->current_src_ctx =3D NULL; + decoder->current_src =3D -1; + + err =3D nexus_rv_init_packet_buffer(&decoder->packet_buffer); + if (err) + return err; + + return 0; +} + +int nexus_rv_insn_decode_data(struct nexus_rv_insn_decoder *decoder, + const unsigned char *buf, size_t size) +{ + int err; + char filename[PATH_MAX]; + FILE *nexus; + char *dir =3D getenv("PERF_BUILDID_DIR"); + + /* TODO: The filename here is always trace.bin, which results in + * only the last Nexus trace data being retained. A corresponding + * Nexus filename should be generated for each AUX data. + */ + snprintf(filename, sizeof(filename), "%s/trace.bin", dir); + nexus =3D fopen(filename, "w+"); + + if (decoder->formatted) { + err =3D nexus_rv_pkt_defmt(decoder->defmt_bufs, nexus, buf, size); + if (err) { + pr_err("Encoder: failed to remove coresight trace formatter\n"); + fclose(nexus); + return err; + } + } else { + size_t n =3D fwrite(buf, size, 1, nexus); + + if (n !=3D 1) { + pr_err("Encoder: failed to write nexus data\n"); + fclose(nexus); + return -EINVAL; + } + } + + fseek(nexus, 0, SEEK_SET); + err =3D nexus_rv_insn_decode(decoder, nexus); + + fclose(nexus); + + return err; +} diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h b/tools/pe= rf/util/nexus-rv-decoder/nexus-rv-decoder.h new file mode 100644 index 000000000000..1595b59f3b3c --- /dev/null +++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + * Author: liangzhen + */ + +#ifndef INCLUDE__NEXUS_RV_DECODER_H__ +#define INCLUDE__NEXUS_RV_DECODER_H__ + +#define INFO_LINEAR 0x1 // Linear (plain instruction or not taken BRAN= CH) +#define INFO_4 0x2 // If not 4, it must be 2 on RISC-V +// 0x4 // Reserved (for exception or so ...) +#define INFO_INDIRECT 0x8 // Possible for most types above +#define INFO_BRANCH 0x10 // Always direct on RISC-V (may have LINEAR to= o) +#define INFO_JUMP 0x20 // Direct or indirect +#define INFO_CALL 0x40 // Direct or indirect (and always a jump) +#define INFO_RET 0x80 // Return (always indirect and always a jump) + +#define MAX_ID 112 // Values of 0x00 and 0x70-0x7F are reserved by the AT= B specification +#define MSGFIELDS_MAX 10 +#define INSN_SZ 16 + +/* check an ID is in the valid range */ +#define IS_VALID_ID(id) \ + ((id > 0) && (id < MAX_ID)) + +struct rvtrace_queue; + +struct nexus_rv_defmt_buf { + unsigned char *buf; + size_t size; + size_t capacity; +}; + +struct nexus_rv_pkt_decoder { + bool formatted; + struct nexus_rv_defmt_buf defmt_bufs[MAX_ID]; + u32 src_bits; +}; + +struct nexus_rv_pkt_decoder_params { + bool formatted; + u32 src_bits; +}; + +struct nexus_rv_stack { + u64 *data; + int top; + int capacity; +}; + +enum rvtrace_sample_type { + RVTRACE_EMPTY, + RVTRACE_RANGE, + RVTRACE_LOSS, + RVTRACE_ERROR, +}; + +enum riscv_privilege_mode { + RISCV_PRIV_USER_MODE, + RISCV_PRIV_SUPERVISOR_MODE, + RISCV_PRIV_MACHINE_MODE =3D 3, +}; + +typedef u32 (*rvtrace_mem_access_type)(void *, u64, enum riscv_privilege_m= ode, + int, size_t, u8 *); + +struct nexus_rv_packet { + enum rvtrace_sample_type sample_type; + u64 start_addr; + u64 end_addr; + u32 insn_cnt; + int cpu; + enum riscv_privilege_mode prv; + bool v; + int context; + u64 timestamp; +}; + +struct nexus_rv_packet_buffer { + struct nexus_rv_packet *packets; + int size; + int capacity; +}; + +struct nexus_rv_src_context { + int src_id; + u64 nexdeco_pc; + u64 nexdeco_lastaddr; + u64 current_pc; + u64 timestamp; + enum riscv_privilege_mode prv; + bool v; + int context; + int resourcefull_icnt; + struct nexus_rv_stack stack; +}; + +struct nexus_rv_insn_decoder { + rvtrace_mem_access_type mem_access; + void *data; + bool formatted; + struct nexus_rv_defmt_buf defmt_bufs[MAX_ID]; + u32 src_bits; + int msg_field_pos; + u64 msg_fields[MSGFIELDS_MAX]; + u64 saved_fields[MSGFIELDS_MAX]; + int msg_field_cnt; + int disp_hist_repeat; + int current_src; + struct intlist *src_contexts; + struct nexus_rv_src_context *current_src_ctx; + struct nexus_rv_packet_buffer packet_buffer; +}; + +struct nexus_rv_insn_decoder_params { + rvtrace_mem_access_type mem_access; + void *data; + bool formatted; + u32 src_bits; +}; + +struct nexus_rv_pkt_decoder *nexus_rv_pkt_decoder_new(struct nexus_rv_pkt_= decoder_params *params); + +void nexus_rv_pkt_decoder_free(struct nexus_rv_pkt_decoder *decoder); + +int nexus_rv_pkt_desc(struct nexus_rv_pkt_decoder *decoder, const unsigned= char *buf, size_t len); + +struct nexus_rv_insn_decoder *nexus_rv_insn_decoder_new( + struct nexus_rv_insn_decoder_params *params); + +void nexus_rv_insn_decoder_free(struct nexus_rv_insn_decoder *decoder); + +int nexus_rv_insn_decoder_reset(struct nexus_rv_insn_decoder *decoder); + +int nexus_rv_insn_decode_data(struct nexus_rv_insn_decoder *decoder, + const unsigned char *buf, size_t size); + +#endif diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h b/tools/perf/u= til/nexus-rv-decoder/nexus-rv-msg.h new file mode 100644 index 000000000000..90d95f2c5f5f --- /dev/null +++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 IAR Systems AB. + */ + +#ifndef INCLUDE__NEXUS_RV_MSG_H__ +#define INCLUDE__NEXUS_RV_MSG_H__ + +/*************************************************************************= ***/ +/* Nexus RISC-V Trace header. */ + +// Header with common Nexus Trace definitions + +#include // For uint32_t and uint64_t + +/*************************************************************************= ***/ +/* Nexus specific values (based on Nexus Standard PDF) */ + +#define NEXUS_FLDSIZE_TCODE 6 // This is standard + +// Nexus TCODE values applicable to RISC-V +#define NEXUS_TCODE_Ownership 2 +#define NEXUS_TCODE_DirectBranch 3 +#define NEXUS_TCODE_IndirectBranch 4 +#define NEXUS_TCODE_Error 8 +#define NEXUS_TCODE_ProgTraceSync 9 +#define NEXUS_TCODE_DirectBranchSync 11 +#define NEXUS_TCODE_IndirectBranchSync 12 +#define NEXUS_TCODE_ResourceFull 27 +#define NEXUS_TCODE_IndirectBranchHist 28 +#define NEXUS_TCODE_IndirectBranchHistSync 29 +#define NEXUS_TCODE_RepeatBranch 30 +#define NEXUS_TCODE_ProgTraceCorrelation 33 + +// End of standard values +/*************************************************************************= ***/ + +/*************************************************************************= ***/ +/* RISC-V Nexus Trace related values (most 'recommended' by Nexus) */ + +// Sizes of fields: +#define NEXUS_FLDSIZE_BTYPE 2 // Branch type +#define NEXUS_FLDSIZE_SYNC 4 // Synchronization code +#define NEXUS_FLDSIZE_ETYPE 4 // Error type +#define NEXUS_FLDSIZE_EVCODE 4 // Event code (correlation) +#define NEXUS_FLDSIZE_CDF 2 +#define NEXUS_FLDSIZE_RCODE 4 // Resource full code size +// from PROCESS fields: +#define NEXUS_FLDSIZE_FORMAT 2 +#define NEXUS_FLDSIZE_PRV 2 +#define NEXUS_FLDSIZE_V 1 + +#define NEXUS_PAR_SIZE_SRC 0 // SRC field size is defined by paramete= r #0 + +#define NEXUS_HIST_BITS 31 // Number of valid HIST bits + +// Address skipping (on RISC-V LSB of PC is always 0, so it is not encoded) +#define NEXUS_PARAM_AddrSkip 1 +#define NEXUS_PARAM_AddrUnit 1 + +// End of RISC-V related values +/*************************************************************************= ***/ + +// Nexus RISC-V Trace message definitions (dump & decode) + +// Macros to define Nexus Messages (NEXM_...) +// NOTE: These macros refer to 'NEXUS_TCODE_...' and 'NEXUS_FLDSIZE_...' +// It is also possible to NOT do it, but it provides less flexibility. +// +// name def (marker | value) +//#define NEXM_BEG(n, t) {#n, 0x100 | (t) } +#define NEXM_BEG(n) {#n, 0x100 | (NEXUS_TCODE_##n) } \ + // , NEXM_FLD_PAR(SRC) // SRC is always following TCODE +//#define NEXM_FLD(n, s) {#n, 0x200 | (s) } +#define NEXM_FLD(n) {#n, 0x200 | (NEXUS_FLDSIZE_##n) } +// 0x80 means size is a parameter +#define NEXM_FLD_PAR(n) {#n, 0x200 | 0x80 | (NEXUS_PAR_SIZE_##n) } +#define NEXM_VAR(n) {#n, 0x400 } +#define NEXM_ADR(n) {#n, 0xC00 } +#define NEXM_END() {NULL, 1 } + +// Definition of Nexus Messages (subset applicable to RISC-V PC trace) +static struct NEXM_MSGDEF_STRU { + const char *name; // Name of message/field + int def; // Definition of field (see NEXM_... above) +} NEXUS_MSG_DEF[] =3D { + + NEXM_BEG(Ownership), + NEXM_FLD_PAR(SRC), + //NEXM_VAR(PROCESS), + NEXM_FLD(FORMAT), + NEXM_FLD(PRV), + NEXM_FLD(V), + NEXM_VAR(CONTEXT), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(DirectBranch), + NEXM_FLD_PAR(SRC), + NEXM_VAR(ICNT), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(IndirectBranch), + NEXM_FLD_PAR(SRC), + NEXM_FLD(BTYPE), + NEXM_VAR(ICNT), + NEXM_ADR(UADDR), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(Error), + NEXM_FLD_PAR(SRC), + NEXM_FLD(ETYPE), + NEXM_VAR(PAD), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(ProgTraceSync), + NEXM_FLD_PAR(SRC), + NEXM_FLD(SYNC), + NEXM_VAR(ICNT), + NEXM_ADR(FADDR), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(DirectBranchSync), + NEXM_FLD_PAR(SRC), + NEXM_FLD(SYNC), + NEXM_VAR(ICNT), + NEXM_ADR(FADDR), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(IndirectBranchSync), + NEXM_FLD_PAR(SRC), + NEXM_FLD(SYNC), + NEXM_FLD(BTYPE), + NEXM_VAR(ICNT), + NEXM_ADR(FADDR), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(ResourceFull), + NEXM_FLD_PAR(SRC), + NEXM_FLD(RCODE), + NEXM_VAR(RDATA), + NEXM_VAR(HREPEAT), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(IndirectBranchHist), + NEXM_FLD_PAR(SRC), + NEXM_FLD(BTYPE), + NEXM_VAR(ICNT), + NEXM_ADR(UADDR), + NEXM_VAR(HIST), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(IndirectBranchHistSync), + NEXM_FLD_PAR(SRC), + NEXM_FLD(SYNC), + NEXM_FLD(BTYPE), + // NEXM_FLD(CANCEL), + NEXM_VAR(ICNT), + NEXM_ADR(FADDR), + NEXM_VAR(HIST), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(RepeatBranch), + NEXM_FLD_PAR(SRC), + NEXM_VAR(BCNT), + NEXM_VAR(TSTAMP), + NEXM_END(), + + NEXM_BEG(ProgTraceCorrelation), + NEXM_FLD_PAR(SRC), + NEXM_FLD(EVCODE), + NEXM_FLD(CDF), + NEXM_VAR(ICNT), + NEXM_VAR(HIST), // Only if CDF=3D1! + NEXM_VAR(TSTAMP), + NEXM_END(), + + { NULL, 0 } // End-marker ('def =3D=3D 0' is not otherwise used - see NEX= M_...) +}; + +#endif --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbg151.qq.com (smtpbg151.qq.com [18.169.211.239]) (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 17E8A330651 for ; Tue, 14 Apr 2026 03:44:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=18.169.211.239 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138250; cv=none; b=mGIYMzmCCbCGKf3ABY5My+BxcEg7Wzcz+eSbGGof/UoYLNivyWRLiJhPSjPGdYsjVI+xosTN0LGNSiFoyJHFdbK1yXJJ1nxbmQhoyZZU78qEViUZt3ldfdnIGcybMRviifOh+7Bhakd9BRuV5uAgTR6XVADX/lCxKNhbNk6jX6w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138250; c=relaxed/simple; bh=gDlXGxodshaEGL/77XFLWX5eIwI7UKXkRUrgQ3cWxiQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=D3TX01IuTng5+p/FYMBeLHWHpmpj85qwz+rkvJuMcpWBcvUV7T7vBw5ziw9nvhIDEBo+kwcsFKGxs/zLrtDYjkOxK1BeggWqXxr24t1nVPvwiPFXBXsViXmC2MF+1XIGGFamcfPmuVYhtCpklwEcLaBDF7MT+AtPxnLpNp26+/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=IlZmvfg1; arc=none smtp.client-ip=18.169.211.239 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="IlZmvfg1" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138235; bh=InYbmzu0LhOVxzCvOhL7w5tQA7Qr5roxBjUclg6lFhc=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=IlZmvfg1PNtE3HeFFiNQb/y+ERtGq/YeCYcjLJt1vrUiLXBxO0rMBkuFLkz6hyewz 1mxtLNecjHxHg7heROJPLps6o4hlpA9Cqzq0+mZ5egaVs2DlW9J2bpICD2FO69bE0c OUYOgsvHI9lzFSpq1EYpXDUv7bKmet9xAS0U8fwg= X-QQ-mid: zesmtpsz5t1776138157t27d795e9 X-QQ-Originating-IP: 1lHqPqE89eQuvMv5Vq8A6d+wSfjEkGnUFlxIs8jjGCw= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:35 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 11799666380509806706 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes Date: Tue, 14 Apr 2026 11:41:52 +0800 Message-Id: <20260414034153.3272485-12-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: M9oQrxULcDvj0nAmwUTVI0A7h/08cWL6UnJmsPqECSfA2IP78ULCn39Z 1fHLrif85i6/zPY1yE6QpXaUAVauYGqReFAeSWytQ766EuwfMHY8GsSzGjPNXOdfyRwm3N+ QHQp1gQM3SAMi1cVr02Ykm8cguEDZZ6JCLG2V5PvY0O4kG9PnN/pDxTRsdNxkozxyC45b5S qEl+KgcNmFzDE3jw9eu8jfDOrDHqvJFPSEjJmUkOQi40MzHZAZ8hJlSVCSETT3Ya/itjqMC fIdkDn737pjQyGnyt5spwHV1Yzc2Hcm2xsosO3OU6GCMtn8zmB147pcLNZ02jvm75sjHsIb gzF/4OJ0mOPsDJZQYr9Q963VUZ7BTdTHD6Oeg5BYM9bYEHD5xWi2sACTRawLO7CdnbbJnTs upKt+WlcQMaPzOJsdr3fTokgu2EMAaXBwSx7i9+l6hzaVNr+IFEGmljwPZyEvlgYJjZG7fe x4GXXFT/oWJccklqgWDW3YA0wZFSSIAsmQnwL7IE0P/R52w8Ze7X4zhWisBiKwUtI6ATD4w SkWEHQeiddLDqKDRkfMLg1DwtzFkUsxT/+v1C3JkAir088hkRZPoaYqzE6hRBOdJcr2eUmR 3BgzDN1ne4S/Gf6ArESsvU0QV/AaSYA9/ALtp6Z2dVF42rvb396BgKu4Rp9JZy6Rhhg1KhI 3LR/DN5vS03c1OGfx3qG0U4GMgFZbwdLdFk3PAgN9/qBea58hOOup6ACS5qw4pFL1pu0dKM t61KKRfuHTWe/Ef6Yp/hdeHck5bIafNkY1Y/Q1ur/WZrxyKpGf1f4HHRQnBT4jLU0z9XoKv lH6nVtKErOUCDi61vPtbKDbF6QQmtD5TC/7/aZ3M1S95xaWPHmDTU01Cj+yq3QqAXArHwZt 3sVK05f1GhVFR8pOMX0OGXMloq9iXZOXzwKSSBMLz27RPeSyWuoyM3qZsCmh2K13V7KIxfk kWYWJceds5s2Y2EX/bxp+hlY+DAM21ebpCVJNpj0urmU5zMOnLu0dqh7HBJa1XjGzbp1PZ7 HXeHIT2HypcHGeYuw7esBCssTHW7VbL1Y5MWuqEwSTI3jlfgqeJcRO5GOrkMHrWrbLvkD+X h5QK5RRr3Ov863/t3GjaVPa57JAgWbqcfu9Py2XEjEhMNNoeAkXL9GzOkTuu1Bs9g== X-QQ-XMRINFO: Mp0Kj//9VHAxzExpfF+O8yhSrljjwrznVg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Add PLT header and entry size definitions for RISC-V architecture. RISC-V uses: - PLT header size: 32 bytes - PLT entry size: 16 bytes This allows perf to correctly identify and display PLT symbols instead of showing them as [unknown]. Signed-off-by: liangzhen --- tools/perf/util/symbol-elf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 76912c62b6a0..e54ff59eb6ef 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -379,6 +379,10 @@ static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *= ehdr, GElf_Shdr *shdr_plt, *plt_header_size =3D 32; *plt_entry_size =3D 16; return true; + case EM_RISCV: + *plt_header_size =3D 32; + *plt_entry_size =3D 16; + return true; case EM_SPARC: *plt_header_size =3D 48; *plt_entry_size =3D 12; --=20 2.34.1 From nobody Tue Apr 14 14:18:27 2026 Received: from smtpbgjp3.qq.com (smtpbgjp3.qq.com [54.92.39.34]) (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 34B273451CD for ; Tue, 14 Apr 2026 03:44:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.92.39.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138255; cv=none; b=RPS6TPIITs717ID5bhyt/8uXeOd3FTtPWpPnh5jGxyrE0/ZIx3tD5a17f0xRx1WmsO7xVAlod+5iOPZlnfjuqkMogA6oRxt7tfgrz7QMPDMeyp7vla9S0Xmw6C7Mf7M50Z9HZFW43hlwbhNozkvHUrkGTzVgCyH3avzv6wlVJsk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776138255; c=relaxed/simple; bh=j6RBVNhgeKjSdJs4S62ZkCH721Crv4FOV27sNm6rnsw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jgTIxC6oCKNVd++u1G2FnHljIWL/fqPS9SywWAL15oPAuQTsWQrxjUNxmd1WKhMfbxpMyCZKadCU5G7leiQiEJO2gtxn3mdV9/3C9GKSu1gKiQdBU1yIupcO034X5hM1nUG++CT9yq6ocb2MJCScf2PlARc5FGe7cWOyP65F8Y0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=Ij5tj+7R; arc=none smtp.client-ip=54.92.39.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="Ij5tj+7R" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1776138237; bh=yX80mvVMbu07IXgMrxN3RxPPX84i9uLc6rmZMDEMLlo=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=Ij5tj+7RkbFrCYjHDJULv/OCKAufjZHketnAOsQvyS3H8QUcfGfii4DFF+BbuOzn5 vPsoSPBZc4XitBXLdwne3tPQ16sY2VR5lK5aLNOyGZqYRTknRPFvKmHyCvIrfuCUoJ Pcjf4GQ3HH/eXjCyQF9YSeUXF47mZy1dGo5phqJY= X-QQ-mid: zesmtpsz5t1776138160te2bc3a15 X-QQ-Originating-IP: 8/PK67r88G8NLwhW58xcwFuNcrIEYpLgHOTHAOtmoaI= Received: from snode5.. ( [61.145.255.150]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 14 Apr 2026 11:42:38 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 17905651787328883968 EX-QQ-RecipientCnt: 26 From: Zane Leung To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org, alexander.shishkin@linux.intel.com, irogers@google.com Cc: coresight@lists.linaro.org, peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com, kan.liang@linux.intel.com, mchitale@gmail.com, anup@brainfault.org, atish.patra@linux.dev, andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com, zhuangqiubin@linux.spacemit.com Subject: [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace Date: Tue, 14 Apr 2026 11:41:53 +0800 Message-Id: <20260414034153.3272485-13-liangzhen@linux.spacemit.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com> References: <20260414034153.3272485-1-liangzhen@linux.spacemit.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-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz6b-0 X-QQ-XMAILINFO: Nao9u9SdMvQSFy/0m4ONVyyCfIA8JNvz1jtpMJyQlxjOBbaRe5Rz/MOG axVNo2mdkpj/CHcDA/a7CBwPxFd9LOjhs/kpf5UszkLPUucBuZr1FbBP9yvCZyh3YRUXO/N Tgi9uG3QoBs4FU0qrPKdqhaZtgNneDqTDNL/qi3Vv8qVBOpJ3E002TpGvx+CDofYTnSeey/ Mh29AcGdu+mwOH02iHenWP8dE6X0gWT5EHPbfPhM63adLNbNv2SUYh7RW+i1bghsafb6mag Cx+kkdifPuGTiyRwLnGHQzmoQjkE+gE00rLL+vfbYSmugaFtLTewvQkETrfTMI65tbdphe9 JzqmTOGZX3pLUg4PrMPGcJ6k6Sk1s0ugmeeTFRy/L5Q4FpqS6kfzC9vivoR4HyE9cvMN0qi Ix71QjJgs4DdQufgyp7HoJYR4+W42MMBOyVzEhUE9RCSRYvlHL6f6EUhJdnK+Dgd7IiXgkJ FL2Hg/vJiPKv9c0yexwp9zoBFA8OBsslRVmCx5fijFvwXyBcvkTFGf0/9s9p2GVJ5UG+7EX gCk02oiA0nIJgFV7mQ17cHogfoaJCBjoxjAkPUAIgLUsj+vToQG1BjYg1JjzIoigEbesLDR vH8oxTCo70jRyLcqV4Xa8LviOqXWp+TTIx7tvPYf5hD0t3Mcn9MeKPhSvfprMucrK2Mvww6 cdqnnAe2fMxTM2ZEU/kyw25EzmyXGpcDmzPHdR/Vd1uNTG4i2RHPYRyO6THYwKbziKbu1ni xxx0WP8yxe0MfczfaUPUbbPqNmpeaSY+UNrT15Gn4tXIChyKCJ0dlszaH5MxXmqr1l6Y1pK vK8PmFKsY9hKFAMSs7fCDltxkBDaf/DfgrF8X/A/ZWLArjACv0CoEtAW4ljQHERhvMul+Qv FqShEsAH7UJqVKHIZFFUKlff0Sm/zST0ILuI3bs3kj5LDRauOotWXuvLWnniyKqkzoH51uT ZBY/plZTMgPf6Tjy2BOVlJ72lWiiIoK4oJ8044sP6dSjlCsIGAMUf3hrDxCqlH+9DfsNEC0 YQ87kjhG3mh84J+KnOdTo2o/FwHd6wUfqb91Rt4xTkTBOHo/9+1jBpB8YlizobAr8iuyq72 Sz2JeOXo4jsnA1MMUWifZrbfSIxVmcJ6QDe0jUe6Lx2OZ+myhcj9Nvpb4xUbI0xEJzd1gSl k0Pe X-QQ-XMRINFO: MPJ6Tf5t3I/ylTmHUqvI8+Wpn+Gzalws3A== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" From: liangzhen Add RISC-V trace decoder implementation and integrate it into perf's auxtrace infrastructure. This enables processing of RISC-V Nexus trace data through the standard perf auxtrace pipeline. Signed-off-by: liangzhen --- tools/perf/util/Build | 1 + tools/perf/util/auxtrace.c | 3 + tools/perf/util/rvtrace-decoder.c | 1039 +++++++++++++++++++++++++++++ tools/perf/util/rvtrace.h | 2 + 4 files changed, 1045 insertions(+) create mode 100644 tools/perf/util/rvtrace-decoder.c diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 648a8552df9e..5913eec9b847 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -148,6 +148,7 @@ perf-util-y +=3D cs-etm-decoder/ endif perf-util-y +=3D cs-etm-base.o =20 +perf-util-y +=3D rvtrace-decoder.o perf-util-y +=3D nexus-rv-decoder/ =20 perf-util-y +=3D parse-branch-options.o diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 944a43d48739..7539252a4543 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -56,6 +56,7 @@ #include "s390-cpumsf.h" #include "util/mmap.h" #include "powerpc-vpadtl.h" +#include "rvtrace.h" =20 #include #include "symbol/kallsyms.h" @@ -1412,6 +1413,8 @@ int perf_event__process_auxtrace_info(const struct pe= rf_tool *tool __maybe_unuse err =3D powerpc_vpadtl_process_auxtrace_info(event, session); break; case PERF_AUXTRACE_RISCV_TRACE: + err =3D rvtrace_process_auxtrace_info(event, session); + break; case PERF_AUXTRACE_UNKNOWN: default: return -EINVAL; diff --git a/tools/perf/util/rvtrace-decoder.c b/tools/perf/util/rvtrace-de= coder.c new file mode 100644 index 000000000000..ce103783d0c3 --- /dev/null +++ b/tools/perf/util/rvtrace-decoder.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2026 Spacemit Limited. All rights reserved. + * Author: liangzhen + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "auxtrace.h" +#include "color.h" +#include "debug.h" +#include "evlist.h" +#include "intlist.h" +#include "machine.h" +#include "map.h" +#include "perf.h" +#include "session.h" +#include "tool.h" +#include "thread.h" +#include "thread_map.h" +#include "thread-stack.h" +#include "util.h" +#include "dso.h" +#include "addr_location.h" +#include +#include "util/synthetic-events.h" +#include "rvtrace.h" +#include "nexus-rv-decoder/nexus-rv-decoder.h" +#include "../../arch/riscv/include/asm/insn.h" + +struct rvtrace_auxtrace { + struct auxtrace auxtrace; + struct auxtrace_queues queues; + struct auxtrace_heap heap; + struct itrace_synth_opts synth_opts; + struct perf_session *session; + struct machine *machine; + + u8 timeless_decoding; + u8 snapshot_mode; + u8 data_queued; + + int num_cpu; + u64 latest_kernel_timestamp; + u32 auxtrace_type; + u64 branches_sample_type; + u64 branches_id; + u64 **metadata; + unsigned int pmu_type; +}; + +struct rvtrace_queue { + struct rvtrace_auxtrace *rvtrace; + struct thread *thread; + struct nexus_rv_insn_decoder *decoder; + struct auxtrace_buffer *buffer; + union perf_event *event_buf; + unsigned int queue_nr; + u64 offset; +}; + +static void rvtrace_set_thread(struct rvtrace_queue *rvtraceq, + pid_t tid) +{ + struct rvtrace_auxtrace *rvtrace =3D rvtraceq->rvtrace; + + if (tid !=3D -1) { + thread__zput(rvtraceq->thread); + rvtraceq->thread =3D machine__find_thread(rvtrace->machine, -1, tid); + } + + /* Couldn't find a known thread */ + if (!rvtraceq->thread) + rvtraceq->thread =3D machine__idle_thread(rvtrace->machine); +} + +static u32 rvtrace_devmem_access(u64 address, size_t size, u8 *buffer) +{ + int fd; + void *map_base, *virt_addr; + u64 page_size =3D 4096, mapped_size =3D 4096; + u64 page_base =3D address & ~(page_size - 1); + u64 offset_in_page =3D address - page_base; + unsigned int width =3D 8 * size; + + if (offset_in_page + width > page_size) + mapped_size *=3D 2; + + fd =3D open("/dev/mem", O_RDONLY | O_SYNC); + if (fd < 0) + return 0; + + map_base =3D mmap(NULL, mapped_size, PROT_READ, MAP_SHARED, fd, (off_t)(p= age_base)); + if (map_base =3D=3D MAP_FAILED) { + pr_debug("failed to mmap device address 0x%lx\n", address); + close(fd); + return 0; + } + + virt_addr =3D (char *)map_base + offset_in_page; + + /* + * Note: higher versions of glibc use automatic vectorization by + * default for memcpy, which can lead to incorrect memory results. + */ + /* memcpy(buffer, virt_addr, size); */ + for (size_t i =3D 0; i < size; i++) + buffer[i] =3D ((volatile u8 *)virt_addr)[i]; + + munmap(map_base, mapped_size); + close(fd); + return size; +} + +static u32 rvtrace_mem_access(void *data, u64 address, enum riscv_privileg= e_mode prv, + int context, size_t size, u8 *buffer) +{ + u8 cpumode; + u64 offset; + int len; + struct machine *machine; + struct addr_location al; + struct dso *dso; + int ret =3D 0; + struct rvtrace_queue *rvtraceq =3D data; + + if (!rvtraceq) + goto out; + + addr_location__init(&al); + + /* If the riscv_privilege_mode is machine mode, access physical address b= y /dev/mem */ + if (prv =3D=3D RISCV_PRIV_MACHINE_MODE) + return rvtrace_devmem_access(address, size, buffer); + + machine =3D rvtraceq->rvtrace->machine; + if (address >=3D machine__kernel_start(machine)) + cpumode =3D PERF_RECORD_MISC_KERNEL; + else + cpumode =3D PERF_RECORD_MISC_USER; + + if (thread__tid(rvtraceq->thread) !=3D context) + rvtrace_set_thread(rvtraceq, context); + + if (!thread__find_map(rvtraceq->thread, cpumode, address, &al)) + goto out; + + dso =3D map__dso(al.map); + if (!dso) + goto out; + + if (dso__data(dso)->status =3D=3D DSO_DATA_STATUS_ERROR && + dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE)) + goto out; + + offset =3D map__map_ip(al.map, address); + + map__load(al.map); + + len =3D dso__data_read_offset(dso, machine, offset, buffer, size); + if (len <=3D 0) { + ui__warning_once("RISC-V Nexus Trace: Missing DSO. Use 'perf archive' or= debuginfod to export data from the traced system.\n" + " Enable CONFIG_PROC_KCORE or use option '-k /path/t= o/vmlinux' for kernel symbols.\n"); + if (!dso__auxtrace_warned(dso)) { + pr_err("RISC-V Nexus Trace: Debug data not found for address %#"PRIx64"= in %s\n", + address, + dso__long_name(dso) ? dso__long_name(dso) : "Unknown"); + dso__set_auxtrace_warned(dso); + } + goto out; + } + ret =3D len; +out: + addr_location__exit(&al); + return ret; +} + + +static u8 rvtrace_cpu_mode(enum riscv_privilege_mode prv) +{ + u8 cpumode; + + switch (prv) { + case RISCV_PRIV_USER_MODE: + cpumode =3D PERF_RECORD_MISC_USER; + break; + case RISCV_PRIV_SUPERVISOR_MODE: + case RISCV_PRIV_MACHINE_MODE: + default: + cpumode =3D PERF_RECORD_MISC_KERNEL; + break; + } + return cpumode; +} + +static void rvtrace_synth_copy_insn(struct rvtrace_queue *rvtraceq, + struct nexus_rv_packet *packet, + struct perf_sample *sample) +{ + int ret; + u32 insn; + + ret =3D rvtrace_mem_access(rvtraceq, sample->ip, packet->prv, packet->con= text, + sizeof(insn), (u8 *)&insn); + if (!ret) + return; + + sample->insn_len =3D ((insn & __INSN_LENGTH_MASK) =3D=3D __INSN_LENGTH_GE= _32) ? 4 : 2; + memcpy(sample->insn, &insn, sample->insn_len); +} + +static inline u64 rvtrace_resolve_sample_time(struct rvtrace_queue *rvtrac= eq, + struct nexus_rv_packet *packet) +{ + struct rvtrace_auxtrace *rvtrace =3D rvtraceq->rvtrace; + + if (!rvtrace->timeless_decoding) + return packet->timestamp; + else + return rvtrace->latest_kernel_timestamp; +} + +static int rvtrace_synth_branch_sample(struct rvtrace_queue *rvtraceq, + struct nexus_rv_packet *packet) +{ + int ret =3D 0; + struct rvtrace_auxtrace *rvtrace =3D rvtraceq->rvtrace; + struct perf_sample sample; + union perf_event *event =3D rvtraceq->event_buf; + + perf_sample__init(&sample, /*all=3D*/true); + event->sample.header.type =3D PERF_RECORD_SAMPLE; + event->sample.header.misc =3D rvtrace_cpu_mode(packet->prv); + event->sample.header.size =3D sizeof(struct perf_event_header); + + if (thread__tid(rvtraceq->thread) !=3D packet->context) + rvtrace_set_thread(rvtraceq, packet->context); + + /* Set time field based on rvtrace auxtrace config. */ + sample.time =3D rvtrace_resolve_sample_time(rvtraceq, packet); + + sample.ip =3D packet->start_addr; + sample.pid =3D thread__pid(rvtraceq->thread); + sample.tid =3D thread__tid(rvtraceq->thread); + sample.addr =3D packet->end_addr; + sample.insn_cnt =3D packet->insn_cnt; + sample.id =3D rvtraceq->rvtrace->branches_id; + sample.stream_id =3D rvtraceq->rvtrace->branches_id; + sample.period =3D 1; + sample.cpu =3D packet->cpu; + sample.flags =3D 0; + sample.cpumode =3D event->sample.header.misc; + + rvtrace_synth_copy_insn(rvtraceq, packet, &sample); + + ret =3D perf_session__deliver_synth_event(rvtrace->session, event, &sampl= e); + if (ret) + pr_err( + "RISC-V Trace: failed to deliver instruction event, error %d\n", + ret); + perf_sample__exit(&sample); + + return ret; +} + +static int rvtrace_synth_events(struct rvtrace_auxtrace *rvtrace, + struct perf_session *session) +{ + struct evlist *evlist =3D session->evlist; + struct evsel *evsel; + struct perf_event_attr attr; + bool found =3D false; + u64 id; + int err; + + evlist__for_each_entry(evlist, evsel) { + if (evsel->core.attr.type =3D=3D rvtrace->pmu_type) { + found =3D true; + break; + } + } + + if (!found) { + pr_debug("No selected events with RISC-V Trace data\n"); + return 0; + } + + memset(&attr, 0, sizeof(struct perf_event_attr)); + attr.size =3D sizeof(struct perf_event_attr); + attr.type =3D PERF_TYPE_HARDWARE; + attr.sample_type =3D evsel->core.attr.sample_type & PERF_SAMPLE_MASK; + attr.sample_type |=3D PERF_SAMPLE_IP | PERF_SAMPLE_TID | + PERF_SAMPLE_PERIOD; + if (rvtrace->timeless_decoding) + attr.sample_type &=3D ~(u64)PERF_SAMPLE_TIME; + else + attr.sample_type |=3D PERF_SAMPLE_TIME; + + attr.exclude_user =3D evsel->core.attr.exclude_user; + attr.exclude_kernel =3D evsel->core.attr.exclude_kernel; + attr.exclude_hv =3D evsel->core.attr.exclude_hv; + attr.exclude_host =3D evsel->core.attr.exclude_host; + attr.exclude_guest =3D evsel->core.attr.exclude_guest; + attr.sample_id_all =3D evsel->core.attr.sample_id_all; + attr.read_format =3D evsel->core.attr.read_format; + + /* create new id val to be a fixed offset from evsel id */ + id =3D evsel->core.id[0] + 1000000000; + if (!id) + id =3D 1; + + if (rvtrace->synth_opts.branches) { + attr.config =3D PERF_COUNT_HW_BRANCH_INSTRUCTIONS; + attr.sample_period =3D 1; + attr.sample_type |=3D PERF_SAMPLE_ADDR; + + err =3D perf_session__deliver_synth_attr_event(session, &attr, id); + if (err) + return err; + rvtrace->branches_sample_type =3D attr.sample_type; + rvtrace->branches_id =3D id; + id +=3D 1; + attr.sample_type &=3D ~(u64)PERF_SAMPLE_ADDR; + } + + return 0; +} + +static int rvtrace_process_queue(struct rvtrace_queue *rvtraceq) +{ + struct nexus_rv_packet_buffer *packet_buffer =3D &rvtraceq->decoder->pack= et_buffer; + + for (int i =3D 0; i < packet_buffer->size; i++) { + struct nexus_rv_packet packet =3D packet_buffer->packets[i]; + + if (packet.sample_type =3D=3D RVTRACE_RANGE) + rvtrace_synth_branch_sample(rvtraceq, &packet); + else if (packet.sample_type =3D=3D RVTRACE_LOSS) + fprintf(stdout, "RISC-V Trace: A FIFO overrun has resulted in the loss = of one or more messages\n"); + } + + return 0; +} + +static int rvtrace_get_trace(struct rvtrace_queue *rvtraceq) +{ + struct auxtrace_buffer *aux_buffer =3D rvtraceq->buffer; + struct auxtrace_buffer *old_buffer =3D aux_buffer; + struct auxtrace_queue *queue; + + queue =3D &rvtraceq->rvtrace->queues.queue_array[rvtraceq->queue_nr]; + + aux_buffer =3D auxtrace_buffer__next(queue, aux_buffer); + + /* If no more data, drop the previous auxtrace_buffer and return */ + if (!aux_buffer) { + if (old_buffer) + auxtrace_buffer__drop_data(old_buffer); + return 0; + } + + rvtraceq->buffer =3D aux_buffer; + + /* If the aux_buffer doesn't have data associated, try to load it */ + if (!aux_buffer->data) { + /* get the file desc associated with the perf data file */ + int fd =3D perf_data__fd(rvtraceq->rvtrace->session->data); + + aux_buffer->data =3D auxtrace_buffer__get_data(aux_buffer, fd); + if (!aux_buffer->data) + return -ENOMEM; + } + + /* If valid, drop the previous buffer */ + if (old_buffer) + auxtrace_buffer__drop_data(old_buffer); + + return aux_buffer->size; +} + +/* + * rvtrace_get_data_block: Fetch a block from the auxtrace_buffer queue + * if need be. + * Returns: < 0 if error + * =3D 0 if no more auxtrace_buffer to read + * > 0 if the current buffer isn't empty yet + */ +static int rvtrace_get_data_block(struct rvtrace_queue *rvtraceq) +{ + int ret; + + ret =3D rvtrace_get_trace(rvtraceq); + if (ret <=3D 0) + return ret; + + /* + * We cannot assume consecutive blocks in the data file + * are contiguous, reset the decoder to force re-sync. + */ + ret =3D nexus_rv_insn_decoder_reset(rvtraceq->decoder); + if (ret) + return ret; + + return rvtraceq->buffer->size; +} + +static int rvtrace_run_timeless_decoder(struct rvtrace_queue *rvtraceq) +{ + int ret; + + while (1) { + ret =3D rvtrace_get_data_block(rvtraceq); + if (ret < 0) + return ret; + + if (ret =3D=3D 0) + break; + + ret =3D nexus_rv_insn_decode_data(rvtraceq->decoder, + rvtraceq->buffer->data, + rvtraceq->buffer->size); + if (ret) + return ret; + + ret =3D rvtrace_process_queue(rvtraceq); + if (ret) + return ret; + } + + return ret; +} + +static int rvtrace_process_timeless_queues(struct rvtrace_auxtrace *rvtrac= e, + pid_t tid) +{ + unsigned int i; + struct auxtrace_queues *queues =3D &rvtrace->queues; + + for (i =3D 0; i < queues->nr_queues; i++) { + struct auxtrace_queue *queue =3D &rvtrace->queues.queue_array[i]; + struct rvtrace_queue *rvtraceq =3D queue->priv; + + if (rvtraceq && ((tid =3D=3D -1) || (queue->tid =3D=3D tid))) { + rvtrace_set_thread(rvtraceq, queue->tid); + rvtrace_run_timeless_decoder(rvtraceq); + } + } + + return 0; +} + +static u64 rvtrace_queue_get_timestamp(struct rvtrace_queue *rvtraceq) +{ + struct nexus_rv_packet_buffer *packet_buffer =3D &rvtraceq->decoder->pack= et_buffer; + + for (int i =3D 0; i < packet_buffer->size; i++) { + struct nexus_rv_packet packet =3D packet_buffer->packets[i]; + + if (packet.sample_type =3D=3D RVTRACE_RANGE) + return packet.timestamp; + } + + return 0; +} + +static int rvtrace_queue_first_timestamp(struct rvtrace_auxtrace *rvtrace, + struct rvtrace_queue *rvtraceq, + unsigned int queue_nr) +{ + int ret; + u64 timestamp =3D 0; + + /* Decode the first segment of data until a timestamp is found, + * then add it to the heap for sorting by time across multiple + * queues. + */ + while (1) { + /* + * Fetch an aux_buffer from this rvtraceq. Bail if no more + * blocks or an error has been encountered. + */ + ret =3D rvtrace_get_data_block(rvtraceq); + if (ret <=3D 0) + goto out; + + ret =3D nexus_rv_insn_decode_data(rvtraceq->decoder, + rvtraceq->buffer->data, + rvtraceq->buffer->size); + if (ret) + goto out; + + timestamp =3D rvtrace_queue_get_timestamp(rvtraceq); + + /* We found a timestamp, no need to continue. */ + if (timestamp) + break; + } + + /* We have a timestamp and add it to the min heap */ + ret =3D auxtrace_heap__add(&rvtrace->heap, queue_nr, timestamp); +out: + return ret; +} + +static int rvtrace_process_timestamped_queues(struct rvtrace_auxtrace *rvt= race) +{ + int ret =3D 0; + unsigned int queue_nr, i; + struct auxtrace_queue *queue; + struct rvtrace_queue *rvtraceq; + + /* First, find the first timestamp for each queue and add it to the heap.= */ + for (i =3D 0; i < rvtrace->queues.nr_queues; i++) { + queue =3D &rvtrace->queues.queue_array[i]; + rvtraceq =3D queue->priv; + if (!rvtraceq) + continue; + + rvtrace_set_thread(rvtraceq, queue->tid); + + ret =3D rvtrace_queue_first_timestamp(rvtrace, rvtraceq, i); + if (ret) + return ret; + } + + /* Process queues in the heap in timestamp order */ + while (1) { + if (!rvtrace->heap.heap_cnt) + break; + + /* Take the entry at the top of the min heap */ + queue_nr =3D rvtrace->heap.heap_array[0].queue_nr; + queue =3D &rvtrace->queues.queue_array[queue_nr]; + rvtraceq =3D queue->priv; + + /* + * Remove the top entry from the heap since we are about + * to process it. + */ + auxtrace_heap__pop(&rvtrace->heap); + + /* + * Packets associated with this timestamp are already in + * the rvtraceq->packet_buffer, so process them. + */ + ret =3D rvtrace_process_queue(rvtraceq); + if (ret) + return ret; + + /* + * Packets for this timestamp have been processed, time to + * move on to the next timestamp, find the next timestamp + * for this rvtraceq. + */ + ret =3D rvtrace_queue_first_timestamp(rvtrace, rvtraceq, queue_nr); + if (ret) + return ret; + } + + return 0; +} + +static struct rvtrace_queue *rvtrace_alloc_queue(struct rvtrace_auxtrace *= rvtrace, + unsigned int queue_nr) +{ + struct nexus_rv_insn_decoder_params params; + struct rvtrace_queue *rvtraceq; + + rvtraceq =3D zalloc(sizeof(*rvtraceq)); + if (!rvtraceq) + return NULL; + + rvtraceq->event_buf =3D malloc(PERF_SAMPLE_MAX_SIZE); + if (!rvtraceq->event_buf) + goto out_free; + + rvtraceq->rvtrace =3D rvtrace; + rvtraceq->queue_nr =3D queue_nr; + + params.mem_access =3D rvtrace_mem_access; + params.data =3D rvtraceq; + params.formatted =3D true; + if (!rvtrace->metadata[0][RVTRACE_ENCODER_INHB_SRC]) + params.src_bits =3D rvtrace->metadata[0][RVTRACE_ENCODER_SRCBITS]; + + rvtraceq->decoder =3D nexus_rv_insn_decoder_new(¶ms); + if (!rvtraceq->decoder) + goto out_free; + + rvtraceq->offset =3D 0; + + return rvtraceq; + +out_free: + zfree(&rvtraceq->event_buf); + free(rvtraceq); + + return NULL; +} + +static int rvtrace_setup_queue(struct rvtrace_auxtrace *rvtrace, + struct auxtrace_queue *queue, + unsigned int queue_nr) +{ + struct rvtrace_queue *rvtraceq =3D queue->priv; + + if (list_empty(&queue->head) || rvtraceq) + return 0; + + rvtraceq =3D rvtrace_alloc_queue(rvtrace, queue_nr); + + if (!rvtraceq) + return -ENOMEM; + + queue->priv =3D rvtraceq; + + return 0; +} + +static int rvtrace_setup_queues(struct rvtrace_auxtrace *rvtrace) +{ + unsigned int i; + int ret; + + for (i =3D 0; i < rvtrace->queues.nr_queues; i++) { + ret =3D rvtrace_setup_queue(rvtrace, &rvtrace->queues.queue_array[i], i); + if (ret) + return ret; + } + + return 0; +} + +static int rvtrace_update_queues(struct rvtrace_auxtrace *rvtrace) +{ + if (rvtrace->queues.new_data) { + rvtrace->queues.new_data =3D false; + return rvtrace_setup_queues(rvtrace); + } + + return 0; +} + +static int rvtrace_flush_events(struct perf_session *session, + const struct perf_tool *tool) +{ + struct rvtrace_auxtrace *rvtrace =3D container_of(session->auxtrace, + struct rvtrace_auxtrace, + auxtrace); + int ret; + + if (dump_trace) + return 0; + + if (!tool->ordered_events) + return -EINVAL; + + ret =3D rvtrace_update_queues(rvtrace); + if (ret < 0) + return ret; + + if (rvtrace->timeless_decoding) { + /* + * Pass tid =3D -1 to process all queues. But likely they will have + * already been processed on PERF_RECORD_EXIT anyway. + */ + return rvtrace_process_timeless_queues(rvtrace, -1); + } + + return rvtrace_process_timestamped_queues(rvtrace); +} + +static void rvtrace_free_queue(void *priv) +{ + struct rvtrace_queue *rvtraceq =3D priv; + + if (!rvtraceq) + return; + + thread__zput(rvtraceq->thread); + nexus_rv_insn_decoder_free(rvtraceq->decoder); + zfree(&rvtraceq->event_buf); + free(rvtraceq); +} + +static void rvtrace_free_events(struct perf_session *session) +{ + unsigned int i; + struct rvtrace_auxtrace *rvtrace =3D container_of(session->auxtrace, + struct rvtrace_auxtrace, + auxtrace); + struct auxtrace_queues *queues =3D &rvtrace->queues; + + for (i =3D 0; i < queues->nr_queues; i++) { + rvtrace_free_queue(queues->queue_array[i].priv); + queues->queue_array[i].priv =3D NULL; + } + + auxtrace_queues__free(queues); +} + +static void rvtrace_free(struct perf_session *session) +{ + struct rvtrace_auxtrace *rvtrace =3D container_of(session->auxtrace, + struct rvtrace_auxtrace, + auxtrace); + rvtrace_free_events(session); + session->auxtrace =3D NULL; + for (int i =3D 0; i < rvtrace->num_cpu; i++) + zfree(&rvtrace->metadata[i]); + + zfree(&rvtrace->metadata); + zfree(&rvtrace); +} + +static bool rvtrace_evsel_is_auxtrace(struct perf_session *session, + struct evsel *evsel) +{ + struct rvtrace_auxtrace *aux =3D container_of(session->auxtrace, + struct rvtrace_auxtrace, + auxtrace); + + return evsel->core.attr.type =3D=3D aux->pmu_type; +} + +static int rvtrace_process_itrace_start(struct rvtrace_auxtrace *rvtrace, + union perf_event *event) +{ + struct thread *th; + + if (rvtrace->timeless_decoding) + return 0; + + /* + * Add the tid/pid to the log so that we can get a match when + * we get a contextID from the decoder. + */ + th =3D machine__findnew_thread(rvtrace->machine, + event->itrace_start.pid, + event->itrace_start.tid); + if (!th) + return -ENOMEM; + + thread__put(th); + + return 0; +} + +static int rvtrace_process_event(struct perf_session *session, + union perf_event *event, + struct perf_sample *sample, + const struct perf_tool *tool) +{ + int err =3D 0; + u64 timestamp; + struct rvtrace_auxtrace *rvtrace =3D container_of(session->auxtrace, + struct rvtrace_auxtrace, + auxtrace); + + if (dump_trace) + return 0; + + if (!tool->ordered_events) { + pr_err("RISCV Trace requires ordered events\n"); + return -EINVAL; + } + + if (sample->time && (sample->time !=3D (u64) -1)) + timestamp =3D sample->time; + else + timestamp =3D 0; + + if (timestamp || rvtrace->timeless_decoding) { + err =3D rvtrace_update_queues(rvtrace); + if (err) + return err; + } + + switch (event->header.type) { + case PERF_RECORD_EXIT: + /* + * Don't need to wait for rvtrace_flush_events() in per-thread/timeless + * mode to start the decode because we know there will be no more trace + * from this thread. All this does is emit samples earlier than waiting + * for the flush in other modes, but with timestamps it makes sense to + * wait for flush so that events from different threads are interleaved + * properly. + */ + if (rvtrace->timeless_decoding) + return rvtrace_process_timeless_queues(rvtrace, event->fork.tid); + break; + + case PERF_RECORD_ITRACE_START: + return rvtrace_process_itrace_start(rvtrace, event); + + case PERF_RECORD_AUX: + /* + * Record the latest kernel timestamp available for rollback when + * no trace timestamp is available. + */ + if (timestamp) + rvtrace->latest_kernel_timestamp =3D timestamp; + break; + + default: + break; + } + + return 0; +} + +static void rvtrace_dump(struct rvtrace_auxtrace *rvtrace, + unsigned char *buf, size_t len) +{ + int ret; + struct nexus_rv_pkt_decoder_params params; + struct nexus_rv_pkt_decoder *decoder; + const char *color =3D PERF_COLOR_BLUE; + + // TODO: Determine whether the trace data contains a CoreSight formatter = frame + params.formatted =3D true; + if (!rvtrace->metadata[0][RVTRACE_ENCODER_INHB_SRC]) + params.src_bits =3D rvtrace->metadata[0][RVTRACE_ENCODER_SRCBITS]; + + decoder =3D nexus_rv_pkt_decoder_new(¶ms); + if (!decoder) { + color_fprintf(stdout, color, " Failed to create decoder\n"); + return; + } + + color_fprintf(stdout, color, + ". ... Trace Encoder Trace data: size %#zx bytes\n", + len); + + ret =3D nexus_rv_pkt_desc(decoder, buf, len); + if (ret) + color_fprintf(stdout, color, " Bad packet!\n"); + + nexus_rv_pkt_decoder_free(decoder); +} + +static void rvtrace_dump_event(struct rvtrace_auxtrace *rvtrace, + unsigned char *buf, size_t len) +{ + printf(".\n"); + rvtrace_dump(rvtrace, buf, len); +} + +static int rvtrace_process_auxtrace_event(struct perf_session *session, + union perf_event *event, + const struct perf_tool *tool __maybe_unused) +{ + struct rvtrace_auxtrace *rvtrace =3D container_of(session->auxtrace, + struct rvtrace_auxtrace, + auxtrace); + if (!rvtrace->data_queued) { + struct auxtrace_buffer *buffer; + off_t data_offset; + int fd =3D perf_data__fd(session->data); + bool is_pipe =3D perf_data__is_pipe(session->data); + int err; + + if (is_pipe) + data_offset =3D 0; + else { + data_offset =3D lseek(fd, 0, SEEK_CUR); + if (data_offset =3D=3D -1) + return -errno; + } + + err =3D auxtrace_queues__add_event(&rvtrace->queues, session, + event, data_offset, &buffer); + + if (err) + return err; + + if (dump_trace) + if (auxtrace_buffer__get_data(buffer, fd)) { + rvtrace_dump_event(rvtrace, buffer->data, buffer->size); + auxtrace_buffer__put_data(buffer); + } + } + + + return 0; +} + +static bool rvtrace_is_timeless_decoding(struct rvtrace_auxtrace *rvtrace) +{ + struct evsel *evsel; + struct evlist *evlist =3D rvtrace->session->evlist; + bool timeless_decoding =3D true; + + /* + * Circle through the list of event and complain if we find one + * with the time bit set. + */ + evlist__for_each_entry(evlist, evsel) { + if ((evsel->core.attr.sample_type & PERF_SAMPLE_TIME)) + timeless_decoding =3D false; + } + + return timeless_decoding; +} + +static const char * const rvtrace_global_header_fmts[] =3D { + [RVTRACE_PMU_TYPE_CPUS] =3D " PMU type/num cpus %llx\n", +}; + +static const char * const rvtrace_encoder_priv_fmts[] =3D { + [RVTRACE_ENCODER_CPU] =3D " CPU %lld\n", + [RVTRACE_ENCODER_NR_TRC_PARAMS] =3D " NR_TRC_PARAMS %lld\n", + [RVTRACE_ENCODER_FORMAT] =3D " FORMAT %lld\n", + [RVTRACE_ENCODER_CONTEXT] =3D " CONTEXT %lld\n", + [RVTRACE_ENCODER_INHB_SRC] =3D " INHB_SRC %lld\n", + [RVTRACE_ENCODER_SRCBITS] =3D " SRCBITS %lld\n", + [RVTRACE_ENCODER_SRCID] =3D " SRCID %lld\n", +}; + +static void rvtrace_print_auxtrace_info(u64 *val, int num_cpu) +{ + int i, j, cpu =3D 0, nr_params =3D 0, fmt_offset =3D 0; + + for (i =3D 0; i < RVTRACE_HEADER_MAX; i++) + fprintf(stdout, rvtrace_global_header_fmts[i], val[i]); + + for (i =3D RVTRACE_HEADER_MAX; cpu < num_cpu; cpu++) { + fprintf(stdout, rvtrace_encoder_priv_fmts[RVTRACE_ENCODER_CPU], val[i++]= ); + nr_params =3D val[i++]; + fmt_offset =3D RVTRACE_ENCODER_FORMAT; + for (j =3D fmt_offset; j < nr_params + fmt_offset; j++, i++) + fprintf(stdout, rvtrace_encoder_priv_fmts[j], val[i]); + } +} + +int rvtrace_process_auxtrace_info(union perf_event *event, + struct perf_session *session) +{ + struct perf_record_auxtrace_info *auxtrace_info =3D &event->auxtrace_info; + struct rvtrace_auxtrace *rvtrace =3D NULL; + int err =3D 0; + int i; + int num_cpu =3D 0; + u64 *ptr =3D NULL; + u64 **metadata =3D NULL; + + /* First the global part */ + ptr =3D (u64 *) auxtrace_info->priv; + num_cpu =3D ptr[RVTRACE_PMU_TYPE_CPUS] & 0xffffffff; + metadata =3D zalloc(sizeof(*metadata) * num_cpu); + if (!metadata) + err =3D -ENOMEM; + + /* Start parsing after the common part of the header */ + i =3D RVTRACE_HEADER_MAX; + + for (int j =3D 0; j < num_cpu; j++) { + metadata[j] =3D zalloc(sizeof(*metadata[j]) * RVTRACE_ENCODER_PRIV_MAX); + if (!metadata[j]) { + err =3D -ENOMEM; + goto err_free_metadata; + } + + for (int k =3D 0; k < RVTRACE_ENCODER_PRIV_MAX; k++) + metadata[j][k] =3D ptr[i + k]; + i +=3D RVTRACE_ENCODER_PRIV_MAX; + } + + rvtrace =3D zalloc(sizeof(*rvtrace)); + if (!rvtrace) { + err =3D -ENOMEM; + goto err_free_metadata; + } + + err =3D auxtrace_queues__init(&rvtrace->queues); + if (err) + goto err_free_rvtrace; + + if (session->itrace_synth_opts->set) { + rvtrace->synth_opts =3D *session->itrace_synth_opts; + } else { + itrace_synth_opts__set_default(&rvtrace->synth_opts, + session->itrace_synth_opts->default_no_sample); + rvtrace->synth_opts.callchain =3D false; + } + + rvtrace->session =3D session; + rvtrace->machine =3D &session->machines.host; + rvtrace->num_cpu =3D num_cpu; + rvtrace->pmu_type =3D (unsigned int) ((ptr[RVTRACE_PMU_TYPE_CPUS] >> 32) = & 0xffffffff); + rvtrace->metadata =3D metadata; + + rvtrace->auxtrace_type =3D auxtrace_info->type; + rvtrace->timeless_decoding =3D rvtrace_is_timeless_decoding(rvtrace); + + rvtrace->auxtrace.process_event =3D rvtrace_process_event; + rvtrace->auxtrace.process_auxtrace_event =3D rvtrace_process_auxtrace_eve= nt; + rvtrace->auxtrace.flush_events =3D rvtrace_flush_events; + rvtrace->auxtrace.free_events =3D rvtrace_free_events; + rvtrace->auxtrace.free =3D rvtrace_free; + rvtrace->auxtrace.evsel_is_auxtrace =3D rvtrace_evsel_is_auxtrace; + session->auxtrace =3D &rvtrace->auxtrace; + + if (dump_trace) { + rvtrace_print_auxtrace_info(ptr, num_cpu); + return 0; + } + + err =3D rvtrace_synth_events(rvtrace, session); + if (err) + goto err_free_queues; + + err =3D auxtrace_queues__process_index(&rvtrace->queues, session); + if (err) + goto err_free_queues; + + rvtrace->data_queued =3D rvtrace->queues.populated; + + return 0; + +err_free_queues: + auxtrace_queues__free(&rvtrace->queues); + session->auxtrace =3D NULL; +err_free_rvtrace: + zfree(&rvtrace); +err_free_metadata: + for (int j =3D 0; j < num_cpu; j++) + zfree(&metadata[j]); + zfree(&metadata); + + return -EINVAL; +} diff --git a/tools/perf/util/rvtrace.h b/tools/perf/util/rvtrace.h index 1e48ed989dd7..84a59d3dc296 100644 --- a/tools/perf/util/rvtrace.h +++ b/tools/perf/util/rvtrace.h @@ -35,4 +35,6 @@ enum { #define RVTRACE_HEADER_SIZE (RVTRACE_HEADER_MAX * sizeof(u64)) #define RVTRACE_ENCODER_PRIV_SIZE (RVTRACE_ENCODER_PRIV_MAX * sizeof(u64)) =20 +int rvtrace_process_auxtrace_info(union perf_event *event, struct perf_ses= sion *session); + #endif --=20 2.34.1