[PATCH v2 06/13] media: iris: Enable Secure PAS support with IOMMU managed by Linux

Vishnu Reddy
2026-04-23T13:30:53Z

From: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>

Most Qualcomm platforms feature a proprietary hypervisor (such as Gunyah
or QHEE), which typically handles IOMMU configuration. This includes
mapping memory regions and device memory resources for remote processors
by intercepting qcom_scm_pas_auth_and_reset() calls. These mappings are
later removed during teardown. Additionally, SHM bridge setup is required
to enable memory protection for both remoteproc metadata and its memory
regions.

When the hypervisor is absent, the operating system must perform these
configurations instead.

Support for handling IOMMU and SHM setup in the absence of a hypervisor
is now in place. Extend the Iris driver to enable this functionality on
platforms where IOMMU is managed by Linux (i.e., non-Gunyah, non-QHEE).

Additionally, the Iris driver must map the firmware and its required
resources to the firmware SID, which is now specified via iommu-map in
the device tree.

Co-developed-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
---
drivers/media/platform/qcom/iris/iris_core.h | 4 ++
drivers/media/platform/qcom/iris/iris_firmware.c | 72 ++++++++++++++++++++----
2 files changed, 66 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h
index fb194c967ad4..b396c8cf595e 100644
--- a/drivers/media/platform/qcom/iris/iris_core.h
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -34,6 +34,8 @@ enum domain_type {
* struct iris_core - holds core parameters valid for all instances
*
* @dev: reference to device structure
+ * @fw_dev: reference to the context bank device used for firmware load
+ * @pas_ctx: SCM PAS context for authenticated firmware load and shutdown
* @reg_base: IO memory base address
* @irq: iris irq
* @v4l2_dev: a holder for v4l2 device structure
@@ -77,6 +79,8 @@ enum domain_type {

struct iris_core {
struct device *dev;
+ struct device *fw_dev;
+ struct qcom_scm_pas_context *pas_ctx;
void __iomem *reg_base;
int irq;
struct v4l2_device v4l2_dev;
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index 5f408024e967..0085dd7ec052 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -5,6 +5,7 @@

#include <linux/firmware.h>
#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/iommu.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/soc/qcom/mdt_loader.h>
@@ -13,12 +14,15 @@
#include "iris_firmware.h"

#define MAX_FIRMWARE_NAME_SIZE 128
+#define IRIS_FW_START_ADDR 0

static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
{
+ struct device *fw_dev = core->fw_dev ? core->fw_dev : core->dev;
u32 pas_id = core->iris_platform_data->pas_id;
const struct firmware *firmware = NULL;
- struct device *dev = core->dev;
+ struct qcom_scm_pas_context *pas_ctx;
+ struct iommu_domain *domain;
struct resource res;
phys_addr_t mem_phys;
size_t res_size;
@@ -29,14 +33,18 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
return -EINVAL;

- ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ ret = of_reserved_mem_region_to_resource(core->dev->of_node, 0, &res);
if (ret)
return ret;

mem_phys = res.start;
res_size = resource_size(&res);

- ret = request_firmware(&firmware, fw_name, dev);
+ pas_ctx = devm_qcom_scm_pas_context_alloc(fw_dev, pas_id, mem_phys, res_size);
+ if (IS_ERR(pas_ctx))
+ return PTR_ERR(pas_ctx);
+
+ ret = request_firmware(&firmware, fw_name, fw_dev);
if (ret)
return ret;

@@ -52,9 +60,27 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
goto err_release_fw;
}

- ret = qcom_mdt_load(dev, firmware, fw_name,
- pas_id, mem_virt, mem_phys, res_size, NULL);
+ pas_ctx->use_tzmem = !!core->fw_dev;
+ ret = qcom_mdt_pas_load(pas_ctx, firmware, fw_name, mem_virt, NULL);
+ if (ret)
+ goto err_mem_unmap;
+
+ if (pas_ctx->use_tzmem) {
+ domain = iommu_get_domain_for_dev(fw_dev);
+ if (!domain) {
+ ret = -ENODEV;
+ goto err_mem_unmap;
+ }
+
+ ret = iommu_map(domain, IRIS_FW_START_ADDR, mem_phys, res_size,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL);
+ if (ret)
+ goto err_mem_unmap;
+ }

+ core->pas_ctx = pas_ctx;
+
+err_mem_unmap:
memunmap(mem_virt);
err_release_fw:
release_firmware(firmware);
@@ -62,6 +88,18 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
return ret;
}

+static void iris_fw_iommu_unmap(struct iris_core *core)
+{
+ struct iommu_domain *domain;
+
+ if (!core->pas_ctx->use_tzmem)
+ return;
+
+ domain = iommu_get_domain_for_dev(core->fw_dev);
+ if (domain)
+ iommu_unmap(domain, IRIS_FW_START_ADDR, core->pas_ctx->mem_size);
+}
+
int iris_fw_load(struct iris_core *core)
{
const struct tz_cp_config *cp_config;
@@ -79,10 +117,10 @@ int iris_fw_load(struct iris_core *core)
return -ENOMEM;
}

- ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id);
+ ret = qcom_scm_pas_prepare_and_auth_reset(core->pas_ctx);
if (ret) {
dev_err(core->dev, "auth and reset failed: %d\n", ret);
- return ret;
+ goto err_unmap;
}

for (i = 0; i < core->iris_platform_data->tz_cp_config_data_size; i++) {
@@ -93,17 +131,31 @@ int iris_fw_load(struct iris_core *core)
cp_config->cp_nonpixel_size);
if (ret) {
dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret);
- qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
- return ret;
+ goto err_pas_shutdown;
}
}

+ return 0;
+
+err_pas_shutdown:
+ qcom_scm_pas_shutdown(core->pas_ctx->pas_id);
+err_unmap:
+ iris_fw_iommu_unmap(core);
+
return ret;
}

int iris_fw_unload(struct iris_core *core)
{
- return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+ int ret;
+
+ ret = qcom_scm_pas_shutdown(core->pas_ctx->pas_id);
+ if (ret)
+ return ret;
+
+ iris_fw_iommu_unmap(core);
+
+ return ret;
}

int iris_set_hw_state(struct iris_core *core, bool resume)

--
2.34.1