[PATCH 2/4] remoteproc: qcom: pas: Support secondary images
From: Anvesh Jain P
Date: Fri Oct 09 2026 - 03:19:09 EST
The Out-of-Band Management Subsystem (OOBMSS) is managed by a common
remote processor and consists of two Zephyr subsystems:
+----------------------------------------------------------+
| OOBMSS |
| |
| +------------------------+ +------------------------+ |
| | OOB-TEE | | OOB-Non-Secure | |
| | Primary processor | | Auxiliary processor | |
| | Primary image and DTB | | Secondary image and DTB| |
| +------------------------+ +------------------------+ |
+----------------------------------------------------------+
The primary processor is the Out-of-Band Trusted Execution Environment
(OOB-TEE) processor. The OOB-Non-Secure processor is an auxiliary processor
that depends on the primary processor and cannot function independently.
The PAS remoteproc driver currently supports one primary image and its
optional DTB. OOBMSS additionally requires the secondary image and DTB
to be loaded, authenticated, mapped, shut down, and released through
their own PAS identifiers and carveouts.
Add generic PAS support for loading, authenticating, and mapping a
secondary image and its DTB, then shutting down and releasing both while
keeping the existing primary-image lifecycle unchanged.
Signed-off-by: Anvesh Jain P <anvesh.p@xxxxxxxxxxxxxxxx>
---
drivers/remoteproc/qcom_q6v5_pas.c | 235 ++++++++++++++++++++++++++++++++++---
1 file changed, 218 insertions(+), 17 deletions(-)
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index ca8e61254c44..604dfa947340 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -42,8 +42,12 @@ struct qcom_pas_data {
int crash_reason_smem;
const char *firmware_name;
const char *dtb_firmware_name;
+ const char *secondary_firmware_name;
+ const char *secondary_dtb_firmware_name;
int pas_id;
int dtb_pas_id;
+ int secondary_pas_id;
+ int secondary_dtb_pas_id;
int lite_pas_id;
int lite_dtb_pas_id;
unsigned int minidump_id;
@@ -83,8 +87,12 @@ struct qcom_pas {
int proxy_pd_count;
const char *dtb_firmware_name;
+ const char *secondary_firmware_name;
+ const char *secondary_dtb_firmware_name;
int pas_id;
int dtb_pas_id;
+ int secondary_pas_id;
+ int secondary_dtb_pas_id;
int lite_pas_id;
int lite_dtb_pas_id;
unsigned int minidump_id;
@@ -95,17 +103,25 @@ struct qcom_pas {
const struct firmware *firmware;
const struct firmware *dtb_firmware;
+ const struct firmware *secondary_firmware;
+ const struct firmware *secondary_dtb_firmware;
phys_addr_t mem_phys;
phys_addr_t dtb_mem_phys;
+ phys_addr_t secondary_mem_phys;
+ phys_addr_t secondary_dtb_mem_phys;
phys_addr_t mem_reloc;
phys_addr_t dtb_mem_reloc;
+ phys_addr_t secondary_mem_reloc;
+ phys_addr_t secondary_dtb_mem_reloc;
phys_addr_t region_assign_phys[MAX_ASSIGN_COUNT];
void __iomem *mem_region;
size_t mem_size;
size_t dtb_mem_size;
+ size_t secondary_mem_size;
+ size_t secondary_dtb_mem_size;
size_t region_assign_size[MAX_ASSIGN_COUNT];
int region_assign_idx;
@@ -123,6 +139,8 @@ struct qcom_pas {
struct qcom_pas_context *pas_ctx;
struct qcom_pas_context *dtb_pas_ctx;
+ struct qcom_pas_context *secondary_pas_ctx;
+ struct qcom_pas_context *secondary_dtb_pas_ctx;
};
static void qcom_pas_segment_dump(struct rproc *rproc,
@@ -198,19 +216,37 @@ static void qcom_pas_pds_disable(struct qcom_pas *pas, struct device **pds,
}
}
-static int qcom_pas_shutdown_poll_decrypt(struct qcom_pas *pas)
+static int qcom_pas_shutdown_poll_decrypt(int pas_id)
{
unsigned int retry_num = 50;
int ret;
do {
msleep(QCOM_PAS_DECRYPT_SHUTDOWN_DELAY_MS);
- ret = qcom_pas_shutdown(pas->pas_id);
+ ret = qcom_pas_shutdown(pas_id);
} while (ret == -EINVAL && --retry_num);
return ret;
}
+static void qcom_pas_release_firmware(struct qcom_pas *pas)
+{
+ if (pas->dtb_firmware) {
+ release_firmware(pas->dtb_firmware);
+ pas->dtb_firmware = NULL;
+ }
+
+ if (pas->secondary_firmware) {
+ release_firmware(pas->secondary_firmware);
+ pas->secondary_firmware = NULL;
+ }
+
+ if (pas->secondary_dtb_firmware) {
+ release_firmware(pas->secondary_dtb_firmware);
+ pas->secondary_dtb_firmware = NULL;
+ }
+}
+
static int qcom_pas_unprepare(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -224,6 +260,10 @@ static int qcom_pas_unprepare(struct rproc *rproc)
qcom_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
qcom_pas_metadata_release(pas->dtb_pas_ctx);
+ if (pas->secondary_pas_id)
+ qcom_pas_metadata_release(pas->secondary_pas_ctx);
+ if (pas->secondary_dtb_pas_id)
+ qcom_pas_metadata_release(pas->secondary_dtb_pas_ctx);
return 0;
}
@@ -241,19 +281,29 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
if (pas->lite_dtb_pas_id)
qcom_pas_shutdown(pas->lite_dtb_pas_id);
- if (pas->dtb_pas_id) {
- ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev);
+ if (pas->secondary_dtb_pas_id) {
+ ret = request_firmware(&pas->secondary_dtb_firmware,
+ pas->secondary_dtb_firmware_name, pas->dev);
+ if (ret) {
+ qcom_pas_release_firmware(pas);
+ return ret;
+ }
+ }
+
+ if (pas->secondary_pas_id) {
+ ret = request_firmware(&pas->secondary_firmware,
+ pas->secondary_firmware_name, pas->dev);
if (ret) {
- dev_err(pas->dev, "request_firmware failed for %s: %d\n",
- pas->dtb_firmware_name, ret);
+ qcom_pas_release_firmware(pas);
return ret;
}
+ }
- ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
- pas->dtb_firmware_name, &pas->dtb_mem_reloc);
+ if (pas->dtb_pas_id) {
+ ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name,
+ pas->dev);
if (ret) {
- qcom_pas_metadata_release(pas->dtb_pas_ctx);
- release_firmware(pas->dtb_firmware);
+ qcom_pas_release_firmware(pas);
return ret;
}
}
@@ -310,10 +360,51 @@ static int qcom_pas_start(struct rproc *rproc)
goto disable_cx_supply;
}
+ if (pas->secondary_dtb_pas_id) {
+ ret = qcom_mdt_pas_load(pas->secondary_dtb_pas_ctx,
+ pas->secondary_dtb_firmware,
+ pas->secondary_dtb_firmware_name,
+ &pas->secondary_dtb_mem_reloc);
+ if (ret)
+ goto disable_px_supply;
+
+ ret = qcom_pas_map_carveout(rproc, pas->secondary_dtb_mem_phys,
+ pas->secondary_dtb_mem_size);
+ if (ret)
+ goto release_secondary_dtb_metadata;
+
+ ret = qcom_pas_prepare_and_auth_reset(pas->secondary_dtb_pas_ctx);
+ if (ret)
+ goto unmap_secondary_dtb_carveout;
+ }
+
+ if (pas->secondary_pas_id) {
+ ret = qcom_mdt_pas_load(pas->secondary_pas_ctx,
+ pas->secondary_firmware,
+ pas->secondary_firmware_name,
+ &pas->secondary_mem_reloc);
+ if (ret)
+ goto unmap_secondary_dtb_carveout;
+
+ ret = qcom_pas_map_carveout(rproc, pas->secondary_mem_phys,
+ pas->secondary_mem_size);
+ if (ret)
+ goto release_secondary_metadata;
+
+ ret = qcom_pas_prepare_and_auth_reset(pas->secondary_pas_ctx);
+ if (ret)
+ goto unmap_secondary_carveout;
+ }
+
if (pas->dtb_pas_id) {
+ ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
+ pas->dtb_firmware_name, &pas->dtb_mem_reloc);
+ if (ret)
+ goto unmap_secondary_carveout;
+
ret = qcom_pas_map_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
if (ret)
- goto disable_px_supply;
+ goto release_dtb_metadata;
ret = qcom_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
if (ret) {
@@ -326,7 +417,7 @@ static int qcom_pas_start(struct rproc *rproc)
ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware,
&pas->mem_reloc);
if (ret)
- goto release_pas_metadata;
+ goto unmap_dtb_carveout;
qcom_pil_info_store(pas->info_name, pas->mem_phys, pas->mem_size);
@@ -351,6 +442,10 @@ static int qcom_pas_start(struct rproc *rproc)
qcom_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
qcom_pas_metadata_release(pas->dtb_pas_ctx);
+ if (pas->secondary_pas_id)
+ qcom_pas_metadata_release(pas->secondary_pas_ctx);
+ if (pas->secondary_dtb_pas_id)
+ qcom_pas_metadata_release(pas->secondary_dtb_pas_ctx);
/* firmware is used to pass reference from qcom_pas_start(), drop it now */
pas->firmware = NULL;
@@ -361,12 +456,27 @@ static int qcom_pas_start(struct rproc *rproc)
qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
release_pas_metadata:
qcom_pas_metadata_release(pas->pas_ctx);
- if (pas->dtb_pas_id)
- qcom_pas_metadata_release(pas->dtb_pas_ctx);
-
unmap_dtb_carveout:
if (pas->dtb_pas_id)
- qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys,
+ pas->dtb_mem_size);
+release_dtb_metadata:
+ if (pas->dtb_pas_id)
+ qcom_pas_metadata_release(pas->dtb_pas_ctx);
+unmap_secondary_carveout:
+ if (pas->secondary_pas_id)
+ qcom_pas_unmap_carveout(rproc, pas->secondary_mem_phys,
+ pas->secondary_mem_size);
+release_secondary_metadata:
+ if (pas->secondary_pas_id)
+ qcom_pas_metadata_release(pas->secondary_pas_ctx);
+unmap_secondary_dtb_carveout:
+ if (pas->secondary_dtb_pas_id)
+ qcom_pas_unmap_carveout(rproc, pas->secondary_dtb_mem_phys,
+ pas->secondary_dtb_mem_size);
+release_secondary_dtb_metadata:
+ if (pas->secondary_dtb_pas_id)
+ qcom_pas_metadata_release(pas->secondary_dtb_pas_ctx);
disable_px_supply:
if (pas->px_supply)
regulator_disable(pas->px_supply);
@@ -413,7 +523,7 @@ static int qcom_pas_stop(struct rproc *rproc)
ret = qcom_pas_shutdown(pas->pas_id);
if (ret && pas->decrypt_shutdown)
- ret = qcom_pas_shutdown_poll_decrypt(pas);
+ ret = qcom_pas_shutdown_poll_decrypt(pas->pas_id);
if (ret)
dev_err(pas->dev, "failed to shutdown: %d\n", ret);
@@ -426,6 +536,27 @@ static int qcom_pas_stop(struct rproc *rproc)
qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
}
+ if (pas->secondary_pas_id) {
+ ret = qcom_pas_shutdown(pas->secondary_pas_id);
+ if (ret && pas->decrypt_shutdown)
+ ret = qcom_pas_shutdown_poll_decrypt(pas->secondary_pas_id);
+ if (ret)
+ dev_err(pas->dev, "failed to shutdown secondary: %d\n", ret);
+
+ qcom_pas_unmap_carveout(rproc, pas->secondary_mem_phys,
+ pas->secondary_mem_size);
+ }
+
+ if (pas->secondary_dtb_pas_id) {
+ ret = qcom_pas_shutdown(pas->secondary_dtb_pas_id);
+ if (ret)
+ dev_err(pas->dev,
+ "failed to shutdown secondary dtb: %d\n", ret);
+
+ qcom_pas_unmap_carveout(rproc, pas->secondary_dtb_mem_phys,
+ pas->secondary_dtb_mem_size);
+ }
+
qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
handover = qcom_q6v5_unprepare(&pas->q6v5);
@@ -740,6 +871,42 @@ static int qcom_pas_alloc_memory_region(struct qcom_pas *pas)
if (IS_ERR(pas->dtb_pas_ctx))
return PTR_ERR(pas->dtb_pas_ctx);
+ if (!pas->secondary_pas_id)
+ return 0;
+
+ ret = of_reserved_mem_region_to_resource(pas->dev->of_node, 2, &res);
+ if (ret)
+ return ret;
+
+ pas->secondary_mem_phys = res.start;
+ pas->secondary_mem_reloc = res.start;
+ pas->secondary_mem_size = resource_size(&res);
+
+ pas->secondary_pas_ctx =
+ devm_qcom_pas_context_alloc(pas->dev, pas->secondary_pas_id,
+ pas->secondary_mem_phys,
+ pas->secondary_mem_size);
+ if (IS_ERR(pas->secondary_pas_ctx))
+ return PTR_ERR(pas->secondary_pas_ctx);
+
+ if (!pas->secondary_dtb_pas_id)
+ return 0;
+
+ ret = of_reserved_mem_region_to_resource(pas->dev->of_node, 3, &res);
+ if (ret)
+ return ret;
+
+ pas->secondary_dtb_mem_phys = res.start;
+ pas->secondary_dtb_mem_reloc = res.start;
+ pas->secondary_dtb_mem_size = resource_size(&res);
+
+ pas->secondary_dtb_pas_ctx =
+ devm_qcom_pas_context_alloc(pas->dev, pas->secondary_dtb_pas_id,
+ pas->secondary_dtb_mem_phys,
+ pas->secondary_dtb_mem_size);
+ if (IS_ERR(pas->secondary_dtb_pas_ctx))
+ return PTR_ERR(pas->secondary_dtb_pas_ctx);
+
return 0;
}
@@ -823,6 +990,8 @@ static int qcom_pas_probe(struct platform_device *pdev)
struct rproc *rproc;
struct device_node *node;
const char *fw_name, *dtb_fw_name = NULL;
+ const char *secondary_fw_name = NULL;
+ const char *secondary_dtb_fw_name = NULL;
const struct rproc_ops *ops = &qcom_pas_ops;
int ret;
@@ -847,6 +1016,24 @@ static int qcom_pas_probe(struct platform_device *pdev)
return ret;
}
+ if (desc->secondary_firmware_name) {
+ secondary_fw_name = desc->secondary_firmware_name;
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "firmware-name", 2,
+ &secondary_fw_name);
+ if (ret < 0 && ret != -EINVAL)
+ return ret;
+ }
+
+ if (desc->secondary_dtb_firmware_name) {
+ secondary_dtb_fw_name = desc->secondary_dtb_firmware_name;
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "firmware-name", 3,
+ &secondary_dtb_fw_name);
+ if (ret < 0 && ret != -EINVAL)
+ return ret;
+ }
+
if (desc->minidump_id)
ops = &qcom_pas_minidump_ops;
@@ -868,6 +1055,8 @@ static int qcom_pas_probe(struct platform_device *pdev)
pas->pas_id = desc->pas_id;
pas->lite_pas_id = desc->lite_pas_id;
pas->lite_dtb_pas_id = desc->lite_dtb_pas_id;
+ pas->secondary_pas_id = desc->secondary_pas_id;
+ pas->secondary_dtb_pas_id = desc->secondary_dtb_pas_id;
pas->info_name = desc->sysmon_name;
pas->smem_host_id = desc->smem_host_id;
pas->decrypt_shutdown = desc->decrypt_shutdown;
@@ -879,6 +1068,14 @@ static int qcom_pas_probe(struct platform_device *pdev)
pas->dtb_firmware_name = dtb_fw_name;
pas->dtb_pas_id = desc->dtb_pas_id;
}
+ if (secondary_fw_name) {
+ pas->secondary_firmware_name = secondary_fw_name;
+ pas->secondary_pas_id = desc->secondary_pas_id;
+ }
+ if (secondary_dtb_fw_name) {
+ pas->secondary_dtb_firmware_name = secondary_dtb_fw_name;
+ pas->secondary_dtb_pas_id = desc->secondary_dtb_pas_id;
+ }
platform_set_drvdata(pdev, pas);
ret = device_init_wakeup(pas->dev, true);
@@ -925,6 +1122,10 @@ static int qcom_pas_probe(struct platform_device *pdev)
pas->pas_ctx->use_tzmem = desc->needs_tzmem || rproc->has_iommu;
if (pas->dtb_pas_id)
pas->dtb_pas_ctx->use_tzmem = desc->needs_tzmem || rproc->has_iommu;
+ if (pas->secondary_pas_id)
+ pas->secondary_pas_ctx->use_tzmem = desc->needs_tzmem || rproc->has_iommu;
+ if (pas->secondary_dtb_pas_id)
+ pas->secondary_dtb_pas_ctx->use_tzmem = desc->needs_tzmem || rproc->has_iommu;
if (desc->early_boot)
pas->rproc->state = RPROC_DETACHED;
--
2.34.1