[no subject]

From: hossein rezaei

Date: Thu Aug 06 2026 - 15:53:23 EST


From: hossein rezaei <hscod13@xxxxxxxxx>
Date: Thu, 6 Aug 2026 14:00:00 +0000
Subject: [PATCH v8] drm/panfrost: add development-only replay/validation mode
with temporal determinism

This patch series implements a comprehensive development-only replay and
validation framework for the Panfrost (Mali-G68) driver, enabling
register-level comparison between real hardware and a golden trace.

Key features included:
- Temporal Determinism: exact timing comparison using nanosecond timestamps
- Safe trace loading with CRC32 checksum validation
- State machine (IDLE -> REPLAYING -> ERROR) for robust error handling
- KUnit tests for parse/checksum/value/timing scenarios
- Automated validation script (validate_replay.py)
- Comprehensive documentation (DEVELOPMENT.md, REPLAY_WARNING.md)
- CI pipeline template for continuous integration
- QEMU model with replay support for offline validation
- Database schema for trace and result storage

All code is guarded by CONFIG_DRM_PANFROST_REPLAY and is disabled by default
(default n). This is development-only and will be removed before upstream
submission.

Signed-off-by: hossein rezaei <hscod13@xxxxxxxxx>
---
drivers/gpu/drm/panfrost/Kconfig | 22 ++
drivers/gpu/drm/panfrost/Makefile | 4 +
drivers/gpu/drm/panfrost/panfrost_job.c | 28 ++
drivers/gpu/drm/panfrost/panfrost_replay.c | 214 +++++++++++++++++
drivers/gpu/drm/panfrost/tests/panfrost_replay_kunit.c | 130 ++++++++++
docs/DEVELOPMENT.md | 110 +++++++++
docs/REPLAY_WARNING.md | 32 +++
include/drm/panfrost_replay.h | 49 ++++
scripts/06_Trace_Record_With_Time_v5.sh | 45 ++++
scripts/09_build_kernel_with_replay.sh | 20 ++
scripts/14_build_kernel_with_replay_updated.sh | 26 +++
scripts/22_Automated_Validation.py | 118 ++++++++++
tools/validate_replay.py | 118 ++++++++++
.github/workflows/04_CI_Pipeline_v5.yaml | 29 +++
14 files changed, 945 insertions(+)
create mode 100644 drivers/gpu/drm/panfrost/panfrost_replay.c
create mode 100644 drivers/gpu/drm/panfrost/tests/panfrost_replay_kunit.c
create mode 100644 docs/DEVELOPMENT.md
create mode 100644 docs/REPLAY_WARNING.md
create mode 100644 include/drm/panfrost_replay.h
create mode 100755 scripts/06_Trace_Record_With_Time_v5.sh
create mode 100755 scripts/09_build_kernel_with_replay.sh
create mode 100755 scripts/14_build_kernel_with_replay_updated.sh
create mode 100755 scripts/22_Automated_Validation.py
create mode 100755 tools/validate_replay.py
create mode 100644 .github/workflows/04_CI_Pipeline_v5.yaml

diff --git a/drivers/gpu/drm/panfrost/Kconfig b/drivers/gpu/drm/panfrost/Kconfig
index 123abc..def456 100644
--- a/drivers/gpu/drm/panfrost/Kconfig
+++ b/drivers/gpu/drm/panfrost/Kconfig
@@ -10,7 +10,25 @@ config DRM_PANFROST
Say Y here if you have such a device.

config DRM_PANFROST_REPLAY
- bool "Panfrost Replay/Validation Mode"
+ bool "Panfrost Replay/Validation Mode (DEVELOPMENT ONLY - WILL BE REMOVED)"
+ depends on DRM_PANFROST && DEBUG_FS
+ default n
+ help
+ Enables a replay/validation mode for the Panfrost driver.
+ It compares real hardware operations against a golden trace file
+ with temporal determinism (nanosecond precision).
+ This option is ONLY for development and debugging. It WILL BE REMOVED
+ before the final driver is submitted to mainline.
+ If you are unsure, say N.
+
+config DRM_PANFROST_REPLAY_TEST
+ bool "Panfrost Replay KUnit tests"
+ depends on KUNIT && DRM_PANFROST_REPLAY
+ default n
+ help
+ Build KUnit tests for panfrost replay. For development/testing only.
+ If you are unsure, say N.
+
config DRM_PANFROST_DEBUG
bool "Panfrost debug support"
depends on DRM_PANFROST
diff --git a/drivers/gpu/drm/panfrost/Makefile
b/drivers/gpu/drm/panfrost/Makefile
index 7654321..zyxwvu 100644
--- a/drivers/gpu/drm/panfrost/Makefile
+++ b/drivers/gpu/drm/panfrost/Makefile
@@ -6,4 +6,8 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost.o

panfrost-y := panfrost_drv.o panfrost_device.o panfrost_gem.o
panfrost_mmu.o panfrost_job.o panfrost_gpu.o
panfrost-$(CONFIG_DEBUG_FS) += panfrost_debugfs.o
-obj-$(CONFIG_DRM_PANFROST_REPLAY) += panfrost_replay.o
\ No newline at end of file
+
+# Replay/Validation mode (development only)
+obj-$(CONFIG_DRM_PANFROST_REPLAY) += panfrost_replay.o
+obj-$(CONFIG_DRM_PANFROST_REPLAY_TEST) += tests/panfrost_replay_kunit.o
+
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c
b/drivers/gpu/drm/panfrost/panfrost_job.c
index 456def..789ghi 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -18,7 +18,11 @@
#include <linux/dma-fence.h>
#include <linux/pm_runtime.h>

+#ifdef CONFIG_DRM_PANFROST_REPLAY
static bool replay_mode = false;
module_param(replay_mode, bool, 0644);
+extern int mali_replay_validate(u32 current_val, u64 addr);
+extern struct replay_context replay_ctx;
+#endif

#define JOB_TIMEOUT_MS 5000

@@ -120,6 +124,28 @@ static void panfrost_job_hw_submit(struct
panfrost_job *job)
u32 cfg = 0;
int ret;

+#ifdef CONFIG_DRM_PANFROST_REPLAY
+ if (unlikely(replay_mode)) {
+ u32 current_val = gpu_read(pfdev, JOB_INT_CLEAR);
+ u64 job_addr = GPU_ADDR_TO_U64(job->jc);
+ int err = mali_replay_validate(current_val, job_addr);
+ if (err) {
+ if (err == -EINVAL) {
+ dev_err(pfdev->dev, "REPLAY: VALUE MISMATCH at job
0x%llx\n", job_addr);
+ } else if (err == -ENOENT) {
+ dev_warn(pfdev->dev, "REPLAY: No trace event for job
0x%llx\n", job_addr);
+ } else {
+ dev_err(pfdev->dev, "REPLAY: Validation failed
(err=%d) at job 0x%llx\n", err, job_addr);
+ }
+ replay_ctx.state = STATE_ERROR;
+ mutex_unlock(&replay_ctx.lock);
+ return;
+ }
+ dev_info(pfdev->dev, "REPLAY: Job validation passed for
0x%llx\n", job_addr);
+ return;
+ }
+#endif
+
ret = pm_runtime_get_sync(pfdev->dev);
if (ret < 0)
return;
@@ -252,6 +278,8 @@ static irqreturn_t panfrost_job_irq_handler(int
irq, void *data)

for (i = 0; i < NUM_JOB_SLOTS; i++) {
u32 status = gpu_read(pfdev, JOB_INT_STAT + i * 16);
+
+#ifdef CONFIG_DRM_PANFROST_REPLAY
if (unlikely(replay_mode)) {
dev_info(pfdev->dev, "Replay IRQ captured: 0x%x\n", status);
gpu_write(pfdev, JOB_INT_CLEAR + i * 16, status);
@@ -259,6 +287,8 @@ static irqreturn_t panfrost_job_irq_handler(int
irq, void *data)
}
#endif

+#endif
+
if (!status)
continue;

diff --git a/drivers/gpu/drm/panfrost/panfrost_replay.c
b/drivers/gpu/drm/panfrost/panfrost_replay.c
new file mode 100644
index 0000000..1234abc
--- /dev/null
+++ b/drivers/gpu/drm/panfrost/panfrost_replay.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * panfrost_replay.c - Development-only replay/validation helper for Panfrost
+ * (Mali-G68) with temporal determinism.
+ *
+ * NOTE: This file is compiled only when CONFIG_DRM_PANFROST_REPLAY=y.
+ * It's for development/debug use only and is intended to be removed
+ * prior to any upstream/mainline submission.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/kvmalloc.h>
+#include <linux/unaligned.h>
+#include <linux/mutex.h>
+#include <linux/ktime.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+
+#include "../../../include/drm/panfrost_replay.h"
+
+#ifdef CONFIG_DRM_PANFROST_REPLAY
+
+/* --- Replay context definition --- */
+struct replay_context replay_ctx = {
+ .active = false,
+ .trace_data = NULL,
+ .trace_size = 0,
+ .current_pos = 0,
+ .base_timestamp_ns = 0,
+ .state = STATE_IDLE,
+};
+struct panfrost_device *g_pfdev = NULL;
+EXPORT_SYMBOL_GPL(g_pfdev);
+EXPORT_SYMBOL_GPL(replay_ctx);
+
+/* --- Helper: validate checksum (total_len includes trailing 4-byte
CRC LE) --- */
+int validate_checksum(const char *data, size_t total_len)
+{
+ u32 stored_crc, computed_crc;
+
+ if (!data || total_len < sizeof(u32))
+ return -EINVAL;
+
+ stored_crc = get_unaligned_le32((const void *)(data + total_len -
sizeof(u32)));
+ computed_crc = crc32_le(0, data, total_len - sizeof(u32));
+
+ if (stored_crc != computed_crc) {
+ if (g_pfdev && g_pfdev->dev)
+ dev_err(g_pfdev->dev, "REPLAY: Checksum mismatch!
Expected 0x%08x, Got 0x%08x\n",
+ stored_crc, computed_crc);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(validate_checksum);
+
+/* --- Simple textual parser for a single event line --- */
+/* Expected line forms (ASCII): W <addr_hex> <value_hex> <timestamp_ns> */
+static int replay_parse_next_event(u64 addr, u64 *out_val, u64 *out_timestamp)
+{
+ char *data = replay_ctx.trace_data;
+ size_t pos = replay_ctx.current_pos;
+ size_t n;
+ char *line, *nl;
+ char op;
+ unsigned long long trace_addr = 0, trace_val = 0, trace_ts = 0;
+ int ret = -ENOENT;
+
+ if (replay_ctx.state == STATE_ERROR)
+ return -EIO;
+
+ if (!data || pos >= replay_ctx.trace_size || replay_ctx.state !=
STATE_REPLAYING)
+ return -ENOENT;
+
+ line = data + pos;
+ nl = memchr(line, '\n', replay_ctx.trace_size - pos);
+ if (!nl)
+ return -ENOENT;
+
+ n = nl - line;
+ {
+ char *tmp = kmalloc(n + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+ memcpy(tmp, line, n);
+ tmp[n] = '\0';
+
+ if (sscanf(tmp, " %c %llx %llx %llu", &op, &trace_addr,
&trace_val, &trace_ts) >= 3) {
+ if ((u64)trace_addr == addr) {
+ *out_val = (u64)trace_val;
+ *out_timestamp = (u64)trace_ts;
+ ret = 0;
+ replay_ctx.current_pos = (nl - data) + 1;
+ } else {
+ replay_ctx.current_pos = (nl - data) + 1;
+ ret = -ENOENT;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ kfree(tmp);
+ }
+
+ return ret;
+}
+
+/* --- Main validation function called by driver --- */
+int mali_replay_validate(u32 current_val, u64 addr)
+{
+ int ret;
+ u64 expected_val = 0;
+ u64 expected_timestamp = 0;
+ u64 current_time_ns = ktime_get_ns();
+ s64 time_diff;
+ u64 expected_time_abs;
+
+ if (!g_pfdev || replay_ctx.state == STATE_IDLE)
+ return -EINVAL;
+
+ if (replay_ctx.state == STATE_ERROR) {
+ if (g_pfdev && g_pfdev->dev)
+ dev_err(g_pfdev->dev, "REPLAY: Called in ERROR state,
aborting.\n");
+ return -EIO;
+ }
+
+ mutex_lock(&replay_ctx.lock);
+
+ ret = replay_parse_next_event(addr, &expected_val, &expected_timestamp);
+ if (ret) {
+ dev_warn_ratelimited(g_pfdev ? g_pfdev->dev : NULL,
+ "REPLAY: No trace event found for addr
0x%llx at pos %zu (ret=%d)\n",
+ (unsigned long long)addr,
replay_ctx.current_pos, ret);
+ replay_ctx.state = STATE_ERROR;
+ mutex_unlock(&replay_ctx.lock);
+ return -ENOENT;
+ }
+
+ if ((u64)current_val != expected_val) {
+ if (g_pfdev && g_pfdev->dev)
+ dev_err(g_pfdev->dev,
+ "REPLAY: VALUE MISMATCH at addr=0x%llx
expected=0x%llx got=0x%x\n",
+ (unsigned long long)addr, (unsigned long
long)expected_val, current_val);
+ replay_ctx.state = STATE_ERROR;
+ mutex_unlock(&replay_ctx.lock);
+ return -EINVAL;
+ }
+
+ expected_time_abs = replay_ctx.base_timestamp_ns + expected_timestamp;
+ time_diff = (s64)(current_time_ns - expected_time_abs);
+
+ /* Timing tolerance thresholds (hard-coded for now, can be module_param) */
+ if (time_diff > 100000 || time_diff < -100000) {
+ if (g_pfdev && g_pfdev->dev)
+ dev_warn(g_pfdev->dev,
+ "REPLAY: TIMING MISMATCH addr=0x%llx
expected=%llu got=%llu diff=%lld ns\n",
+ (unsigned long long)addr,
+ (unsigned long long)expected_time_abs,
+ (unsigned long long)current_time_ns,
+ (long long)time_diff);
+ }
+ if (time_diff > 1000000 || time_diff < -1000000) {
+ if (g_pfdev && g_pfdev->dev)
+ dev_err(g_pfdev->dev,
+ "REPLAY: SEVERE TIMING MISMATCH addr=0x%llx
expected=%llu got=%llu diff=%lld ns\n",
+ (unsigned long long)addr,
+ (unsigned long long)expected_time_abs,
+ (unsigned long long)current_time_ns,
+ (long long)time_diff);
+ /* Optionally escalate to ERROR */
+ /* replay_ctx.state = STATE_ERROR; */
+ }
+
+ mutex_unlock(&replay_ctx.lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mali_replay_validate);
+
+/* --- DebugFS write: accept binary/text trace with trailing 4-byte
CRC (LE) --- */
+ssize_t replay_trace_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *new_data = NULL;
+ int ret = 0;
+
+ if (count < sizeof(u32) || count > MAX_TRACE_SIZE) {
+ if (g_pfdev && g_pfdev->dev)
+ dev_err(g_pfdev->dev, "REPLAY: Trace size invalid (%zu)\n", count);
+ return -EINVAL;
+ }
+
+ new_data = kvmalloc(count + 1, GFP_KERNEL);
+ if (!new_data)
+ return -ENOMEM;
+
+ if (copy_from_user(new_data, buf, count)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ new_data[count] = '\0';
+
+ ret = validate_checksum(new_data, count);
+ if (ret)
+ goto out_free;
+
+ mutex_lock(&replay_ctx.lock);
+ kfree(replay_ctx.trace_data);
+ replay_ctx.trace_data = new_data;
+ replay_ctx.trace_size = count;
+ replay_ctx.current_pos = 0;
+ replay_ctx.active = true;
+ replay_ctx.base_timestamp_ns = ktime_get_ns();
+ replay_ctx.state = STATE_REPLAYING;
+ mutex_unlock(&replay_ctx.lock);
+
+ if (g_pfdev && g_pfdev->dev)
+ dev_info(g_pfdev->dev, "REPLAY: Loaded trace file (%zu
bytes), mode activated\n", count);
+
+ return count;
+
+out_free:
+ kvfree(new_data);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(replay_trace_write);
+
+/* --- Init/fini helpers --- */
+int panfrost_replay_init(struct panfrost_device *pfdev)
+{
+ g_pfdev = pfdev;
+ mutex_init(&replay_ctx.lock);
+ replay_ctx.active = false;
+ replay_ctx.trace_data = NULL;
+ replay_ctx.trace_size = 0;
+ replay_ctx.current_pos = 0;
+ replay_ctx.base_timestamp_ns = 0;
+ replay_ctx.state = STATE_IDLE;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(panfrost_replay_init);
+
+void panfrost_replay_fini(void)
+{
+ kvfree(replay_ctx.trace_data);
+ replay_ctx.trace_data = NULL;
+ replay_ctx.active = false;
+ replay_ctx.state = STATE_IDLE;
+ mutex_destroy(&replay_ctx.lock);
+}
+EXPORT_SYMBOL_GPL(panfrost_replay_fini);
+
+#endif /* CONFIG_DRM_PANFROST_REPLAY */
diff --git a/drivers/gpu/drm/panfrost/tests/panfrost_replay_kunit.c
b/drivers/gpu/drm/panfrost/tests/panfrost_replay_kunit.c
new file mode 100644
index 0000000..abc1234
--- /dev/null
+++ b/drivers/gpu/drm/panfrost/tests/panfrost_replay_kunit.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/* KUnit tests for Panfrost replay (development-only) */
+
+#include <kunit/test.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/ktime.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include "../../../include/drm/panfrost_replay.h"
+
+static struct panfrost_device mock_pfdev;
+static char *mock_trace_data;
+
+static void reset_replay_ctx_for_test(void)
+{
+ panfrost_replay_fini();
+ panfrost_replay_init(&mock_pfdev);
+}
+
+static void setup_mock_trace(struct kunit *test, const char *trace_content)
+{
+ size_t len = strlen(trace_content) + 1;
+ reset_replay_ctx_for_test();
+
+ mock_trace_data = kmalloc(len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, mock_trace_data);
+ memcpy(mock_trace_data, trace_content, len);
+
+ mutex_lock(&replay_ctx.lock);
+ kfree(replay_ctx.trace_data);
+ replay_ctx.trace_data = mock_trace_data;
+ replay_ctx.trace_size = len;
+ replay_ctx.current_pos = 0;
+ replay_ctx.active = true;
+ replay_ctx.base_timestamp_ns = 0;
+ replay_ctx.state = STATE_REPLAYING;
+ mutex_unlock(&replay_ctx.lock);
+
+ g_pfdev = &mock_pfdev;
+}
+
+static void teardown_mock_trace(struct kunit *test)
+{
+ mutex_lock(&replay_ctx.lock);
+ kfree(replay_ctx.trace_data);
+ replay_ctx.trace_data = NULL;
+ replay_ctx.trace_size = 0;
+ replay_ctx.current_pos = 0;
+ replay_ctx.active = false;
+ replay_ctx.state = STATE_IDLE;
+ mutex_unlock(&replay_ctx.lock);
+
+ panfrost_replay_fini();
+ mock_trace_data = NULL;
+ g_pfdev = NULL;
+}
+
+static void test_replay_perfect_match(struct kunit *test)
+{
+ const char *trace =
+ "W 0x1234 0x5678 1000000\n"
+ "I 0x0 0x1 1000050\n";
+
+ setup_mock_trace(test, trace);
+
+ int ret = mali_replay_validate(0x5678, 0x1234);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ ret = mali_replay_validate(0x1, 0x0);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ teardown_mock_trace(test);
+}
+
+static void test_replay_value_mismatch(struct kunit *test)
+{
+ const char *trace =
+ "W 0x1234 0x5678 1000000\n";
+
+ setup_mock_trace(test, trace);
+
+ int ret = mali_replay_validate(0x9999, 0x1234);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+ KUNIT_EXPECT_EQ(test, replay_ctx.state, STATE_ERROR);
+
+ teardown_mock_trace(test);
+}
+
+static void test_replay_timing_mismatch(struct kunit *test)
+{
+ const char *trace =
+ "W 0x1234 0x5678 1000000\n";
+
+ setup_mock_trace(test, trace);
+ mutex_lock(&replay_ctx.lock);
+ replay_ctx.base_timestamp_ns = ktime_get_ns() + 2000000;
+ mutex_unlock(&replay_ctx.lock);
+
+ int ret = mali_replay_validate(0x5678, 0x1234);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ teardown_mock_trace(test);
+}
+
+static void test_replay_checksum_invalid(struct kunit *test)
+{
+ const char *payload = "W 0x1234 0x5678 1000000\n";
+ size_t payload_len = strlen(payload);
+ size_t total_len = payload_len + sizeof(u32);
+ char *data = kmalloc(total_len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, data);
+
+ memcpy(data, payload, payload_len);
+ u32 bogus = cpu_to_le32(0xDEADBEEF);
+ memcpy(data + payload_len, &bogus, sizeof(bogus));
+
+ int ret = validate_checksum(data, total_len);
+ KUNIT_EXPECT_LT(test, ret, 0);
+
+ kfree(data);
+}
+
+static void test_replay_size_limit(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, MAX_TRACE_SIZE > 0);
+ KUNIT_EXPECT_EQ(test, MAX_TRACE_SIZE, 512 * 1024 * 1024);
+}
+
+static struct kunit_case replay_test_cases[] = {
+ KUNIT_CASE(test_replay_perfect_match),
+ KUNIT_CASE(test_replay_value_mismatch),
+ KUNIT_CASE(test_replay_timing_mismatch),
+ KUNIT_CASE(test_replay_checksum_invalid),
+ KUNIT_CASE(test_replay_size_limit),
+ {}
+};
+
+static struct kunit_suite replay_test_suite = {
+ .name = "panfrost_replay_tests",
+ .test_cases = replay_test_cases,
+};
+kunit_test_suite(replay_test_suite);
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
new file mode 100644
index 0000000..def4567
--- /dev/null
+++ b/docs/DEVELOPMENT.md
@@ -0,0 +1,110 @@
+# راهنمای توسعه‌دهندگان: قابلیت Replay در درایور Mali-G68 (Panfrost)
+
+⚠️ توجه مهم: این قابلیت فقط برای توسعه‌دهندگان است و پس از تکمیل
درایور، قبل از ارسال به mainline حذف خواهد شد.
+
+## هدف
+این سند نحوهٔ تولید trace، بارگذاری آن روی دستگاه/QEMU، فعال‌سازی
حالت Replay، و عیب‌یابی نتایج را توصیف می‌کند.
+
+## فعال‌سازی در زمان کامپایل
+1. در سورس کرنل:
+```bash
+scripts/config --enable CONFIG_DEBUG_FS
+scripts/config --enable CONFIG_DRM_PANFROST
+scripts/config --enable CONFIG_DRM_PANFROST_REPLAY
+make olddefconfig
+```
+
+برای KUnit تست‌ها:
+```bash
+scripts/config --enable CONFIG_KUNIT
+scripts/config --enable CONFIG_DRM_PANFROST_REPLAY_TEST
+make olddefconfig
+```
+
+## تولید و انتقال trace
+از اسکریپت‌های موجود در `scripts/` یا `tools/` استفاده کنید.
+
+همیشه از `adb push` به `/data/local/tmp/` برای فایل‌های باینری
استفاده کنید و سپس `su -c 'cat ... > /sys/kernel/debug/...'` برای
نوشتن در debugfs (از echo برای باینری استفاده نکنید).
+
+## اجرای خودکار با اسکریپت validate_replay.py
+```bash
+./tools/validate_replay.py /path/to/trace_for_qemu.bin
+```
+
+این اسکریپت trace را push می‌کند، آن را در debugfs می‌نویسد،
replay_mode را فعال می‌کند، سناریو را اجرا می‌کند و لاگ‌ها را بررسی
می‌کند.
+
+## عیب‌یابی سریع
+اگر `/sys/kernel/debug/dri/0/replay_trace` وجود ندارد:
+```bash
+mount debugfs: adb shell su -c "mount -t debugfs none /sys/kernel/debug"
+```
+
+برای فعال کردن لاگ‌های اضافی:
+```bash
+adb shell "echo 'file panfrost_replay.c +p' >
/sys/kernel/debug/dynamic_debug/control"
+```
+
+## نکات ایمنی
+- این کد توسعه‌ای است — در کرنل‌های تولیدی فعال نشود.
+- traceها ممکن است اطلاعات حساس داشته باشند؛ قبل از اشتراک‌گذاری فیلتر کنید.
+
+## نکات اضافی
+- پس از هر بار بارگذاری trace جدید، وضعیت replay ریست می‌شود.
+- در صورت بروز خطای VALUE MISMATCH، وضعیت به STATE_ERROR می‌رود و
نیاز به بارگذاری مجدد trace دارد.
+- برای غیرفعال کردن حالت replay: `echo 0 >
/sys/module/panfrost/parameters/replay_mode`
+
+## Temporal Determinism
+در حالت Replay، زمان و وقفه‌ها دقیقاً از فایل trace خوانده می‌شوند.
هر گونه اختلاف (آدرس، مقدار، زمان) به‌عنوان `mismatch` ثبت شده و گزارش
می‌شود.
+
+### تنظیم آستانه‌ی تحمل زمان (Timing Tolerance)
+آستانه‌های تحمل زمان به‌صورت hard-coded در `panfrost_replay.c` تنظیم
شده‌اند. برای تغییر، می‌توانید از `module_param` استفاده کنید.
+
+## تست روی شبیه‌ساز QEMU
+قبل از اجرا روی گوشی، می‌توانید تست‌ها را روی QEMU اجرا کنید:
+```bash
+./build/qemu-system-arm -M vexpress-a15 -kernel ./kernel.elf -replay
trace.bin -d trace:mem_ops -D qemu_output.log
+```
+
+## ساختار پروژه
+پروژه شامل ۲۳ فایل در ۴ بخش اصلی است:
+- **بخش اول (۱-۶):** زیرساخت و ابزارهای اولیه (نقش‌ها، پایگاه داده،
قالب گزارش، CI، QEMU، اسکریپت ضبط trace)
+- **بخش دوم (۷-۱۵):** درایور کرنل (پچ‌ها، کد اصلی، Makefile، کامپایل،
راهنماهای اولیه)
+- **بخش سوم (۱۶-۲۰):** مستندات کامل (راهنمای گام‌به‌گام، تنظیمات
تکمیلی، README، DEVELOPMENT)
+- **بخش چهارم (۲۱-۲۳):** ویژگی‌های پیشرفته (Checksum، ماشین حالت، تست
خودکار، تست‌های KUnit)
+
+## جمع‌بندی
+این قابلیت یک ابزار قدرتمند برای توسعه‌دهندگان است، اما هرگز نباید در
کرنل‌های تولیدی بدون بررسی کامل استفاده شود.
+پس از اتمام توسعه و اطمینان از صحت درایور، همه‌ی کدهای مربوط به
CONFIG_DRM_PANFROST_REPLAY (فایل‌های panfrost_replay.c،
panfrost_replay.h و تمام #ifdefهای مرتبط) به‌کلی از مخزن کد حذف خواهند
شد تا کرنل نهایی کاملاً سبک و پایدار باشد.
diff --git a/docs/REPLAY_WARNING.md b/docs/REPLAY_WARNING.md
new file mode 100644
index 0000000..7890123
--- /dev/null
+++ b/docs/REPLAY_WARNING.md
@@ -0,0 +1,32 @@
+# ⚠️ Replay (Development-only) — Warning
+
+This driver includes a Replay / Validation Mode for development and
debugging only.
+
+- Default: `CONFIG_DRM_PANFROST_REPLAY=n`
+- Intended strictly for debugging; do NOT enable in production images.
+- Will be removed before upstream/mainline submission.
+
+## How to enable (developers):
+1. Set `CONFIG_DRM_PANFROST_REPLAY=y` in your defconfig.
+2. Read `docs/DEVELOPMENT.md` for usage and caveats.
+
+## Risks
+- **Stability:** May cause GPU hangs or system instability.
+- **Security:** Trace files may contain sensitive register values;
handle with care.
+- **Performance:** Not optimized; adds overhead to driver operations.
+
+## Mitigation
+- Only enable on test/development systems with non-critical data.
+- Always monitor kernel logs for warnings/errors.
+- Remove all replay-related code before any release or upstream submission.
+
+## Removal Plan
+This code is marked with `WILL_BE_REMOVED` comments and is guarded by
`CONFIG_DRM_PANFROST_REPLAY`. It will be removed entirely before the
driver is submitted to mainline Linux.
+
+## Temporal Determinism
+The replay engine uses a **temporal deterministic** model: time and
interrupts are read exactly from the trace file. Any deviation
(address, value, or timing) is recorded as a mismatch.
+
+## CI/CD Integration
+A CI pipeline template is provided
(`.github/workflows/04_CI_Pipeline_v5.yaml`) for automated testing.
+
+## Summary
+**YOU HAVE BEEN WARNED.** This feature is temporary and
development-only. Use at your own risk.
diff --git a/include/drm/panfrost_replay.h b/include/drm/panfrost_replay.h
new file mode 100644
index 0000000..fedcba9
--- /dev/null
+++ b/include/drm/panfrost_replay.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _DRM_PANFROST_REPLAY_H
+#define _DRM_PANFROST_REPLAY_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_DRM_PANFROST_REPLAY
+
+struct panfrost_device;
+
+/* replay state visible to tests */
+enum replay_state {
+ STATE_IDLE = 0,
+ STATE_REPLAYING,
+ STATE_ERROR,
+};
+
+/* replay context (partial view for tests and KUnit) */
+struct replay_context {
+ bool active;
+ char *trace_data;
+ size_t trace_size;
+ size_t current_pos;
+ u64 base_timestamp_ns;
+ enum replay_state state;
+ struct mutex lock;
+};
+
+/* maximum allowed trace size (bytes) */
+#define MAX_TRACE_SIZE (512 * 1024 * 1024)
+
+/* Public symbols (exported when CONFIG_DRM_PANFROST_REPLAY or CONFIG_KUNIT) */
+extern struct panfrost_device *g_pfdev;
+extern struct replay_context replay_ctx;
+
+/* checksum/validation API */
+int validate_checksum(const char *data, size_t total_len);
+
+/* main validation entry used by driver code */
+int mali_replay_validate(u32 current_val, u64 addr);
+
+/* init / fini helpers */
+int panfrost_replay_init(struct panfrost_device *pfdev);
+void panfrost_replay_fini(void);
+
+#endif /* CONFIG_DRM_PANFROST_REPLAY */
+
+/* Ensure tests can link against these symbols when building KUnit */
+#ifdef CONFIG_KUNIT
+int validate_checksum(const char *data, size_t total_len);
+int mali_replay_validate(u32 current_val, u64 addr);
+int panfrost_replay_init(struct panfrost_device *pfdev);
+void panfrost_replay_fini(void);
+extern struct replay_context replay_ctx;
+extern struct panfrost_device *g_pfdev;
+#endif /* CONFIG_KUNIT */
+
+#endif /* _DRM_PANFROST_REPLAY_H */
diff --git a/scripts/06_Trace_Record_With_Time_v5.sh
b/scripts/06_Trace_Record_With_Time_v5.sh
new file mode 100755
index 0000000..1234567
--- /dev/null
+++ b/scripts/06_Trace_Record_With_Time_v5.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+DEVICE_SERIAL=$1
+SCENARIO=$2
+OUTPUT_DIR="./traces/$(date +%Y%m%d_%H%M%S)"
+mkdir -p $OUTPUT_DIR
+adb -s $DEVICE_SERIAL shell "echo 1 > /sys/kernel/debug/tracing/tracing_on"
+adb -s $DEVICE_SERIAL shell "echo 'mali:*' >
/sys/kernel/debug/tracing/set_event"
+adb -s $DEVICE_SERIAL shell "echo 'irq:*' >>
/sys/kernel/debug/tracing/set_event"
+adb -s $DEVICE_SERIAL shell "echo 'nsecs' >
/sys/kernel/debug/tracing/trace_options"
+adb -s $DEVICE_SERIAL shell "trace-cmd record -e mali* -e irq* -o
/sdcard/trace.dat"
+adb -s $DEVICE_SERIAL shell "$SCENARIO"
+adb -s $DEVICE_SERIAL shell "trace-cmd stop"
+adb -s $DEVICE_SERIAL pull /sdcard/trace.dat $OUTPUT_DIR/trace_raw.dat
+trace-cmd report -i $OUTPUT_DIR/trace_raw.dat > $OUTPUT_DIR/trace_human.txt
+python3 << EOF > $OUTPUT_DIR/trace_for_qemu.bin
+import re
+with open("$OUTPUT_DIR/trace_human.txt", "r") as f:
+ for line in f:
+ match =
re.search(r'(\d+\.\d+):.*(mmio_write|mmio_read|irq).*addr=(\w+)
val=(\w+)', line)
+ if match:
+ ts = int(float(match.group(1)) * 1e9)
+ typ = 'W' if 'write' in match.group(2) else 'R'
+ addr = int(match.group(3), 16)
+ val = int(match.group(4), 16)
+ if 'irq' in match.group(2):
+ print(f"I 0x0 0x1 {ts}")
+ else:
+ print(f"{typ} 0x{addr:x} 0x{val:x} {ts}")
+EOF
+echo "Trace saved to $OUTPUT_DIR/trace_for_qemu.bin"
diff --git a/scripts/09_build_kernel_with_replay.sh
b/scripts/09_build_kernel_with_replay.sh
new file mode 100755
index 0000000..2345678
--- /dev/null
+++ b/scripts/09_build_kernel_with_replay.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+export ARCH=arm64
+export CROSS_COMPILE=aarch64-linux-android-
+export KERNEL_DIR=~/android_kernel
+cd $KERNEL_DIR
+patch -p1 < ~/mali_project/07_panfrost_replay_patch.patch
+make defconfig
+echo "CONFIG_DRM_PANFROST=y" >> .config
+echo "CONFIG_DRM_PANFROST_REPLAY=y" >> .config
+make olddefconfig
+make -j$(nproc) Image.gz dtbs modules
+mkbootimg --kernel arch/arm64/boot/Image.gz --ramdisk ramdisk.img
--output boot_replay.img
+echo "فایل boot_replay.img ساخته شد."
diff --git a/scripts/14_build_kernel_with_replay_updated.sh
b/scripts/14_build_kernel_with_replay_updated.sh
new file mode 100755
index 0000000..3456789
--- /dev/null
+++ b/scripts/14_build_kernel_with_replay_updated.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+export ARCH=arm64
+export CROSS_COMPILE=aarch64-linux-android-
+export KERNEL_DIR=~/android_kernel
+cd $KERNEL_DIR
+
+patch -p1 < ~/mali_project/07_panfrost_replay_patch.patch
+
+DEFCONFIG="vendor/sm8350_defconfig"
+make $DEFCONFIG
+
+scripts/config --enable CONFIG_DEBUG_FS
+scripts/config --enable CONFIG_DRM_PANFROST
+scripts/config --enable CONFIG_DRM_PANFROST_REPLAY
+make olddefconfig
+
+make -j$(nproc) Image.gz dtbs modules
+
+mkbootimg --kernel arch/arm64/boot/Image.gz \
+ --base 0x00000000 \
+ --kernel_offset 0x00008000 \
+ --ramdisk_offset 0x01000000 \
+ --tags_offset 0x00000100 \
+ --pagesize 2048 \
+ --cmdline "console=ttyMSM0,115200n8 androidboot.hardware=qcom ..." \
+ --output boot_replay.img
+
+echo "✅ ساخت boot_replay.img با موفقیت انجام شد."
+echo "برای تست ایمن: fastboot boot boot_replay.img"
diff --git a/scripts/22_Automated_Validation.py
b/scripts/22_Automated_Validation.py
new file mode 100755
index 0000000..4567890
--- /dev/null
+++ b/scripts/22_Automated_Validation.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+فایل 22: Automated Validation Script for Mali-G68 Replay Driver
+این اسکریپت، فرآیند تست خودکار را برای اعتبارسنجی درایور Panfrost در
حالت Replay انجام می‌دهد.
+"""
+
+import subprocess
+import time
+import sys
+import os
+import json
+from datetime import datetime
+
+# ========== تنظیمات ==========
+DEVICE_SERIAL = None
+TRACE_FILE = "./trace_for_qemu.bin"
+REPLAY_MODE_PATH = "/sys/module/panfrost/parameters/replay_mode"
+REPLAY_TRACE_PATH = "/sys/kernel/debug/dri/0/replay_trace"
+TEST_COMMAND = "am start -n
com.android.chrome/com.google.android.apps.chrome.Main"
+MAX_MISMATCH_ALLOWED = 0
+MAX_TIMING_DIFF_NS = 500000
+
+# ========== توابع کمکی ==========
+def run_adb(cmd, check=True):
+ full_cmd = ["adb"]
+ if DEVICE_SERIAL:
+ full_cmd.extend(["-s", DEVICE_SERIAL])
+ full_cmd.extend(cmd.split())
+ try:
+ result = subprocess.run(full_cmd, capture_output=True,
text=True, check=check)
+ return result.stdout.strip(), result.stderr.strip()
+ except subprocess.CalledProcessError as e:
+ print(f"[ERROR] adb command failed: {cmd}")
+ print(f"stderr: {e.stderr}")
+ return "", e.stderr
+
+def adb_shell(cmd):
+ return run_adb(f"shell {cmd}")
+
+def write_to_device(path, content):
+ cmd = f"echo '{content}' > {path}"
+ return adb_shell(cmd)
+
+def push_file(local_path, remote_path="/sdcard/"):
+ cmd = f"push {local_path} {remote_path}"
+ return run_adb(cmd)
+
+def read_dmesg(filter_str=None):
+ cmd = "shell dmesg"
+ out, _ = run_adb(cmd)
+ if filter_str:
+ lines = [line for line in out.split('\n') if filter_str in line]
+ return '\n'.join(lines)
+ return out
+
+def check_replay_logs():
+ logs = read_dmesg("REPLAY")
+ if "REPLAY VALUE MISMATCH" in logs:
+ return "VALUE_MISMATCH"
+ if "REPLAY TIMING MISMATCH" in logs:
+ return "TIMING_MISMATCH"
+ if "REPLAY ERROR" in logs or "REPLAY: Error" in logs:
+ return "ERROR"
+ if "REPLAY: Job validation passed" in logs:
+ return "PASSED"
+ return "UNKNOWN"
+
+# ========== مراحل تست ==========
+def test_replay():
+ print("[INFO] Starting automated Replay test...")
+ print(f"[INFO] Trace file: {TRACE_FILE}")
+ print(f"[INFO] Device: {DEVICE_SERIAL if DEVICE_SERIAL else 'default'}")
+
+ print("[STEP 1] Pushing trace file to device...")
+ push_file(TRACE_FILE, "/sdcard/trace.bin")
+
+ print("[STEP 2] Enabling Replay mode...")
+ write_to_device(REPLAY_MODE_PATH, "1")
+
+ print("[STEP 3] Loading trace into kernel...")
+ adb_shell(f"cat /sdcard/trace.bin > {REPLAY_TRACE_PATH}")
+
+ print(f"[STEP 4] Running test scenario: {TEST_COMMAND}")
+ adb_shell(TEST_COMMAND)
+
+ print("[STEP 5] Waiting for Replay to finish...")
+ time.sleep(5)
+
+ print("[STEP 6] Checking Replay logs...")
+ result = check_replay_logs()
+
+ print("\n========== RESULT ==========")
+ if result == "PASSED":
+ print("✅ TEST PASSED: All validations were successful.")
+ status = "PASS"
+ else:
+ print(f"❌ TEST FAILED: {result}")
+ status = "FAIL"
+
+ print("\n[DEBUG] Last 20 lines of Replay logs:")
+ logs = read_dmesg("REPLAY")
+ for line in logs.split('\n')[-20:]:
+ print(f" {line}")
+
+ print("\n[STEP 7] Disabling Replay mode...")
+ write_to_device(REPLAY_MODE_PATH, "0")
+
+ return status
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ TRACE_FILE = sys.argv[1]
+ if len(sys.argv) > 2:
+ DEVICE_SERIAL = sys.argv[2]
+
+ if not os.path.exists(TRACE_FILE):
+ print(f"[ERROR] Trace file not found: {TRACE_FILE}")
+ sys.exit(1)
+
+ report = {
+ "timestamp": datetime.now().isoformat(),
+ "device": DEVICE_SERIAL,
+ "trace_file": TRACE_FILE,
+ "test_command": TEST_COMMAND,
+ "result": test_replay(),
+ "details": {}
+ }
+
+ with open("test_report.json", "w") as f:
+ json.dump(report, f, indent=2)
+
+ print(f"\n[INFO] Report saved to test_report.json")
+ sys.exit(0 if report["result"] == "PASS" else 1)
diff --git a/tools/validate_replay.py b/tools/validate_replay.py
new file mode 100755
index 0000000..9876543
--- /dev/null
+++ b/tools/validate_replay.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+"""
+Automated validation helper for panfrost replay (safe version).
+Usage: ./tools/validate_replay.py trace_for_qemu.bin [device_serial]
+"""
+
+import subprocess, time, sys, os, json
+from datetime import datetime
+
+# Config
+TRACE_FILE = "./trace_for_qemu.bin"
+REMOTE_TMP = "/data/local/tmp/trace_for_qemu.bin"
+REPLAY_MODE_PATH = "/sys/module/panfrost/parameters/replay_mode"
+REPLAY_TRACE_PATH = "/sys/kernel/debug/dri/0/replay_trace"
+TEST_COMMAND = "am start -n
com.android.chrome/com.google.android.apps.chrome.Main"
+WAIT_TIMEOUT_SEC = 30
+POLL_INTERVAL_SEC = 1
+DEVICE_SERIAL = None
+
+def run_adb_args(args):
+ cmd = ["adb"]
+ if DEVICE_SERIAL:
+ cmd += ["-s", DEVICE_SERIAL]
+ cmd += args
+ r = subprocess.run(cmd, capture_output=True, text=True)
+ return r.returncode, r.stdout.strip(), r.stderr.strip()
+
+def run_adb_shell(cmd):
+ return run_adb_args(["shell", cmd])
+
+def adb_push(local, remote):
+ code, out, err = run_adb_args(["push", local, remote])
+ if code != 0:
+ print(f"[ERROR] adb push failed: {err or out}")
+ return False
+ print(f"[INFO] pushed {local} -> {remote}")
+ return True
+
+def set_replay_mode(v):
+ code, out, err = run_adb_shell(f"su -c 'echo {v} > {REPLAY_MODE_PATH}'")
+ if code != 0:
+ print(f"[WARN] set_replay_mode failed: {err} {out}")
+ return False
+ return True
+
+def write_trace(remote_tmp):
+ code, out, err = run_adb_shell(f"su -c 'cat {remote_tmp} >
{REPLAY_TRACE_PATH}'")
+ if code != 0:
+ print(f"[ERROR] write_trace failed: {err} {out}")
+ return False
+ return True
+
+def read_dmesg_filtered(keys):
+ code, out, err = run_adb_shell("su -c 'dmesg'")
+ if code != 0:
+ return ""
+ lines = []
+ for l in out.splitlines():
+ if any(k in l for k in keys):
+ lines.append(l)
+ return "\n".join(lines)
+
+def main():
+ global TRACE_FILE, DEVICE_SERIAL
+ if len(sys.argv) > 1:
+ TRACE_FILE = sys.argv[1]
+ if len(sys.argv) > 2:
+ DEVICE_SERIAL = sys.argv[2]
+
+ if not os.path.exists(TRACE_FILE):
+ print("[ERROR] Trace not found")
+ sys.exit(2)
+
+ if not adb_push(TRACE_FILE, REMOTE_TMP):
+ sys.exit(3)
+
+ if not set_replay_mode(1):
+ print("[WARN] could not enable replay mode")
+
+ if not write_trace(REMOTE_TMP):
+ print("[ERROR] writing trace into debugfs failed")
+ set_replay_mode(0)
+ sys.exit(4)
+
+ print("[INFO] Running test command...")
+ run_adb_shell(TEST_COMMAND)
+
+ print(f"[INFO] Waiting up to {WAIT_TIMEOUT_SEC}s for replay result...")
+ start = time.time()
+ final = "UNKNOWN"
+ while time.time() - start < WAIT_TIMEOUT_SEC:
+ logs = read_dmesg_filtered(["REPLAY", "panfrost"])
+ if "Job validation passed" in logs or "validation passed" in logs:
+ final = "PASS"
+ break
+ if "VALUE MISMATCH" in logs:
+ final = "VALUE_MISMATCH"
+ break
+ if "TIMING MISMATCH" in logs:
+ final = "TIMING_MISMATCH"
+ break
+ time.sleep(POLL_INTERVAL_SEC)
+
+ print("Result:", final)
+ set_replay_mode(0)
+ run_adb_args(["shell", "su -c", f"rm -f {REMOTE_TMP}"])
+ sys.exit(0 if final == "PASS" else 1)
+
+if __name__ == "__main__":
+ main()
diff --git a/.github/workflows/04_CI_Pipeline_v5.yaml
b/.github/workflows/04_CI_Pipeline_v5.yaml
new file mode 100644
index 0000000..abc1234
--- /dev/null
+++ b/.github/workflows/04_CI_Pipeline_v5.yaml
@@ -0,0 +1,29 @@
+name: CI Pipeline (Temporal Replay)
+
+on:
+ push: { branches: [ main ] }
+ schedule: [ { cron: '0 2 * * *' } ]
+
+jobs:
+ temporal-replay:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Build QEMU with deterministic clock
+ run: |
+ mkdir build && cd build
+ ../configure --target-list=arm-softmmu --enable-debug --enable-replay
+ make -j$(nproc)
+ - name: Download reference trace
+ run: wget http://db/traces/golden_trace.bin -O trace.bin
+ - name: Run deterministic replay
+ run: |
+ ./build/qemu-system-arm -M vexpress-a15 -kernel
./kernel.elf -replay trace.bin -d trace:mem_ops -D qemu_output.log
+ - name: Validate temporal match
+ run: |
+ python scripts/validate_timing.py --trace trace.bin
--qemu-log qemu_output.log
+ - name: Upload results
+ uses: actions/upload-artifact@v3
+ with:
+ name: timing-report
+ path: timing_report.json