remoteproc: qcom: pas: Map/unmap subsystem region before auth_and_reset

Qualcomm remoteproc drivers such as qcom_q6v5_mss, which do not use the
Peripheral Authentication Service (PAS), always map the MBA region before
use and unmap it once the usage is complete. This behavior was introduced
to avoid issues seen in the past where speculative accesses from the
application processor to the MBA region after it was assigned to the remote
Q6 led to an XPU violation. The issue was mitigated by unmapping the region
before handing control to the remote Q6.

Currently, most Qualcomm SoCs using the PAS driver run either with a
standalone QHEE or the Gunyah hypervisor. In these environments, the
hypervisor unmaps the Q6 memory from HLOS Stage-2 and remaps it into the
Q6 Stage-2 page table. As a result, speculative accesses from HLOS cannot
reach the region even if it remains mapped in HLOS Stage-1; therefore, XPU
violations cannot occur.

However, when the same SoC runs Linux at EL2, Linux itself must perform the
unmapping to avoid such issues. It is still correct to apply this mapping/
unmapping sequence even for SoCs that run under Gunyah, so this behavior
should not be conditional.

Introduce qcom_pas_ctx_map() in qcom_pas.h to centralise the ioremap_wc
pattern used by both qcom_q6v5_pas and qcom_mdt_pas_load, and use it in
both places.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20260724182858.1868271-5-mukesh.ojha@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
This commit is contained in:
Mukesh Ojha 2026-07-24 23:58:56 +05:30 committed by Bjorn Andersson
parent a5464fa317
commit 0b0379fcf9
4 changed files with 47 additions and 19 deletions

View File

@ -150,7 +150,13 @@ static void qcom_pas_minidump(struct rproc *rproc)
if (rproc->dump_conf == RPROC_COREDUMP_DISABLED)
return;
pas->mem_region = qcom_pas_ctx_map(pas->pas_ctx);
if (!pas->mem_region)
return;
qcom_minidump(rproc, pas->minidump_id, qcom_pas_segment_dump);
iounmap(pas->mem_region);
pas->mem_region = NULL;
}
static int qcom_pas_pds_enable(struct qcom_pas *pas, struct device **pds,
@ -243,8 +249,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
}
ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
pas->dtb_firmware_name, (__force void *)pas->dtb_mem_region,
&pas->dtb_mem_reloc);
pas->dtb_firmware_name, &pas->dtb_mem_reloc);
if (ret) {
qcom_pas_metadata_release(pas->dtb_pas_ctx);
release_firmware(pas->dtb_firmware);
@ -318,7 +323,7 @@ static int qcom_pas_start(struct rproc *rproc)
}
ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware,
(__force void *)pas->mem_region, &pas->mem_reloc);
&pas->mem_reloc);
if (ret)
goto release_pas_metadata;
@ -570,6 +575,19 @@ static int qcom_pas_attach(struct rproc *rproc)
return ret;
}
static void qcom_pas_coredump(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
pas->mem_region = qcom_pas_ctx_map(pas->pas_ctx);
if (!pas->mem_region)
return;
rproc_coredump(rproc);
iounmap(pas->mem_region);
pas->mem_region = NULL;
}
static const struct rproc_ops qcom_pas_ops = {
.unprepare = qcom_pas_unprepare,
.start = qcom_pas_start,
@ -579,6 +597,7 @@ static const struct rproc_ops qcom_pas_ops = {
.load = qcom_pas_load,
.panic = qcom_pas_panic,
.attach = qcom_pas_attach,
.coredump = qcom_pas_coredump,
};
static const struct rproc_ops qcom_pas_minidump_ops = {
@ -696,11 +715,6 @@ static int qcom_pas_alloc_memory_region(struct qcom_pas *pas)
pas->mem_phys = pas->mem_reloc = res.start;
pas->mem_size = resource_size(&res);
pas->mem_region = devm_ioremap_resource_wc(pas->dev, &res);
if (IS_ERR(pas->mem_region)) {
dev_err(pas->dev, "unable to map memory region: %pR\n", &res);
return PTR_ERR(pas->mem_region);
}
pas->pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->pas_id,
pas->mem_phys, pas->mem_size);
@ -718,11 +732,6 @@ static int qcom_pas_alloc_memory_region(struct qcom_pas *pas)
pas->dtb_mem_phys = pas->dtb_mem_reloc = res.start;
pas->dtb_mem_size = resource_size(&res);
pas->dtb_mem_region = devm_ioremap_resource_wc(pas->dev, &res);
if (IS_ERR(pas->dtb_mem_region)) {
dev_err(pas->dev, "unable to map dtb memory region: %pR\n", &res);
return PTR_ERR(pas->dtb_mem_region);
}
pas->dtb_pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->dtb_pas_id,
pas->dtb_mem_phys,

View File

@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/elf.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/firmware/qcom/qcom_pas.h>
@ -478,22 +479,28 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load);
* @ctx: Pointer to the PAS (Peripheral Authentication Service) context
* @fw: Firmware object representing the .mdt file
* @firmware: Name of the firmware used to construct segment file names
* @mem_region: Memory region allocated for loading the firmware
* @reloc_base: Physical address adjusted after relocation
*
* Return: 0 on success or a negative error code on failure.
*/
int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw,
const char *firmware, void *mem_region, phys_addr_t *reloc_base)
const char *firmware, phys_addr_t *reloc_base)
{
void __iomem *mem_region;
int ret;
ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx);
if (ret)
return ret;
return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys,
ctx->mem_size, reloc_base);
mem_region = qcom_pas_ctx_map(ctx);
if (!mem_region)
return -ENOMEM;
ret = qcom_mdt_load_no_init(ctx->dev, fw, firmware, (__force void *)mem_region,
ctx->mem_phys, ctx->mem_size, reloc_base);
iounmap(mem_region);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_mdt_pas_load);

View File

@ -8,7 +8,9 @@
#ifndef __QCOM_PAS_H
#define __QCOM_PAS_H
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/types.h>
struct qcom_pas_context {
@ -22,6 +24,16 @@ struct qcom_pas_context {
bool use_tzmem;
};
static inline void __iomem *qcom_pas_ctx_map(struct qcom_pas_context *ctx)
{
void __iomem *ptr = ioremap_wc(ctx->mem_phys, ctx->mem_size);
if (!ptr)
dev_err(ctx->dev, "unable to map memory region: %pa+%zx\n",
&ctx->mem_phys, ctx->mem_size);
return ptr;
}
bool qcom_pas_is_available(void);
struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
u32 pas_id,

View File

@ -21,7 +21,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
phys_addr_t *reloc_base);
int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw,
const char *firmware, void *mem_region, phys_addr_t *reloc_base);
const char *firmware, phys_addr_t *reloc_base);
int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw,
const char *fw_name, void *mem_region,
@ -47,7 +47,7 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw,
static inline int qcom_mdt_pas_load(struct qcom_pas_context *ctx,
const struct firmware *fw, const char *firmware,
void *mem_region, phys_addr_t *reloc_base)
phys_addr_t *reloc_base)
{
return -ENODEV;
}