[PATCH 4/5] drm/imagination: Limit access to the META firmware interface

From: Alexandru Dadu

Date: Wed Oct 28 2026 - 09:32:01 EST


From: Alessio Belle <alessio.belle@xxxxxxxxxx>

Ensure all accesses to the META firmware interface are done via the
generic firmware interface.

To achieve this:
- Introduce a new function to the firmware processor table to remove
the last direct access to the META firmware interface from
outside of the generic firmware logic.
- Remove the META firmware interface header.
- Rename the META firmware processor header adding an "internal" suffix.
- Add guard to the META firmware processor header to prevent accidental
inclusion outside of the META firmware specific logic.
- Add a multi_dm_thread_assoc property to struct pvr_fw_defs to isolate
that behaviour from the architecture.

Signed-off-by: Alessio Belle <alessio.belle@xxxxxxxxxx>
Signed-off-by: Alexandru Dadu <alexandru.dadu@xxxxxxxxxx>
---
drivers/gpu/drm/imagination/pvr_fw.h | 26 +++++++++
drivers/gpu/drm/imagination/pvr_fw_meta.c | 28 ++++++++--
drivers/gpu/drm/imagination/pvr_fw_meta.h | 14 -----
drivers/gpu/drm/imagination/pvr_fw_mips.c | 1 +
drivers/gpu/drm/imagination/pvr_fw_riscv.c | 1 +
drivers/gpu/drm/imagination/pvr_fw_startstop.c | 61 ++++++++++++----------
drivers/gpu/drm/imagination/pvr_gem.h | 1 -
...{pvr_rogue_meta.h => pvr_rogue_meta_internal.h} | 10 ++--
8 files changed, 93 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/imagination/pvr_fw.h b/drivers/gpu/drm/imagination/pvr_fw.h
index 4b25291135b6..610406668bbb 100644
--- a/drivers/gpu/drm/imagination/pvr_fw.h
+++ b/drivers/gpu/drm/imagination/pvr_fw.h
@@ -184,6 +184,23 @@ struct pvr_fw_defs {
*/
void (*irq_clear)(struct pvr_device *pvr_dev);

+ /**
+ * @must_wait_fw_idle: Check if the FW stop sequence should wait for hardware
+ * to report FW idle before proceeding.
+ *
+ * @pvr_dev: Target PowerVR device.
+ * @wait[out]: Whether the FW stop sequence should wait for FW idle.
+ *
+ * This function is optional. The assumption is that we must always wait
+ * for the firmware to reach idle; implement this function and return
+ * %false to alter this behavior.
+ *
+ * Returns:
+ * * 0 on success.
+ * * Any appropriate error on failure.
+ */
+ int (*must_wait_fw_idle)(struct pvr_device *pvr_dev, bool *wait);
+
/**
* @has_fixed_data_addr: Specify whether the firmware fixed data must be loaded at the
* address given by the firmware layout table.
@@ -191,6 +208,15 @@ struct pvr_fw_defs {
* This value is mandatory.
*/
bool has_fixed_data_addr;
+
+ /**
+ * @multi_dm_thread_assoc: Specify that all threads must be associated
+ * with the DM separately rather than just the
+ * first thread (the default behaviour).
+ *
+ * This value is mandatory.
+ */
+ bool multi_dm_thread_assoc;
};

/**
diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.c b/drivers/gpu/drm/imagination/pvr_fw_meta.c
index 84f568b5510f..f451f97a4f2f 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_meta.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_meta.c
@@ -4,10 +4,11 @@
#include "pvr_device.h"
#include "pvr_fw.h"
#include "pvr_fw_info.h"
-#include "pvr_fw_meta.h"
#include "pvr_gem.h"
#include "pvr_rogue_cr_defs.h"
-#include "pvr_rogue_meta.h"
+#define PVR_ROGUE_META_INTERNAL_H_EXPLICIT_INCLUDE
+#include "pvr_rogue_meta_internal.h"
+#undef PVR_ROGUE_META_INTERNAL_H_EXPLICIT_INCLUDE
#include "pvr_vm.h"

#include <linux/compiler.h>
@@ -32,7 +33,7 @@
* * 0 on success, or
* * Any error returned by pvr_cr_poll_reg32().
*/
-int
+static int
pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out)
{
int err;
@@ -563,6 +564,25 @@ pvr_meta_irq_clear(struct pvr_device *pvr_dev)
ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_CLRMSK);
}

+static int
+pvr_meta_must_wait_fw_idle(struct pvr_device *pvr_dev, bool *wait)
+{
+ u32 reg_value;
+ int err;
+
+ err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, &reg_value);
+ if (err)
+ return err;
+
+ /*
+ * Wait for Sidekick/Jones to signal IDLE including the Garten
+ * Wrapper if there is no debugger attached (TxVECINT_BHALT = 0x0).
+ */
+ *wait = !reg_value;
+
+ return 0;
+}
+
const struct pvr_fw_defs pvr_fw_defs_meta = {
.init = pvr_meta_init,
.fini = pvr_meta_fini,
@@ -573,5 +593,7 @@ const struct pvr_fw_defs pvr_fw_defs_meta = {
.wrapper_init = pvr_meta_wrapper_init,
.irq_pending = pvr_meta_irq_pending,
.irq_clear = pvr_meta_irq_clear,
+ .must_wait_fw_idle = pvr_meta_must_wait_fw_idle,
.has_fixed_data_addr = false,
+ .multi_dm_thread_assoc = true,
};
diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.h b/drivers/gpu/drm/imagination/pvr_fw_meta.h
deleted file mode 100644
index 911ad700cba6..000000000000
--- a/drivers/gpu/drm/imagination/pvr_fw_meta.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
-/* Copyright (c) 2023 Imagination Technologies Ltd. */
-
-#ifndef PVR_FW_META_H
-#define PVR_FW_META_H
-
-#include <linux/types.h>
-
-/* Forward declaration from pvr_device.h */
-struct pvr_device;
-
-int pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out);
-
-#endif /* PVR_FW_META_H */
diff --git a/drivers/gpu/drm/imagination/pvr_fw_mips.c b/drivers/gpu/drm/imagination/pvr_fw_mips.c
index 6914fc46db50..ec0dad1692c3 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_mips.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_mips.c
@@ -196,4 +196,5 @@ const struct pvr_fw_defs pvr_fw_defs_mips = {
.irq_pending = pvr_mips_irq_pending,
.irq_clear = pvr_mips_irq_clear,
.has_fixed_data_addr = true,
+ .multi_dm_thread_assoc = false,
};
diff --git a/drivers/gpu/drm/imagination/pvr_fw_riscv.c b/drivers/gpu/drm/imagination/pvr_fw_riscv.c
index 2b24c40bc20f..ef2801276252 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_riscv.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_riscv.c
@@ -182,4 +182,5 @@ const struct pvr_fw_defs pvr_fw_defs_riscv = {
.irq_pending = pvr_riscv_irq_pending,
.irq_clear = pvr_riscv_irq_clear,
.has_fixed_data_addr = false,
+ .multi_dm_thread_assoc = false,
};
diff --git a/drivers/gpu/drm/imagination/pvr_fw_startstop.c b/drivers/gpu/drm/imagination/pvr_fw_startstop.c
index 98ca2692239d..9a617f78256e 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_startstop.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_startstop.c
@@ -3,10 +3,8 @@

#include "pvr_device.h"
#include "pvr_fw.h"
-#include "pvr_fw_meta.h"
#include "pvr_fw_startstop.h"
#include "pvr_rogue_cr_defs.h"
-#include "pvr_rogue_meta.h"
#include "pvr_vm.h"

#include <linux/compiler.h>
@@ -199,6 +197,30 @@ pvr_fw_start(struct pvr_device *pvr_dev)
return err;
}

+static inline bool layout_is_mars(struct pvr_device *pvr_dev)
+{
+ u64 layout_mars_value = 0;
+
+ if (PVR_FEATURE_VALUE(pvr_dev, layout_mars, &layout_mars_value))
+ return layout_mars_value > 0;
+
+ return false;
+}
+
+static inline int must_wait_fw_idle_before_stop(struct pvr_device *pvr_dev)
+{
+ bool wait_fw_idle = false;
+ int err;
+
+ if (pvr_dev->fw_dev.defs->must_wait_fw_idle) {
+ err = pvr_dev->fw_dev.defs->must_wait_fw_idle(pvr_dev, &wait_fw_idle);
+ if (err)
+ return err;
+ }
+
+ return wait_fw_idle;
+}
+
/**
* pvr_fw_stop() - Stop FW processor
* @pvr_dev: Target PowerVR device.
@@ -214,15 +236,9 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
~(ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN |
ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN |
ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN);
- bool skip_garten_idle = false;
- u64 layout_mars_value = 0;
- bool layout_mars = false;
- bool meta_fw = pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_META;
- u32 reg_value;
- int err;

- if (PVR_FEATURE_VALUE(pvr_dev, layout_mars, &layout_mars_value) == 0)
- layout_mars = layout_mars_value > 0;
+ bool layout_mars = layout_is_mars(pvr_dev);
+ int err;

/*
* For cores with the LAYOUT_MARS feature, SIDEKICK and SLC would have been
@@ -250,7 +266,7 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL &
ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);

- if (meta_fw) {
+ if (pvr_dev->fw_dev.defs->multi_dm_thread_assoc) {
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC,
ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL &
ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
@@ -297,6 +313,8 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
* powered down by the FW.
*/
if (!layout_mars) {
+ int wait_fw_idle;
+
/* Wait for SLC to signal IDLE. */
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SLC_IDLE,
ROGUE_CR_SLC_IDLE_MASKFULL,
@@ -309,24 +327,12 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
sidekick_idle_mask, POLL_TIMEOUT_USEC);
if (err)
return err;
- }

- if (meta_fw) {
- err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, &reg_value);
- if (err)
- return err;
-
- /*
- * Wait for Sidekick/Jones to signal IDLE including the Garten
- * Wrapper if there is no debugger attached (TxVECINT_BHALT =
- * 0x0).
- */
- if (reg_value)
- skip_garten_idle = true;
- }
+ wait_fw_idle = must_wait_fw_idle_before_stop(pvr_dev);
+ if (wait_fw_idle < 0)
+ return wait_fw_idle;

- if (meta_fw || !layout_mars) {
- if (!skip_garten_idle) {
+ if (wait_fw_idle) {
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE,
ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
@@ -346,7 +352,6 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
ROGUE_CR_MARS_IDLE_CPU_EN |
ROGUE_CR_MARS_IDLE_MH_SYSARB0_EN,
POLL_TIMEOUT_USEC);
-
if (err)
return err;
}
diff --git a/drivers/gpu/drm/imagination/pvr_gem.h b/drivers/gpu/drm/imagination/pvr_gem.h
index ae7af82b6f17..e84be25f8576 100644
--- a/drivers/gpu/drm/imagination/pvr_gem.h
+++ b/drivers/gpu/drm/imagination/pvr_gem.h
@@ -5,7 +5,6 @@
#define PVR_GEM_H

#include "pvr_heap_config.h"
-#include "pvr_rogue_meta.h"

#include <uapi/drm/pvr_drm.h>

diff --git a/drivers/gpu/drm/imagination/pvr_rogue_meta.h b/drivers/gpu/drm/imagination/pvr_rogue_meta_internal.h
similarity index 98%
rename from drivers/gpu/drm/imagination/pvr_rogue_meta.h
rename to drivers/gpu/drm/imagination/pvr_rogue_meta_internal.h
index 3020e6582daa..dd978988ed40 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_meta.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_meta_internal.h
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */

-#ifndef PVR_ROGUE_META_H
-#define PVR_ROGUE_META_H
+#ifndef PVR_ROGUE_META_INTERNAL_H
+#define PVR_ROGUE_META_INTERNAL_H
+
+#ifndef PVR_ROGUE_META_INTERNAL_H_EXPLICIT_INCLUDE
+#error This file may only be included if explicitly defined
+#endif

/***** The META HW register definitions in the file are updated manually *****/

@@ -353,4 +357,4 @@ struct rogue_meta_ldr_cfg_blk {

#define ROGUE_FW_PROCESSOR_META "META"

-#endif /* PVR_ROGUE_META_H */
+#endif /* PVR_ROGUE_META_INTERNAL_H */

--
2.43.0