* [PATCH 1/1] wifi: mac80211: free ack status frame on TX header build failure
From: Ren Wei @ 2026-06-26 16:58 UTC (permalink / raw)
To: linux-wireless
Cc: johannes, linville, yuantan098, yifanwucs, tomapufckgml, bird,
roxy520tt, n05ec
In-Reply-To: <cover.1782462409.git.roxy520tt@gmail.com>
From: Zhiling Zou <roxy520tt@gmail.com>
ieee80211_build_hdr() stores an ACK status frame before it has
finished all validation and header construction. If a later error path
is taken, the transmit skb is freed but the stored ACK status frame
remains in local->ack_status_frames.
This can happen for control port frames when the requested MLO link ID
does not match the link selected for a non-MLO station. Repeated
failures can fill the ACK status IDR and leave pending ACK frames until
hardware teardown.
Remove any stored ACK status frame before returning an error after it
has been inserted into the IDR.
Fixes: a729cff8ad51 ("mac80211: implement wifi TX status")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
net/mac80211/tx.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index ea7f63e1fc17..8e7f43ee10e5 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2597,6 +2597,18 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local,
return info_id;
}
+static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id)
+{
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_remove(&local->ack_status_frames, info_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ kfree_skb(ack_skb);
+}
+
/**
* ieee80211_build_hdr - build 802.11 header in the given frame
* @sdata: virtual interface to build the header for
@@ -2972,7 +2984,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto free;
}
}
@@ -3040,6 +3053,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
return skb;
free:
+ if (info_id)
+ ieee80211_remove_ack_skb(local, info_id);
kfree_skb(skb);
return ERR_PTR(ret);
}
--
2.43.0
^ permalink raw reply related
* [PATCH] fix: ath11k: ath11k_ahb_fw_resources_init: fix device_node reference leak on setup failure
From: WenTao Liang @ 2026-06-26 15:36 UTC (permalink / raw)
To: Kalle Valo, linux-wireless
Cc: Jeff Johnson, stable, linux-kernel, WenTao Liang
of_get_child_by_name acquires a device_node reference. When the
subsequent ath11k_ahb_setup_msa_resources call fails, the function
returns immediately without calling of_node_put, leaking the node
reference.
Add of_node_put before returning on the setup failure error path.
Cc: stable@vger.kernel.org
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: WenTao Liang <vulab@iscas.ac.cn>
---
drivers/net/wireless/ath/ath11k/ahb.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 08d3a0c8f105..8a08275db40a 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab)
ret = ath11k_ahb_setup_msa_resources(ab);
if (ret) {
ath11k_err(ab, "failed to setup msa resources\n");
+ of_node_put(node);
return ret;
}
--
2.39.5 (Apple Git-154)
^ permalink raw reply related
* Re: [patch 09/24] timekeeping: Add CLOCK_AUX support for ktime_get_snapshot_id()
From: Thomas Gleixner @ 2026-06-26 15:17 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: LKML, David Woodhouse, Miroslav Lichvar, John Stultz,
Stephen Boyd, Anna-Maria Behnsen, Frederic Weisbecker,
Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Jacob Keller,
Tony Nguyen, Saeed Mahameed, Peter Hilber, Michael S. Tsirkin,
virtualization, linux-wireless, linux-sound
In-Reply-To: <20260626125819-d8b197fc-7671-4d12-a578-9025affc52d9@linutronix.de>
On Fri, Jun 26 2026 at 13:03, Thomas Weißschuh wrote:
> On Fri, Jun 26, 2026 at 12:49:41PM +0200, Thomas Gleixner wrote:
>> On Fri, Jun 26 2026 at 10:48, Thomas Weißschuh wrote:
>> > On Tue, May 26, 2026 at 07:14:13PM +0200, Thomas Gleixner wrote:
>> > (...)
>> >
>> >> static inline void tk_update_aux_offs(struct timekeeper *tk, ktime_t offs)
>> >> @@ -1218,6 +1223,12 @@ bool ktime_get_snapshot_id(struct system
>> >> tkd = &tk_core;
>> >> offs = &tk_core.timekeeper.offs_boot;
>> >> break;
>> >> + case CLOCK_AUX ... CLOCK_AUX_LAST:
>> >> + tkd = aux_get_tk_data(clock_id);
>> >> + if (!tkd)
>> >> + return false;
>> >> + offs = &tkd->timekeeper.offs_aux;
>> >> + break;
>> >
>> > 'tkd' is also used to compute 'monoraw'. However 'tkr_raw' and 'tkr_mono'
>> > are the same for auxilary clocks, so this will compute a wrong 'monoraw'.
>>
>> AUX clocks are independent in the first place and the MONORAW part is
>> the "MONORAW" related to the AUX clock itself.
>>
>> > Instead 'monoraw' should be computed based on 'tk_core'.
>> > Which then also requires the sequence locking of 'tk_core'.
>>
>> No. From a PTP and steering point of view you want the "raw" value which
>> is related to the AUX clock itself and not the global one.
>
> Ack.
>
> However the kdocs call it 'CLOCK_MONOTONIC_RAW'. Can we clean this up?
Yes. Something like the below?
Thanks,
tglx
---
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1202,10 +1202,21 @@ static inline u64 tk_clock_read_snapshot
/**
* ktime_get_snapshot_id - Simultaneously snapshot a given clock ID with
- * CLOCK_MONOTONIC_RAW and the underlying
+ * the corresponding monotonic raw the underlying
* clocksource counter value.
* @clock_id: The clock ID to snapshot
* @systime_snapshot: Pointer to struct receiving the system time snapshot
+ *
+ * For the system time keeping clocks (REALTIME, MONOTONIC and BOOTTIME) the
+ * monotonic raw clock is CLOCK_MONOTONIC_RAW. For AUX clocks this is the
+ * monotonic raw clock related to the AUX clock. These AUX clock related
+ * monotonic raw clocks have a strict linear offset to the system time
+ * CLOCK_MONOTONIC_RAW:
+ *
+ * MONOTONIC_RAW(AUX$N) = CLOCK_MONOTONIC_RAW(system) + offset(AUX$N)
+ *
+ * The offset is established when a AUX clock is initialized, but it is
+ * currently not accessible.
*/
void ktime_get_snapshot_id(clockid_t clock_id, struct system_time_snapshot *systime_snapshot)
{
@@ -1512,6 +1523,9 @@ EXPORT_SYMBOL_GPL(ktime_real_to_base_clo
* @xtstamp: Receives simultaneously captured system and device time
*
* Reads a timestamp from a device and correlates it to system time
+ *
+ * See documentation for ktime_get_snapshot_id() for information about the raw
+ * monotonic time stamp which is used here.
*/
int get_device_system_crosststamp(int (*get_time_fn)
(ktime_t *device_time,
^ permalink raw reply
* Re: [PATCH wireless-next] wifi: cfg80211: fix regulatory.db async firmware request blocking __usermodehelper_disable()
From: Carlos Llamas @ 2026-06-26 14:46 UTC (permalink / raw)
To: Kavita Kavita; +Cc: johannes, linux-wireless
In-Reply-To: <20260625092904.2097371-1-kavita.kavita@oss.qualcomm.com>
On Thu, Jun 25, 2026 at 02:59:04PM +0530, Kavita Kavita wrote:
> cfg80211 schedules an asynchronous request_firmware() work item for
> regulatory.db via request_firmware_work_func(). When the direct
> firmware load fails, _request_firmware() falls back to the sysfs
> fallback path via firmware_fallback_sysfs(), which blocks indefinitely
> in wait_for_completion_killable_timeout() waiting for userspace to
> supply the firmware through the sysfs interface.
>
> While this work item is pending, any caller of
> __usermodehelper_disable() will deadlock attempting to acquire the
> usermodehelper rwsem for writing, since the sysfs firmware fallback
> path holds the rwsem for reading and is blocked waiting for userspace
> response that can never arrive while usermode helpers are being
> disabled.
>
> Observed call traces where system suspend blocked due to regulatory.db
> async firmware request:
>
> kworker/6:3 (pid 194) holding usermodehelper rwsem read lock, blocked
> waiting for userspace firmware response:
> wait_for_completion_killable_timeout+0x48
> firmware_fallback_sysfs+0x270
> _request_firmware+0x790
> request_firmware_work_func+0x44
> process_one_work[jt]+0x59c
> worker_thread+0x260
> kthread+0x150
> ret_from_fork+0x10
>
> Caller blocked in __usermodehelper_disable() during system suspend:
> rwsem_down_write_slowpath+0x768
> down_write+0x98
> __usermodehelper_disable+0x3c
> freeze_processes+0x18
> pm_suspend+0x320
> state_store+0x104
> kernfs_fop_write_iter[jt]+0x168
> vfs_write+0x270
> ksys_write+0x78
>
> Any service or kernel subsystem invoking __usermodehelper_disable() is
> susceptible to this hang as long as the regulatory.db sysfs fallback
> request remains outstanding.
>
> Fix this by replacing the unconditional uevent-based load with a
> two-step approach. First, attempt a synchronous load via
> request_firmware_direct() at init time. This is fast and
> non-blocking, if the file is present in standard paths it is loaded
> immediately with no delay. If not found, the load is deferred to
> wiphy_regulatory_register() and triggered via
> firmware_request_nowait_nowarn() only when the first non-self-managed
> wiphy registers.
>
> For self-managed drivers (REGULATORY_WIPHY_SELF_MANAGED), the hang is
> avoided because wiphy_regulatory_register() handles them separately
> and the deferred load path is never reached, so the file load is not
> attempted at all. For this case, regulatory information is obtained
> from driver/firmware during wiphy registration. For non-self-managed
> drivers, the file is required and is expected to be present. The
> deferred load via firmware_request_nowait_nowarn() is triggered only
> when the first such wiphy registers. This ensures the database is
> loaded only when it is actually needed, avoiding the sysfs fallback
> path on systems where the file is absent at init time.
>
> Also refactor regdb_fw_cb() into two functions: regdb_load() which
> validates and stores the firmware image, and regdb_fw_cb_restore()
> which is the async callback that calls restore_regulatory_settings()
> to replay all pending regulatory requests (country hints from core,
> user, driver and country IE) that arrived while the database was not
> yet available.
>
> NOTE:
> This issue was observed on Android platforms where regulatory.db is
> absent.
> Steps to reproduce on Android platforms:
> 1. Power off the device completely.
> 2. Connect the charger; the device enters off-mode charging.
> 3. While in off-mode charging, short press the power key.
>
> Signed-off-by: Kavita Kavita <kavita.kavita@oss.qualcomm.com>
Are you missing a `Fixes:` tag for this?
^ permalink raw reply
* [PATCH] wifi: cfg80211: bound element ID read when checking non-inheritance
From: HE WEI (ギカク) @ 2026-06-26 14:45 UTC (permalink / raw)
To: Johannes Berg
Cc: linux-wireless, linux-kernel, HE WEI (ギカク)
cfg80211_is_element_inherited() reads the first data octet of the
candidate element (id = elem->data[0]) to look it up in an extension
non-inheritance list. It does so after testing elem->id, but without
verifying that the element actually has a data octet. A zero-length
extension element (WLAN_EID_EXTENSION with length 0) therefore makes it
read one octet past the end of the element.
_ieee802_11_parse_elems_full() runs this check for every element of a
frame once a non-inheritance context exists -- e.g. while parsing a
per-STA profile of a Multi-Link element in a (re)association response,
or a non-transmitted BSS profile -- so a crafted frame from an AP can
trigger a one-octet slab-out-of-bounds read during element parsing:
BUG: KASAN: slab-out-of-bounds in cfg80211_is_element_inherited
Read of size 1 ... in net/wireless/scan.c
Return early (treat the element as inherited) when an extension element
carries no data, mirroring the existing handling of empty ID lists.
The bug was found by fuzzing ieee802_11_parse_elems_full() under KASAN.
Fixes: dfd9aa3e7a45 ("wifi: cfg80211: rewrite merging of inherited elements")
Signed-off-by: HE WEI (ギカク) <skyexpoc@gmail.com>
---
net/wireless/scan.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 05b7dc6b7..0a44856b1 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem,
return true;
if (elem->id == WLAN_EID_EXTENSION) {
- if (!ext_id_len)
+ if (!ext_id_len || !elem->datalen)
return true;
loop_len = ext_id_len;
list = &non_inherit_elem->data[3 + id_len];
--
2.43.0
^ permalink raw reply related
* [PATCH v8 14/14] MAINTAINERS: Add maintainer entry for Qualcomm PAS TZ service
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Add Sumit Garg as the maintainer for the Qualcomm generic Peripheral
Authentication Service (PAS) as well as the PAS TEE backend driver.
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..7847b2a98f90 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22315,6 +22315,15 @@ F: Documentation/devicetree/bindings/media/*qcom*
F: drivers/media/platform/qcom
F: include/dt-bindings/media/*qcom*
+QUALCOMM PAS TZ SERVICE
+M: Sumit Garg <sumit.garg@oss.qualcomm.com>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: drivers/firmware/qcom/qcom_pas.c
+F: drivers/firmware/qcom/qcom_pas.h
+F: drivers/firmware/qcom/qcom_pas_tee.c
+F: include/linux/firmware/qcom/qcom_pas.h
+
QUALCOMM SMB CHARGER DRIVER
M: Casey Connolly <casey.connolly@linaro.org>
L: linux-arm-msm@vger.kernel.org
--
2.53.0
^ permalink raw reply related
* [PATCH v8 13/14] firmware: qcom_scm: Remove SCM PAS wrappers
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Now since all the Qcom SCM client drivers have been migrated over to
generic PAS TZ service, let's drop the exported SCM PAS wrappers.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 143 +++++--------------------
include/linux/firmware/qcom/qcom_scm.h | 29 -----
2 files changed, 29 insertions(+), 143 deletions(-)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 7933e55803dc..1deee6aea387 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -553,26 +553,6 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
}
-struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
- u32 pas_id,
- phys_addr_t mem_phys,
- size_t mem_size)
-{
- struct qcom_pas_context *ctx;
-
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return ERR_PTR(-ENOMEM);
-
- ctx->dev = dev;
- ctx->pas_id = pas_id;
- ctx->mem_phys = mem_phys;
- ctx->mem_size = mem_size;
-
- return (struct qcom_scm_pas_context *)ctx;
-}
-EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
-
static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id,
dma_addr_t mdata_phys,
struct qcom_scm_res *res)
@@ -630,9 +610,9 @@ static int qcom_scm_pas_prep_and_init_image(struct device *dev,
return ret ? : res.result[0];
}
-static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id,
- const void *metadata, size_t size,
- struct qcom_pas_context *ctx)
+static int qcom_scm_pas_init_image(struct device *dev, u32 pas_id,
+ const void *metadata, size_t size,
+ struct qcom_pas_context *ctx)
{
struct qcom_scm_res res;
dma_addr_t mdata_phys;
@@ -672,16 +652,8 @@ static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id,
return ret ? : res.result[0];
}
-int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
- struct qcom_scm_pas_context *ctx)
-{
- return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size,
- (struct qcom_pas_context *)ctx);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
-
-static void __qcom_scm_pas_metadata_release(struct device *dev,
- struct qcom_pas_context *ctx)
+static void qcom_scm_pas_metadata_release(struct device *dev,
+ struct qcom_pas_context *ctx)
{
if (ctx->use_tzmem)
qcom_tzmem_free(ctx->ptr);
@@ -691,15 +663,8 @@ static void __qcom_scm_pas_metadata_release(struct device *dev,
ctx->ptr = NULL;
}
-void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
-{
- __qcom_scm_pas_metadata_release(__scm->dev,
- (struct qcom_pas_context *)ctx);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
-
-static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id,
- phys_addr_t addr, phys_addr_t size)
+static int qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id,
+ phys_addr_t addr, phys_addr_t size)
{
int ret;
struct qcom_scm_desc desc = {
@@ -730,12 +695,6 @@ static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id,
return ret ? : res.result[0];
}
-int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
-{
- return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
-
static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id,
void *input_rt_tzm,
size_t input_rt_size,
@@ -789,11 +748,10 @@ static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id,
return ret ? ERR_PTR(ret) : output_rt_tzm;
}
-static void *__qcom_scm_pas_get_rsc_table2(struct device *dev,
- struct qcom_pas_context *ctx,
- void *input_rt,
- size_t input_rt_size,
- size_t *output_rt_size)
+static void *qcom_scm_pas_get_rsc_table(struct device *dev,
+ struct qcom_pas_context *ctx,
+ void *input_rt, size_t input_rt_size,
+ size_t *output_rt_size)
{
struct resource_table empty_rsc = {};
size_t size = SZ_16K;
@@ -864,19 +822,7 @@ static void *__qcom_scm_pas_get_rsc_table2(struct device *dev,
return ret ? ERR_PTR(ret) : tbl_ptr;
}
-struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
- void *input_rt,
- size_t input_rt_size,
- size_t *output_rt_size)
-{
- return __qcom_scm_pas_get_rsc_table2(__scm->dev,
- (struct qcom_pas_context *)ctx,
- input_rt, input_rt_size,
- output_rt_size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
-
-static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id)
+static int qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
@@ -905,14 +851,8 @@ static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id)
return ret ? : res.result[0];
}
-int qcom_scm_pas_auth_and_reset(u32 pas_id)
-{
- return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
-
-static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
- struct qcom_pas_context *ctx)
+static int qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
+ struct qcom_pas_context *ctx)
{
u64 handle;
int ret;
@@ -923,7 +863,7 @@ static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
* memory region and then invokes a call to TrustZone to authenticate.
*/
if (!ctx->use_tzmem)
- return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
+ return qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
/*
* When Linux runs @ EL2 Linux must create the shmbridge itself and then
@@ -933,21 +873,14 @@ static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
if (ret)
return ret;
- ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
+ ret = qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
qcom_tzmem_shm_bridge_delete(handle);
return ret;
}
-int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
-{
- return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev,
- (struct qcom_pas_context *)ctx);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
-
-static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state,
- u32 pas_id)
+static int qcom_scm_pas_set_remote_state(struct device *dev, u32 state,
+ u32 pas_id)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_BOOT,
@@ -965,13 +898,7 @@ static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state,
return ret ? : res.result[0];
}
-int qcom_scm_set_remote_state(u32 state, u32 id)
-{
- return __qcom_scm_pas_set_remote_state(__scm->dev, state, id);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
-
-static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id)
+static int qcom_scm_pas_shutdown(struct device *dev, u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
@@ -1000,13 +927,7 @@ static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id)
return ret ? : res.result[0];
}
-int qcom_scm_pas_shutdown(u32 pas_id)
-{
- return __qcom_scm_pas_shutdown(__scm->dev, pas_id);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
-
-static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id)
+static bool qcom_scm_pas_supported(struct device *dev, u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
@@ -1027,23 +948,17 @@ static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id)
return ret ? false : !!res.result[0];
}
-bool qcom_scm_pas_supported(u32 pas_id)
-{
- return __qcom_scm_pas_supported(__scm->dev, pas_id);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
-
static struct qcom_pas_ops qcom_pas_ops_scm = {
.drv_name = "qcom_scm",
- .supported = __qcom_scm_pas_supported,
- .init_image = __qcom_scm_pas_init_image2,
- .mem_setup = __qcom_scm_pas_mem_setup,
- .get_rsc_table = __qcom_scm_pas_get_rsc_table2,
- .auth_and_reset = __qcom_scm_pas_auth_and_reset,
- .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset,
- .set_remote_state = __qcom_scm_pas_set_remote_state,
- .shutdown = __qcom_scm_pas_shutdown,
- .metadata_release = __qcom_scm_pas_metadata_release,
+ .supported = qcom_scm_pas_supported,
+ .init_image = qcom_scm_pas_init_image,
+ .mem_setup = qcom_scm_pas_mem_setup,
+ .get_rsc_table = qcom_scm_pas_get_rsc_table,
+ .auth_and_reset = qcom_scm_pas_auth_and_reset,
+ .prepare_and_auth_reset = qcom_scm_pas_prepare_and_auth_reset,
+ .set_remote_state = qcom_scm_pas_set_remote_state,
+ .shutdown = qcom_scm_pas_shutdown,
+ .metadata_release = qcom_scm_pas_metadata_release,
};
/**
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index 5747bd191bf1..a0a6bc0229c4 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -64,35 +64,6 @@ bool qcom_scm_is_available(void);
int qcom_scm_set_cold_boot_addr(void *entry);
int qcom_scm_set_warm_boot_addr(void *entry);
void qcom_scm_cpu_power_down(u32 flags);
-int qcom_scm_set_remote_state(u32 state, u32 id);
-
-struct qcom_scm_pas_context {
- struct device *dev;
- u32 pas_id;
- phys_addr_t mem_phys;
- size_t mem_size;
- void *ptr;
- dma_addr_t phys;
- ssize_t size;
- bool use_tzmem;
-};
-
-struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
- u32 pas_id,
- phys_addr_t mem_phys,
- size_t mem_size);
-int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
- struct qcom_scm_pas_context *ctx);
-void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx);
-int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size);
-int qcom_scm_pas_auth_and_reset(u32 pas_id);
-int qcom_scm_pas_shutdown(u32 pas_id);
-bool qcom_scm_pas_supported(u32 pas_id);
-struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
- void *input_rt, size_t input_rt_size,
- size_t *output_rt_size);
-
-int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
--
2.53.0
^ permalink raw reply related
* [PATCH v8 12/14] wifi: ath12k: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch ath12k client driver over to generic PAS TZ APIs. Generic PAS TZ
service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Acked-by: Jeff Johnson <jjohnson@kernel.org>
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/Kconfig | 2 +-
drivers/net/wireless/ath/ath12k/ahb.c | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig
index 4a2b240f967a..0d5d1c55bfc1 100644
--- a/drivers/net/wireless/ath/ath12k/Kconfig
+++ b/drivers/net/wireless/ath/ath12k/Kconfig
@@ -18,7 +18,7 @@ config ATH12K_AHB
bool "Qualcomm ath12k AHB support"
depends on ATH12K && REMOTEPROC
select QCOM_MDT_LOADER
- select QCOM_SCM
+ select QCOM_PAS
help
Enable support for Ath12k AHB bus chipsets, example IPQ5332.
diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
index 30733a244454..d9717ea999d7 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.c
+++ b/drivers/net/wireless/ath/ath12k/ahb.c
@@ -5,7 +5,7 @@
*/
#include <linux/dma-mapping.h>
-#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -420,7 +420,7 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab)
if (ab_ahb->scm_auth_enabled) {
/* Authenticate FW image using peripheral ID */
- ret = qcom_scm_pas_auth_and_reset(pasid);
+ ret = qcom_pas_auth_and_reset(pasid);
if (ret) {
ath12k_err(ab, "failed to boot the remote processor %d\n", ret);
goto err_fw2;
@@ -485,10 +485,10 @@ static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend)
pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) |
ATH12K_AHB_UPD_SWID;
/* Release the firmware */
- ret = qcom_scm_pas_shutdown(pasid);
+ ret = qcom_pas_shutdown(pasid);
if (ret)
- ath12k_err(ab, "scm pas shutdown failed for userPD%d\n",
- ab_ahb->userpd_id);
+ ath12k_err(ab, "pas shutdown failed for userPD%d: %d\n",
+ ab_ahb->userpd_id, ret);
}
}
--
2.53.0
^ permalink raw reply related
* [PATCH v8 11/14] net: ipa: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Alex Elder
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch ipa client driver over to generic PAS TZ APIs. Generic PAS TZ
service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Reviewed-by: Alex Elder <elder@riscstar.com>
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/net/ipa/Kconfig | 2 +-
drivers/net/ipa/ipa_main.c | 13 ++++++++-----
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ipa/Kconfig b/drivers/net/ipa/Kconfig
index 01d219d3760c..a9aff1b7977d 100644
--- a/drivers/net/ipa/Kconfig
+++ b/drivers/net/ipa/Kconfig
@@ -6,7 +6,7 @@ config QCOM_IPA
depends on QCOM_RPROC_COMMON || (QCOM_RPROC_COMMON=n && COMPILE_TEST)
depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
select QCOM_MDT_LOADER
- select QCOM_SCM
+ select QCOM_PAS
select QCOM_QMI_HELPERS
help
Choose Y or M here to include support for the Qualcomm
diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
index 788dd99af2a4..3cd9e44680e9 100644
--- a/drivers/net/ipa/ipa_main.c
+++ b/drivers/net/ipa/ipa_main.c
@@ -14,7 +14,7 @@
#include <linux/pm_runtime.h>
#include <linux/types.h>
-#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "ipa.h"
@@ -624,10 +624,13 @@ static int ipa_firmware_load(struct device *dev)
}
ret = qcom_mdt_load(dev, fw, path, IPA_PAS_ID, virt, phys, size, NULL);
- if (ret)
+ if (ret) {
dev_err(dev, "error %d loading \"%s\"\n", ret, path);
- else if ((ret = qcom_scm_pas_auth_and_reset(IPA_PAS_ID)))
- dev_err(dev, "error %d authenticating \"%s\"\n", ret, path);
+ } else {
+ ret = qcom_pas_auth_and_reset(IPA_PAS_ID);
+ if (ret)
+ dev_err(dev, "error %d authenticating \"%s\"\n", ret, path);
+ }
memunmap(virt);
out_release_firmware:
@@ -758,7 +761,7 @@ static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev)
return IPA_LOADER_INVALID;
out_self:
/* We need Trust Zone to load firmware; make sure it's available */
- if (qcom_scm_is_available())
+ if (qcom_pas_is_available())
return IPA_LOADER_SELF;
return IPA_LOADER_DEFER;
--
2.53.0
^ permalink raw reply related
* [PATCH v8 10/14] media: qcom: Pass proper PAS ID to set_remote_state API
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
As per testing the SCM backend just ignores it while OP-TEE makes
use of it to for proper book keeping purpose.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Reviewed-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/media/platform/qcom/iris/iris_firmware.c | 2 +-
drivers/media/platform/qcom/venus/firmware.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index ea9654dd679e..d2e7ba4f37e3 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -110,5 +110,5 @@ int iris_fw_unload(struct iris_core *core)
int iris_set_hw_state(struct iris_core *core, bool resume)
{
- return qcom_pas_set_remote_state(resume, 0);
+ return qcom_pas_set_remote_state(resume, IRIS_PAS_ID);
}
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 3a38ff985822..3c0727ea137d 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -59,7 +59,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume)
int ret;
if (core->use_tz) {
- ret = qcom_pas_set_remote_state(resume, 0);
+ ret = qcom_pas_set_remote_state(resume, VENUS_PAS_ID);
if (resume && ret == -EINVAL)
ret = 0;
return ret;
--
2.53.0
^ permalink raw reply related
* [PATCH v8 09/14] media: qcom: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch qcom media client drivers over to generic PAS TZ APIs. Generic PAS
TZ service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/media/platform/qcom/iris/Kconfig | 27 ++++++++++---------
.../media/platform/qcom/iris/iris_firmware.c | 9 ++++---
drivers/media/platform/qcom/venus/Kconfig | 1 +
drivers/media/platform/qcom/venus/firmware.c | 11 ++++----
4 files changed, 26 insertions(+), 22 deletions(-)
diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig
index af78a1775937..388c9bbc8136 100644
--- a/drivers/media/platform/qcom/iris/Kconfig
+++ b/drivers/media/platform/qcom/iris/Kconfig
@@ -1,14 +1,15 @@
config VIDEO_QCOM_IRIS
- tristate "Qualcomm iris V4L2 decoder driver"
- depends on VIDEO_DEV
- depends on ARCH_QCOM || COMPILE_TEST
- select V4L2_MEM2MEM_DEV
- select QCOM_MDT_LOADER
- select QCOM_SCM
- select QCOM_UBWC_CONFIG
- select VIDEOBUF2_DMA_CONTIG
- help
- This is a V4L2 driver for Qualcomm iris video accelerator
- hardware. It accelerates decoding operations on various
- Qualcomm SoCs.
- To compile this driver as a module choose m here.
+ tristate "Qualcomm iris V4L2 decoder driver"
+ depends on VIDEO_DEV
+ depends on ARCH_QCOM || COMPILE_TEST
+ select V4L2_MEM2MEM_DEV
+ select QCOM_MDT_LOADER
+ select QCOM_SCM
+ select QCOM_PAS
+ select QCOM_UBWC_CONFIG
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ This is a V4L2 driver for Qualcomm iris video accelerator
+ hardware. It accelerates decoding operations on various
+ Qualcomm SoCs.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index 1a476146d758..ea9654dd679e 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -4,6 +4,7 @@
*/
#include <linux/firmware.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
@@ -80,7 +81,7 @@ int iris_fw_load(struct iris_core *core)
return -ENOMEM;
}
- ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID);
+ ret = qcom_pas_auth_and_reset(IRIS_PAS_ID);
if (ret) {
dev_err(core->dev, "auth and reset failed: %d\n", ret);
return ret;
@@ -94,7 +95,7 @@ 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(IRIS_PAS_ID);
+ qcom_pas_shutdown(IRIS_PAS_ID);
return ret;
}
}
@@ -104,10 +105,10 @@ int iris_fw_load(struct iris_core *core)
int iris_fw_unload(struct iris_core *core)
{
- return qcom_scm_pas_shutdown(IRIS_PAS_ID);
+ return qcom_pas_shutdown(IRIS_PAS_ID);
}
int iris_set_hw_state(struct iris_core *core, bool resume)
{
- return qcom_scm_set_remote_state(resume, 0);
+ return qcom_pas_set_remote_state(resume, 0);
}
diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig
index 63ee8c78dc6d..7997b8aa427a 100644
--- a/drivers/media/platform/qcom/venus/Kconfig
+++ b/drivers/media/platform/qcom/venus/Kconfig
@@ -6,6 +6,7 @@ config VIDEO_QCOM_VENUS
select OF_DYNAMIC if ARCH_QCOM
select QCOM_MDT_LOADER
select QCOM_SCM
+ select QCOM_PAS
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 1de7436713ed..3a38ff985822 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -12,6 +12,7 @@
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/sizes.h>
#include <linux/soc/qcom/mdt_loader.h>
@@ -58,7 +59,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume)
int ret;
if (core->use_tz) {
- ret = qcom_scm_set_remote_state(resume, 0);
+ ret = qcom_pas_set_remote_state(resume, 0);
if (resume && ret == -EINVAL)
ret = 0;
return ret;
@@ -218,7 +219,7 @@ int venus_boot(struct venus_core *core)
int ret;
if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
- (core->use_tz && !qcom_scm_is_available()))
+ (core->use_tz && !qcom_pas_is_available()))
return -EPROBE_DEFER;
ret = of_property_read_string_index(dev->of_node, "firmware-name", 0,
@@ -236,7 +237,7 @@ int venus_boot(struct venus_core *core)
core->fw.mem_phys = mem_phys;
if (core->use_tz)
- ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
+ ret = qcom_pas_auth_and_reset(VENUS_PAS_ID);
else
ret = venus_boot_no_tz(core, mem_phys, mem_size);
@@ -259,7 +260,7 @@ int venus_boot(struct venus_core *core)
res->cp_nonpixel_start,
res->cp_nonpixel_size);
if (ret) {
- qcom_scm_pas_shutdown(VENUS_PAS_ID);
+ qcom_pas_shutdown(VENUS_PAS_ID);
dev_err(dev, "set virtual address ranges fail (%d)\n",
ret);
return ret;
@@ -274,7 +275,7 @@ int venus_shutdown(struct venus_core *core)
int ret;
if (core->use_tz)
- ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
+ ret = qcom_pas_shutdown(VENUS_PAS_ID);
else
ret = venus_shutdown_no_tz(core);
--
2.53.0
^ permalink raw reply related
* [PATCH v8 08/14] drm/msm: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Dmitry Baryshkov
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch drm/msm client drivers over to generic PAS TZ APIs. Generic PAS
TZ service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Acked-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/gpu/drm/msm/Kconfig | 1 +
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 ++--
drivers/gpu/drm/msm/adreno/adreno_gpu.c | 11 ++++++-----
3 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 250246f81ea9..09469d56513b 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -21,6 +21,7 @@ config DRM_MSM
select SHMEM
select TMPFS
select QCOM_SCM
+ select QCOM_PAS
select QCOM_UBWC_CONFIG
select WANT_DEV_COREDUMP
select SND_SOC_HDMI_CODEC if SND_SOC
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 2c0bbac43c52..57135acc8e2c 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -5,7 +5,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/cpumask.h>
-#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/pm_opp.h>
#include <linux/nvmem-consumer.h>
#include <linux/slab.h>
@@ -653,7 +653,7 @@ static int a5xx_zap_shader_resume(struct msm_gpu *gpu)
if (adreno_is_a506(adreno_gpu))
return 0;
- ret = qcom_scm_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID);
+ ret = qcom_pas_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID);
if (ret)
DRM_ERROR("%s: zap-shader resume failed: %d\n",
gpu->name, ret);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index c62c45bb0ddb..c36382b23310 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -8,6 +8,7 @@
#include <linux/ascii85.h>
#include <linux/interconnect.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/kernel.h>
#include <linux/of_reserved_mem.h>
@@ -146,10 +147,10 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname,
goto out;
/* Send the image to the secure world */
- ret = qcom_scm_pas_auth_and_reset(pasid);
+ ret = qcom_pas_auth_and_reset(pasid);
/*
- * If the scm call returns -EOPNOTSUPP we assume that this target
+ * If the pas call returns -EOPNOTSUPP we assume that this target
* doesn't need/support the zap shader so quietly fail
*/
if (ret == -EOPNOTSUPP)
@@ -175,9 +176,9 @@ int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid)
if (!zap_available)
return -ENODEV;
- /* We need SCM to be able to load the firmware */
- if (!qcom_scm_is_available()) {
- DRM_DEV_ERROR(&pdev->dev, "SCM is not available\n");
+ /* We need PAS to be able to load the firmware */
+ if (!qcom_pas_is_available()) {
+ DRM_DEV_ERROR(&pdev->dev, "Qcom PAS is not available\n");
return -EPROBE_DEFER;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v8 07/14] remoteproc: qcom: Select QCOM_PAS generic service
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Select PAS generic service driver to enable support for multiple PAS
backends like OP-TEE in addition to SCM.
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/remoteproc/Kconfig | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index c521c744e7db..65befdbfa5f7 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -210,6 +210,7 @@ config QCOM_Q6V5_MSS
select QCOM_Q6V5_COMMON
select QCOM_RPROC_COMMON
select QCOM_SCM
+ select QCOM_PAS
help
Say y here to support the Qualcomm self-authenticating modem
subsystem based on Hexagon V5. The TrustZone based system is
@@ -230,6 +231,7 @@ config QCOM_Q6V5_PAS
select QCOM_Q6V5_COMMON
select QCOM_RPROC_COMMON
select QCOM_SCM
+ select QCOM_PAS
help
Say y here to support the TrustZone based Peripheral Image Loader for
the Qualcomm remote processors. This is commonly used to control
@@ -282,7 +284,7 @@ config QCOM_WCNSS_PIL
select QCOM_MDT_LOADER
select QCOM_PIL_INFO
select QCOM_RPROC_COMMON
- select QCOM_SCM
+ select QCOM_PAS
help
Say y here to support the Peripheral Image Loader for loading WCNSS
firmware and boot the core on e.g. MSM8974, MSM8916. The firmware is
--
2.53.0
^ permalink raw reply related
* [PATCH v8 06/14] remoteproc: qcom_wcnss: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch qcom_wcnss client driver over to generic PAS TZ APIs. Generic PAS
TZ service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/remoteproc/qcom_wcnss.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index 4add9037dbd5..0dbdd18ab3dd 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -19,7 +19,7 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
-#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc.h>
#include <linux/soc/qcom/mdt_loader.h>
@@ -257,7 +257,7 @@ static int wcnss_start(struct rproc *rproc)
wcnss_indicate_nv_download(wcnss);
wcnss_configure_iris(wcnss);
- ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID);
+ ret = qcom_pas_auth_and_reset(WCNSS_PAS_ID);
if (ret) {
dev_err(wcnss->dev,
"failed to authenticate image and release reset\n");
@@ -269,7 +269,7 @@ static int wcnss_start(struct rproc *rproc)
if (wcnss->ready_irq > 0 && ret == 0) {
/* We have a ready_irq, but it didn't fire in time. */
dev_err(wcnss->dev, "start timed out\n");
- qcom_scm_pas_shutdown(WCNSS_PAS_ID);
+ qcom_pas_shutdown(WCNSS_PAS_ID);
ret = -ETIMEDOUT;
goto disable_iris;
}
@@ -311,7 +311,7 @@ static int wcnss_stop(struct rproc *rproc)
0);
}
- ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID);
+ ret = qcom_pas_shutdown(WCNSS_PAS_ID);
if (ret)
dev_err(wcnss->dev, "failed to shutdown: %d\n", ret);
@@ -557,10 +557,10 @@ static int wcnss_probe(struct platform_device *pdev)
data = of_device_get_match_data(&pdev->dev);
- if (!qcom_scm_is_available())
+ if (!qcom_pas_is_available())
return -EPROBE_DEFER;
- if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) {
+ if (!qcom_pas_supported(WCNSS_PAS_ID)) {
dev_err(&pdev->dev, "PAS is not available for WCNSS\n");
return -ENXIO;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v8 05/14] remoteproc: qcom_q6v5_mss: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch qcom_q6v5_mss client driver over to generic PAS TZ APIs. Generic PAS
TZ service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/remoteproc/qcom_q6v5_mss.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index ae78f5c7c1b6..96888007faa8 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -34,6 +34,7 @@
#include "qcom_pil_info.h"
#include "qcom_q6v5.h"
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#define MPSS_CRASH_REASON_SMEM 421
@@ -1480,7 +1481,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
}
if (qproc->need_pas_mem_setup) {
- ret = qcom_scm_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size);
+ ret = qcom_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size);
if (ret) {
dev_err(qproc->dev,
"setting up mpss memory failed: %d\n", ret);
@@ -2077,7 +2078,7 @@ static int q6v5_probe(struct platform_device *pdev)
if (!desc)
return -EINVAL;
- if (desc->need_mem_protection && !qcom_scm_is_available())
+ if (desc->need_mem_protection && !qcom_pas_is_available())
return -EPROBE_DEFER;
mba_image = desc->hexagon_mba_image;
--
2.53.0
^ permalink raw reply related
* [PATCH v8 04/14] remoteproc: qcom_q6v5_pas: Switch over to generic PAS TZ APIs
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Switch qcom_q6v5_pas client driver over to generic PAS TZ APIs. Generic PAS
TZ service allows to support multiple TZ implementation backends like QTEE
based SCM PAS service, OP-TEE based PAS service and any further future TZ
backend service.
Since qcom_q6v5_pas depends on MDT loader for PAS firmware loading, it
has to be switched over to generic PAS APIs in this commit to avoid any
build issues.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/remoteproc/qcom_q6v5_pas.c | 51 +++++++++++++++--------------
drivers/soc/qcom/mdt_loader.c | 12 +++----
include/linux/soc/qcom/mdt_loader.h | 6 ++--
3 files changed, 35 insertions(+), 34 deletions(-)
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index 808e9609988d..9eb1c4f6c2ab 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc.h>
@@ -115,8 +116,8 @@ struct qcom_pas {
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
- struct qcom_scm_pas_context *pas_ctx;
- struct qcom_scm_pas_context *dtb_pas_ctx;
+ struct qcom_pas_context *pas_ctx;
+ struct qcom_pas_context *dtb_pas_ctx;
};
static void qcom_pas_segment_dump(struct rproc *rproc,
@@ -193,7 +194,7 @@ static int qcom_pas_shutdown_poll_decrypt(struct qcom_pas *pas)
do {
msleep(QCOM_PAS_DECRYPT_SHUTDOWN_DELAY_MS);
- ret = qcom_scm_pas_shutdown(pas->pas_id);
+ ret = qcom_pas_shutdown(pas->pas_id);
} while (ret == -EINVAL && --retry_num);
return ret;
@@ -209,9 +210,9 @@ static int qcom_pas_unprepare(struct rproc *rproc)
* auth_and_reset() was successful, but in other cases clean it up
* here.
*/
- qcom_scm_pas_metadata_release(pas->pas_ctx);
+ qcom_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+ qcom_pas_metadata_release(pas->dtb_pas_ctx);
return 0;
}
@@ -225,9 +226,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
pas->firmware = fw;
if (pas->lite_pas_id)
- qcom_scm_pas_shutdown(pas->lite_pas_id);
+ qcom_pas_shutdown(pas->lite_pas_id);
if (pas->lite_dtb_pas_id)
- qcom_scm_pas_shutdown(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);
@@ -247,7 +248,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return 0;
release_dtb_metadata:
- qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+ qcom_pas_metadata_release(pas->dtb_pas_ctx);
release_firmware(pas->dtb_firmware);
return ret;
@@ -307,7 +308,7 @@ static int qcom_pas_start(struct rproc *rproc)
if (ret)
goto disable_px_supply;
- ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
+ ret = qcom_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate dtb image and release reset\n");
@@ -326,7 +327,7 @@ static int qcom_pas_start(struct rproc *rproc)
if (ret)
goto release_pas_metadata;
- ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx);
+ ret = qcom_pas_prepare_and_auth_reset(pas->pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate image and release reset\n");
@@ -336,13 +337,13 @@ static int qcom_pas_start(struct rproc *rproc)
ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000));
if (ret == -ETIMEDOUT) {
dev_err(pas->dev, "start timed out\n");
- qcom_scm_pas_shutdown(pas->pas_id);
+ qcom_pas_shutdown(pas->pas_id);
goto unmap_carveout;
}
- qcom_scm_pas_metadata_release(pas->pas_ctx);
+ qcom_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+ qcom_pas_metadata_release(pas->dtb_pas_ctx);
/* firmware is used to pass reference from qcom_pas_start(), drop it now */
pas->firmware = NULL;
@@ -352,9 +353,9 @@ static int qcom_pas_start(struct rproc *rproc)
unmap_carveout:
qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
release_pas_metadata:
- qcom_scm_pas_metadata_release(pas->pas_ctx);
+ qcom_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+ qcom_pas_metadata_release(pas->dtb_pas_ctx);
unmap_dtb_carveout:
if (pas->dtb_pas_id)
@@ -403,7 +404,7 @@ static int qcom_pas_stop(struct rproc *rproc)
if (ret == -ETIMEDOUT)
dev_err(pas->dev, "timed out on wait\n");
- ret = qcom_scm_pas_shutdown(pas->pas_id);
+ ret = qcom_pas_shutdown(pas->pas_id);
if (ret && pas->decrypt_shutdown)
ret = qcom_pas_shutdown_poll_decrypt(pas);
@@ -411,7 +412,7 @@ static int qcom_pas_stop(struct rproc *rproc)
dev_err(pas->dev, "failed to shutdown: %d\n", ret);
if (pas->dtb_pas_id) {
- ret = qcom_scm_pas_shutdown(pas->dtb_pas_id);
+ ret = qcom_pas_shutdown(pas->dtb_pas_id);
if (ret)
dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret);
@@ -481,11 +482,11 @@ static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *f
*
* Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources
* or not and if it is not having then we pass NULL and zero as input resource
- * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table()
+ * table pointer and size respectively to the argument of qcom_pas_get_rsc_table()
* and this is even true for Qualcomm remote processor who does follow remoteproc
* framework.
*/
- output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size);
+ output_rt = qcom_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size);
ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0;
if (ret) {
dev_err(pas->dev, "Error in getting resource table: %d\n", ret);
@@ -743,7 +744,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
if (!desc)
return -EINVAL;
- if (!qcom_scm_is_available())
+ if (!qcom_pas_is_available())
return -EPROBE_DEFER;
fw_name = desc->firmware_name;
@@ -835,16 +836,16 @@ static int qcom_pas_probe(struct platform_device *pdev)
qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name);
- pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id,
- pas->mem_phys, pas->mem_size);
+ pas->pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->pas_id,
+ pas->mem_phys, pas->mem_size);
if (IS_ERR(pas->pas_ctx)) {
ret = PTR_ERR(pas->pas_ctx);
goto remove_ssr_sysmon;
}
- pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id,
- pas->dtb_mem_phys,
- pas->dtb_mem_size);
+ pas->dtb_pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->dtb_pas_id,
+ pas->dtb_mem_phys,
+ pas->dtb_mem_size);
if (IS_ERR(pas->dtb_pas_ctx)) {
ret = PTR_ERR(pas->dtb_pas_ctx);
goto remove_ssr_sysmon;
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index c004d444d698..137992456b71 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -13,7 +13,7 @@
#include <linux/firmware.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
@@ -229,7 +229,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
const char *fw_name, int pas_id, phys_addr_t mem_phys,
- struct qcom_scm_pas_context *ctx)
+ struct qcom_pas_context *ctx)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
@@ -271,7 +271,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
goto out;
}
- ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx);
+ ret = qcom_pas_init_image(pas_id, metadata, metadata_len, ctx);
kfree(metadata);
if (ret) {
/* Invalid firmware metadata */
@@ -280,7 +280,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
}
if (relocate) {
- ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr);
+ ret = qcom_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr);
if (ret) {
/* Unable to set up relocation */
dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name);
@@ -472,7 +472,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load);
* firmware segments (e.g., .bXX files). Authentication of the segments done
* by a separate call.
*
- * The PAS context must be initialized using qcom_scm_pas_context_init()
+ * The PAS context must be initialized using devm_qcom_pas_context_alloc()
* prior to invoking this function.
*
* @ctx: Pointer to the PAS (Peripheral Authentication Service) context
@@ -483,7 +483,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load);
*
* Return: 0 on success or a negative error code on failure.
*/
-int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
+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)
{
int ret;
diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h
index 82372e0db0a1..142409555425 100644
--- a/include/linux/soc/qcom/mdt_loader.h
+++ b/include/linux/soc/qcom/mdt_loader.h
@@ -10,7 +10,7 @@
struct device;
struct firmware;
-struct qcom_scm_pas_context;
+struct qcom_pas_context;
#if IS_ENABLED(CONFIG_QCOM_MDT_LOADER)
@@ -20,7 +20,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
phys_addr_t mem_phys, size_t mem_size,
phys_addr_t *reloc_base);
-int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
+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);
int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw,
@@ -45,7 +45,7 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw,
return -ENODEV;
}
-static inline int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx,
+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)
{
--
2.53.0
^ permalink raw reply related
* [PATCH v8 03/14] firmware: qcom: Add a PAS TEE service
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Harshal Dev
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Add support for Peripheral Authentication Service (PAS) driver based
on TEE bus with OP-TEE providing the backend PAS service implementation.
The TEE PAS service ABI is designed to be extensible with additional API
as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
extensions of the PAS service needed while still maintaining backwards
compatibility.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Reviewed-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/firmware/qcom/Kconfig | 10 +
drivers/firmware/qcom/Makefile | 1 +
drivers/firmware/qcom/qcom_pas_tee.c | 477 +++++++++++++++++++++++++++
3 files changed, 488 insertions(+)
create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index 732a0bff7d9f..b851bcc592be 100644
--- a/drivers/firmware/qcom/Kconfig
+++ b/drivers/firmware/qcom/Kconfig
@@ -14,6 +14,16 @@ config QCOM_PAS
backends plugged in whether it's an SCM implementation or a proper
TEE bus based PAS service implementation.
+config QCOM_PAS_TEE
+ tristate "Qualcomm PAS TEE interface driver"
+ select QCOM_PAS
+ depends on TEE
+ depends on !CPU_BIG_ENDIAN
+ default m if ARCH_QCOM
+ help
+ Enable the generic Peripheral Authentication Service (PAS) provided
+ by the firmware TEE implementation as the backend.
+
config QCOM_SCM
tristate "Qualcomm PAS SCM interface driver"
select QCOM_PAS
diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
index dc5ab45f906a..48801d18f37b 100644
--- a/drivers/firmware/qcom/Makefile
+++ b/drivers/firmware/qcom/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o
obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o
obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
obj-$(CONFIG_QCOM_PAS) += qcom_pas.o
+obj-$(CONFIG_QCOM_PAS_TEE) += qcom_pas_tee.o
diff --git a/drivers/firmware/qcom/qcom_pas_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c
new file mode 100644
index 000000000000..a2ba3af05a50
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_pas_tee.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/firmware/qcom/qcom_pas.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+
+#include "qcom_pas.h"
+
+/*
+ * Peripheral Authentication Service (PAS) supported.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ */
+#define TA_QCOM_PAS_IS_SUPPORTED 1
+
+/*
+ * PAS capabilities.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ * [out] params[1].value.a: PAS capability flags
+ */
+#define TA_QCOM_PAS_CAPABILITIES 2
+
+/*
+ * PAS image initialization.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ * [in] params[1].memref: Loadable firmware metadata
+ */
+#define TA_QCOM_PAS_INIT_IMAGE 3
+
+/*
+ * PAS memory setup.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ * [in] params[0].value.b: Relocatable firmware size
+ * [in] params[1].value.a: 32bit LSB relocatable firmware memory address
+ * [in] params[1].value.b: 32bit MSB relocatable firmware memory address
+ */
+#define TA_QCOM_PAS_MEM_SETUP 4
+
+/*
+ * PAS get resource table.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ * [inout] params[1].memref: Resource table config
+ */
+#define TA_QCOM_PAS_GET_RESOURCE_TABLE 5
+
+/*
+ * PAS image authentication and co-processor reset.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ * [in] params[0].value.b: Firmware size
+ * [in] params[1].value.a: 32bit LSB firmware memory address
+ * [in] params[1].value.b: 32bit MSB firmware memory address
+ * [in] params[2].memref: Optional fw memory space shared/lent
+ */
+#define TA_QCOM_PAS_AUTH_AND_RESET 6
+
+/*
+ * PAS co-processor set suspend/resume state.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ * [in] params[0].value.b: Co-processor state identifier
+ */
+#define TA_QCOM_PAS_SET_REMOTE_STATE 7
+
+/*
+ * PAS co-processor shutdown.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ */
+#define TA_QCOM_PAS_SHUTDOWN 8
+
+#define TEE_NUM_PARAMS 4
+
+/**
+ * struct qcom_pas_tee_private - PAS service private data
+ * @dev: PAS service device.
+ * @ctx: TEE context handler.
+ * @session_id: PAS TA session identifier.
+ */
+struct qcom_pas_tee_private {
+ struct device *dev;
+ struct tee_context *ctx;
+ u32 session_id;
+};
+
+static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_IS_SUPPORTED,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = pas_id
+ }
+ };
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS not supported, pas_id: %d, ret: %d, err: 0x%x\n",
+ pas_id, ret, inv_arg.ret);
+ return false;
+ }
+
+ return true;
+}
+
+static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id,
+ const void *metadata, size_t size,
+ struct qcom_pas_context *ctx)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_INIT_IMAGE,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = pas_id
+ },
+ [1] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
+ }
+ };
+ struct tee_shm *mdata_shm;
+ u8 *mdata_buf = NULL;
+ int ret;
+
+ mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size);
+ if (IS_ERR(mdata_shm)) {
+ dev_err(dev, "mdata_shm allocation failed\n");
+ return PTR_ERR(mdata_shm);
+ }
+
+ mdata_buf = tee_shm_get_va(mdata_shm, 0);
+ if (IS_ERR(mdata_buf)) {
+ dev_err(dev, "mdata_buf get VA failed\n");
+ tee_shm_free(mdata_shm);
+ return PTR_ERR(mdata_buf);
+ }
+ memcpy(mdata_buf, metadata, size);
+
+ param[1].u.memref.shm = mdata_shm;
+ param[1].u.memref.size = size;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS init image failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ pas_id, ret, inv_arg.ret);
+ tee_shm_free(mdata_shm);
+ return ret ?: -EINVAL;
+ }
+
+ if (ctx)
+ ctx->ptr = (void *)mdata_shm;
+ else
+ tee_shm_free(mdata_shm);
+
+ return ret;
+}
+
+static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id,
+ phys_addr_t addr, phys_addr_t size)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_MEM_SETUP,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = pas_id,
+ .u.value.b = size,
+ },
+ [1] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = lower_32_bits(addr),
+ .u.value.b = upper_32_bits(addr),
+ }
+ };
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS mem setup failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ pas_id, ret, inv_arg.ret);
+ return ret ?: -EINVAL;
+ }
+
+ return ret;
+}
+
+DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T))
+
+static void *qcom_pas_tee_get_rsc_table(struct device *dev,
+ struct qcom_pas_context *ctx,
+ void *input_rt, size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_GET_RESOURCE_TABLE,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = ctx->pas_id,
+ },
+ [1] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
+ .u.memref.size = input_rt_size,
+ }
+ };
+ void *rt_buf = NULL;
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ ctx->pas_id, ret, inv_arg.ret);
+ return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL);
+ }
+
+ if (param[1].u.memref.size) {
+ struct tee_shm *rt_shm __free(shm_free) =
+ tee_shm_alloc_kernel_buf(data->ctx,
+ param[1].u.memref.size);
+ void *rt_shm_va;
+
+ if (IS_ERR_OR_NULL(rt_shm)) {
+ dev_err(dev, "rt_shm allocation failed\n");
+ rt_shm = NULL;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rt_shm_va = tee_shm_get_va(rt_shm, 0);
+ if (IS_ERR(rt_shm_va)) {
+ dev_err(dev, "rt_shm get VA failed\n");
+ return ERR_CAST(rt_shm_va);
+ }
+ memcpy(rt_shm_va, input_rt, input_rt_size);
+
+ param[1].u.memref.shm = rt_shm;
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ ctx->pas_id, ret, inv_arg.ret);
+ return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL);
+ }
+
+ if (param[1].u.memref.size) {
+ *output_rt_size = param[1].u.memref.size;
+ rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL);
+ if (!rt_buf)
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ return rt_buf;
+}
+
+static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id,
+ phys_addr_t mem_phys, size_t mem_size)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_AUTH_AND_RESET,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = pas_id,
+ .u.value.b = mem_size,
+ },
+ [1] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = lower_32_bits(mem_phys),
+ .u.value.b = upper_32_bits(mem_phys),
+ },
+ /* Reserved for fw memory space to be shared or lent */
+ [2] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
+ }
+ };
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS auth reset failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ pas_id, ret, inv_arg.ret);
+ return ret ?: -EINVAL;
+ }
+
+ return ret;
+}
+
+static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id)
+{
+ return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0);
+}
+
+static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev,
+ struct qcom_pas_context *ctx)
+{
+ return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys,
+ ctx->mem_size);
+}
+
+static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state,
+ u32 pas_id)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_SET_REMOTE_STATE,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = pas_id,
+ .u.value.b = state,
+ }
+ };
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS set remote state failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ pas_id, ret, inv_arg.ret);
+ return ret ?: -EINVAL;
+ }
+
+ return ret;
+}
+
+static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id)
+{
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = TA_QCOM_PAS_SHUTDOWN,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS
+ };
+ struct tee_param param[4] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = pas_id
+ }
+ };
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev, "PAS shutdown failed, pas_id: %d, ret: %d, err: 0x%x\n",
+ pas_id, ret, inv_arg.ret);
+ return ret ?: -EINVAL;
+ }
+
+ return ret;
+}
+
+static void qcom_pas_tee_metadata_release(struct device *dev,
+ struct qcom_pas_context *ctx)
+{
+ struct tee_shm *mdata_shm = ctx->ptr;
+
+ tee_shm_free(mdata_shm);
+ ctx->ptr = NULL;
+}
+
+static struct qcom_pas_ops qcom_pas_ops_tee = {
+ .drv_name = "qcom-pas-tee",
+ .supported = qcom_pas_tee_supported,
+ .init_image = qcom_pas_tee_init_image,
+ .mem_setup = qcom_pas_tee_mem_setup,
+ .get_rsc_table = qcom_pas_tee_get_rsc_table,
+ .auth_and_reset = qcom_pas_tee_auth_and_reset,
+ .prepare_and_auth_reset = qcom_pas_tee_prepare_and_auth_reset,
+ .set_remote_state = qcom_pas_tee_set_remote_state,
+ .shutdown = qcom_pas_tee_shutdown,
+ .metadata_release = qcom_pas_tee_metadata_release,
+};
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ return ver->impl_id == TEE_IMPL_ID_OPTEE;
+}
+
+static int qcom_pas_tee_probe(struct tee_client_device *pas_dev)
+{
+ struct device *dev = &pas_dev->dev;
+ struct qcom_pas_tee_private *data;
+ struct tee_ioctl_open_session_arg sess_arg = {
+ .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL
+ };
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
+ if (IS_ERR(data->ctx))
+ return -ENODEV;
+
+ export_uuid(sess_arg.uuid, &pas_dev->id.uuid);
+ ret = tee_client_open_session(data->ctx, &sess_arg, NULL);
+ if (ret < 0 || sess_arg.ret != 0) {
+ dev_err(dev, "tee_client_open_session failed, ret: %d, err: 0x%x\n",
+ ret, sess_arg.ret);
+ tee_client_close_context(data->ctx);
+ return ret ?: -EINVAL;
+ }
+
+ data->session_id = sess_arg.session;
+ dev_set_drvdata(dev, data);
+ qcom_pas_ops_tee.dev = dev;
+ qcom_pas_ops_register(&qcom_pas_ops_tee);
+
+ return ret;
+}
+
+static void qcom_pas_tee_remove(struct tee_client_device *pas_dev)
+{
+ struct device *dev = &pas_dev->dev;
+ struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
+
+ qcom_pas_ops_unregister();
+ tee_client_close_session(data->ctx, data->session_id);
+ tee_client_close_context(data->ctx);
+}
+
+static const struct tee_client_device_id qcom_pas_tee_id_table[] = {
+ {UUID_INIT(0xcff7d191, 0x7ca0, 0x4784,
+ 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)},
+ {}
+};
+MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table);
+
+static struct tee_client_driver optee_pas_tee_driver = {
+ .probe = qcom_pas_tee_probe,
+ .remove = qcom_pas_tee_remove,
+ .id_table = qcom_pas_tee_id_table,
+ .driver = {
+ .name = "qcom-pas-tee",
+ },
+};
+
+module_tee_client_driver(optee_pas_tee_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm PAS TEE driver");
--
2.53.0
^ permalink raw reply related
* [PATCH v8 02/14] firmware: qcom_scm: Migrate to generic PAS service
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Harshal Dev
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
With the availability of generic PAS service, let's add SCM calls as
a backend to keep supporting legacy QTEE interfaces. The exported
qcom_scm* wrappers will get dropped once all the client drivers get
migrated as part of future patches.
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Reviewed-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/firmware/qcom/Kconfig | 3 +-
drivers/firmware/qcom/qcom_scm.c | 335 ++++++++++++++-----------------
2 files changed, 156 insertions(+), 182 deletions(-)
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index 9f66cc774508..732a0bff7d9f 100644
--- a/drivers/firmware/qcom/Kconfig
+++ b/drivers/firmware/qcom/Kconfig
@@ -15,8 +15,9 @@ config QCOM_PAS
TEE bus based PAS service implementation.
config QCOM_SCM
+ tristate "Qualcomm PAS SCM interface driver"
+ select QCOM_PAS
select QCOM_TZMEM
- tristate
config QCOM_TZMEM
tristate
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 6b601a4b89db..7933e55803dc 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -13,6 +13,7 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/firmware/qcom/qcom_tzmem.h>
#include <linux/init.h>
@@ -33,6 +34,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "qcom_pas.h"
#include "qcom_scm.h"
#include "qcom_tzmem.h"
@@ -479,25 +481,6 @@ void qcom_scm_cpu_power_down(u32 flags)
}
EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down);
-int qcom_scm_set_remote_state(u32 state, u32 id)
-{
- struct qcom_scm_desc desc = {
- .svc = QCOM_SCM_SVC_BOOT,
- .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
- .arginfo = QCOM_SCM_ARGS(2),
- .args[0] = state,
- .args[1] = id,
- .owner = ARM_SMCCC_OWNER_SIP,
- };
- struct qcom_scm_res res;
- int ret;
-
- ret = qcom_scm_call(__scm->dev, &desc, &res);
-
- return ret ? : res.result[0];
-}
-EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
-
static int qcom_scm_disable_sdi(void)
{
int ret;
@@ -570,26 +553,12 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
}
-/**
- * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service
- * context for a given peripheral
- *
- * PAS context is device-resource managed, so the caller does not need
- * to worry about freeing the context memory.
- *
- * @dev: PAS firmware device
- * @pas_id: peripheral authentication service id
- * @mem_phys: Subsystem reserve memory start address
- * @mem_size: Subsystem reserve memory size
- *
- * Returns: The new PAS context, or ERR_PTR() on failure.
- */
struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
u32 pas_id,
phys_addr_t mem_phys,
size_t mem_size)
{
- struct qcom_scm_pas_context *ctx;
+ struct qcom_pas_context *ctx;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -600,11 +569,12 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
ctx->mem_phys = mem_phys;
ctx->mem_size = mem_size;
- return ctx;
+ return (struct qcom_scm_pas_context *)ctx;
}
EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
-static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
+static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id,
+ dma_addr_t mdata_phys,
struct qcom_scm_res *res)
{
struct qcom_scm_desc desc = {
@@ -626,7 +596,7 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
desc.args[1] = mdata_phys;
- ret = qcom_scm_call(__scm->dev, &desc, res);
+ ret = qcom_scm_call(dev, &desc, res);
qcom_scm_bw_disable();
disable_clk:
@@ -635,7 +605,8 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
return ret;
}
-static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
+static int qcom_scm_pas_prep_and_init_image(struct device *dev,
+ struct qcom_pas_context *ctx,
const void *metadata, size_t size)
{
struct qcom_scm_res res;
@@ -650,7 +621,7 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
memcpy(mdata_buf, metadata, size);
mdata_phys = qcom_tzmem_to_phys(mdata_buf);
- ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res);
+ ret = __qcom_scm_pas_init_image(dev, ctx->pas_id, mdata_phys, &res);
if (ret < 0)
qcom_tzmem_free(mdata_buf);
else
@@ -659,25 +630,9 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
return ret ? : res.result[0];
}
-/**
- * qcom_scm_pas_init_image() - Initialize peripheral authentication service
- * state machine for a given peripheral, using the
- * metadata
- * @pas_id: peripheral authentication service id
- * @metadata: pointer to memory containing ELF header, program header table
- * and optional blob of data used for authenticating the metadata
- * and the rest of the firmware
- * @size: size of the metadata
- * @ctx: optional pas context
- *
- * Return: 0 on success.
- *
- * Upon successful return, the PAS metadata context (@ctx) will be used to
- * track the metadata allocation, this needs to be released by invoking
- * qcom_scm_pas_metadata_release() by the caller.
- */
-int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
- struct qcom_scm_pas_context *ctx)
+static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id,
+ const void *metadata, size_t size,
+ struct qcom_pas_context *ctx)
{
struct qcom_scm_res res;
dma_addr_t mdata_phys;
@@ -685,7 +640,7 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
int ret;
if (ctx && ctx->use_tzmem)
- return qcom_scm_pas_prep_and_init_image(ctx, metadata, size);
+ return qcom_scm_pas_prep_and_init_image(dev, ctx, metadata, size);
/*
* During the scm call memory protection will be enabled for the meta
@@ -699,16 +654,15 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
* If we pass a buffer that is already part of an SHM Bridge to this
* call, it will fail.
*/
- mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
- GFP_KERNEL);
+ mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL);
if (!mdata_buf)
return -ENOMEM;
memcpy(mdata_buf, metadata, size);
- ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res);
+ ret = __qcom_scm_pas_init_image(dev, pas_id, mdata_phys, &res);
if (ret < 0 || !ctx) {
- dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
+ dma_free_coherent(dev, size, mdata_buf, mdata_phys);
} else if (ctx) {
ctx->ptr = mdata_buf;
ctx->phys = mdata_phys;
@@ -717,36 +671,35 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
return ret ? : res.result[0];
}
-EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
-/**
- * qcom_scm_pas_metadata_release() - release metadata context
- * @ctx: pas context
- */
-void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
+int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
+ struct qcom_scm_pas_context *ctx)
{
- if (!ctx->ptr)
- return;
+ return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size,
+ (struct qcom_pas_context *)ctx);
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
+static void __qcom_scm_pas_metadata_release(struct device *dev,
+ struct qcom_pas_context *ctx)
+{
if (ctx->use_tzmem)
qcom_tzmem_free(ctx->ptr);
else
- dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
+ dma_free_coherent(dev, ctx->size, ctx->ptr, ctx->phys);
ctx->ptr = NULL;
}
+
+void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
+{
+ __qcom_scm_pas_metadata_release(__scm->dev,
+ (struct qcom_pas_context *)ctx);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
-/**
- * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
- * for firmware loading
- * @pas_id: peripheral authentication service id
- * @addr: start address of memory area to prepare
- * @size: size of the memory area to prepare
- *
- * Returns 0 on success.
- */
-int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
+static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id,
+ phys_addr_t addr, phys_addr_t size)
{
int ret;
struct qcom_scm_desc desc = {
@@ -768,7 +721,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
if (ret)
goto disable_clk;
- ret = qcom_scm_call(__scm->dev, &desc, &res);
+ ret = qcom_scm_call(dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
@@ -776,9 +729,15 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
return ret ? : res.result[0];
}
+
+int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
+{
+ return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
-static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
+static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id,
+ void *input_rt_tzm,
size_t input_rt_size,
size_t *output_rt_size)
{
@@ -813,7 +772,7 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
* with output_rt_tzm buffer with res.result[2] size however, It should not
* be of unresonable size.
*/
- ret = qcom_scm_call(__scm->dev, &desc, &res);
+ ret = qcom_scm_call(dev, &desc, &res);
if (!ret && res.result[2] > SZ_1G) {
ret = -E2BIG;
goto free_output_rt;
@@ -830,51 +789,11 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
return ret ? ERR_PTR(ret) : output_rt_tzm;
}
-/**
- * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
- * for a given peripheral.
- *
- * Qualcomm remote processor may rely on both static and dynamic resources for
- * its functionality. Static resources typically refer to memory-mapped addresses
- * required by the subsystem and are often embedded within the firmware binary
- * and dynamic resources, such as shared memory in DDR etc., are determined at
- * runtime during the boot process.
- *
- * On Qualcomm Technologies devices, it's possible that static resources are not
- * embedded in the firmware binary and instead are provided by TrustZone However,
- * dynamic resources are always expected to come from TrustZone. This indicates
- * that for Qualcomm devices, all resources (static and dynamic) will be provided
- * by TrustZone via the SMC call.
- *
- * If the remote processor firmware binary does contain static resources, they
- * should be passed in input_rt. These will be forwarded to TrustZone for
- * authentication. TrustZone will then append the dynamic resources and return
- * the complete resource table in output_rt_tzm.
- *
- * If the remote processor firmware binary does not include a resource table,
- * the caller of this function should set input_rt as NULL and input_rt_size
- * as zero respectively.
- *
- * More about documentation on resource table data structures can be found in
- * include/linux/remoteproc.h
- *
- * @ctx: PAS context
- * @pas_id: peripheral authentication service id
- * @input_rt: resource table buffer which is present in firmware binary
- * @input_rt_size: size of the resource table present in firmware binary
- * @output_rt_size: TrustZone expects caller should pass worst case size for
- * the output_rt_tzm.
- *
- * Return:
- * On success, returns a pointer to the allocated buffer containing the final
- * resource table and output_rt_size will have actual resource table size from
- * TrustZone. The caller is responsible for freeing the buffer. On failure,
- * returns ERR_PTR(-errno).
- */
-struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
- void *input_rt,
- size_t input_rt_size,
- size_t *output_rt_size)
+static void *__qcom_scm_pas_get_rsc_table2(struct device *dev,
+ struct qcom_pas_context *ctx,
+ void *input_rt,
+ size_t input_rt_size,
+ size_t *output_rt_size)
{
struct resource_table empty_rsc = {};
size_t size = SZ_16K;
@@ -909,11 +828,12 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
memcpy(input_rt_tzm, input_rt, input_rt_size);
- output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
+ output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id,
+ input_rt_tzm,
input_rt_size, &size);
if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
/* Try again with the size requested by the TZ */
- output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
+ output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id,
input_rt_tzm,
input_rt_size,
&size);
@@ -943,16 +863,20 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
return ret ? ERR_PTR(ret) : tbl_ptr;
}
+
+struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
+ void *input_rt,
+ size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ return __qcom_scm_pas_get_rsc_table2(__scm->dev,
+ (struct qcom_pas_context *)ctx,
+ input_rt, input_rt_size,
+ output_rt_size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
-/**
- * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
- * and reset the remote processor
- * @pas_id: peripheral authentication service id
- *
- * Return 0 on success.
- */
-int qcom_scm_pas_auth_and_reset(u32 pas_id)
+static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
@@ -972,7 +896,7 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id)
if (ret)
goto disable_clk;
- ret = qcom_scm_call(__scm->dev, &desc, &res);
+ ret = qcom_scm_call(dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
@@ -980,28 +904,15 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id)
return ret ? : res.result[0];
}
+
+int qcom_scm_pas_auth_and_reset(u32 pas_id)
+{
+ return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
-/**
- * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
- * remote processor
- *
- * @ctx: Context saved during call to qcom_scm_pas_context_init()
- *
- * This function performs the necessary steps to prepare a PAS subsystem,
- * authenticate it using the provided metadata, and initiate a reset sequence.
- *
- * It should be used when Linux is in control setting up the IOMMU hardware
- * for remote subsystem during secure firmware loading processes. The preparation
- * step sets up a shmbridge over the firmware memory before TrustZone accesses the
- * firmware memory region for authentication. The authentication step verifies
- * the integrity and authenticity of the firmware or configuration using secure
- * metadata. Finally, the reset step ensures the subsystem starts in a clean and
- * sane state.
- *
- * Return: 0 on success, negative errno on failure.
- */
-int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
+static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
+ struct qcom_pas_context *ctx)
{
u64 handle;
int ret;
@@ -1012,7 +923,7 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
* memory region and then invokes a call to TrustZone to authenticate.
*/
if (!ctx->use_tzmem)
- return qcom_scm_pas_auth_and_reset(ctx->pas_id);
+ return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
/*
* When Linux runs @ EL2 Linux must create the shmbridge itself and then
@@ -1022,20 +933,45 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
if (ret)
return ret;
- ret = qcom_scm_pas_auth_and_reset(ctx->pas_id);
+ ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
qcom_tzmem_shm_bridge_delete(handle);
return ret;
}
+
+int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
+{
+ return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev,
+ (struct qcom_pas_context *)ctx);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
-/**
- * qcom_scm_pas_shutdown() - Shut down the remote processor
- * @pas_id: peripheral authentication service id
- *
- * Returns 0 on success.
- */
-int qcom_scm_pas_shutdown(u32 pas_id)
+static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state,
+ u32 pas_id)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_BOOT,
+ .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
+ .arginfo = QCOM_SCM_ARGS(2),
+ .args[0] = state,
+ .args[1] = pas_id,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ int ret;
+
+ ret = qcom_scm_call(dev, &desc, &res);
+
+ return ret ? : res.result[0];
+}
+
+int qcom_scm_set_remote_state(u32 state, u32 id)
+{
+ return __qcom_scm_pas_set_remote_state(__scm->dev, state, id);
+}
+EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
+
+static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
@@ -1055,7 +991,7 @@ int qcom_scm_pas_shutdown(u32 pas_id)
if (ret)
goto disable_clk;
- ret = qcom_scm_call(__scm->dev, &desc, &res);
+ ret = qcom_scm_call(dev, &desc, &res);
qcom_scm_bw_disable();
disable_clk:
@@ -1063,16 +999,14 @@ int qcom_scm_pas_shutdown(u32 pas_id)
return ret ? : res.result[0];
}
+
+int qcom_scm_pas_shutdown(u32 pas_id)
+{
+ return __qcom_scm_pas_shutdown(__scm->dev, pas_id);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
-/**
- * qcom_scm_pas_supported() - Check if the peripheral authentication service is
- * available for the given peripherial
- * @pas_id: peripheral authentication service id
- *
- * Returns true if PAS is supported for this peripheral, otherwise false.
- */
-bool qcom_scm_pas_supported(u32 pas_id)
+static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
@@ -1084,16 +1018,49 @@ bool qcom_scm_pas_supported(u32 pas_id)
};
struct qcom_scm_res res;
- if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ if (!__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_PIL,
QCOM_SCM_PIL_PAS_IS_SUPPORTED))
return false;
- ret = qcom_scm_call(__scm->dev, &desc, &res);
+ ret = qcom_scm_call(dev, &desc, &res);
return ret ? false : !!res.result[0];
}
+
+bool qcom_scm_pas_supported(u32 pas_id)
+{
+ return __qcom_scm_pas_supported(__scm->dev, pas_id);
+}
EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
+static struct qcom_pas_ops qcom_pas_ops_scm = {
+ .drv_name = "qcom_scm",
+ .supported = __qcom_scm_pas_supported,
+ .init_image = __qcom_scm_pas_init_image2,
+ .mem_setup = __qcom_scm_pas_mem_setup,
+ .get_rsc_table = __qcom_scm_pas_get_rsc_table2,
+ .auth_and_reset = __qcom_scm_pas_auth_and_reset,
+ .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset,
+ .set_remote_state = __qcom_scm_pas_set_remote_state,
+ .shutdown = __qcom_scm_pas_shutdown,
+ .metadata_release = __qcom_scm_pas_metadata_release,
+};
+
+/**
+ * qcom_scm_is_pas_available() - Check if the peripheral authentication service
+ * is available via SCM or not
+ *
+ * Returns true if PAS is available, otherwise false.
+ */
+static bool qcom_scm_is_pas_available(void)
+{
+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PIL_PAS_AUTH_AND_RESET))
+ return false;
+
+ return true;
+}
+
static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
{
struct qcom_scm_desc desc = {
@@ -2837,6 +2804,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
__get_convention();
+ if (qcom_scm_is_pas_available()) {
+ qcom_pas_ops_scm.dev = scm->dev;
+ qcom_pas_ops_register(&qcom_pas_ops_scm);
+ }
+
/*
* If "download mode" is requested, from this point on warmboot
* will cause the boot stages to enter download mode, unless
@@ -2876,6 +2848,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev)
{
/* Clean shutdown, disable download mode to allow normal restart */
qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP);
+ qcom_pas_ops_unregister();
}
static const struct of_device_id qcom_scm_dt_match[] = {
--
2.53.0
^ permalink raw reply related
* [PATCH v8 01/14] firmware: qcom: Add a generic PAS service
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Harshal Dev
In-Reply-To: <20260626133440.692849-1-sumit.garg@kernel.org>
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Qcom platforms has the legacy of using non-standard SCM calls
splintered over the various kernel drivers. These SCM calls aren't
compliant with the standard SMC calling conventions which is a
prerequisite to enable migration to the FF-A specifications from Arm.
OP-TEE as an alternative trusted OS to Qualcomm TEE (QTEE) can't
support these non-standard SCM calls. And even for newer architectures
using S-EL2 with Hafnium support, QTEE won't be able to support SCM
calls either with FF-A requirements coming in. And with both OP-TEE
and QTEE drivers well integrated in the TEE subsystem, it makes further
sense to reuse the TEE bus client drivers infrastructure.
The added benefit of TEE bus infrastructure is that there is support
for discoverable/enumerable services. With that client drivers don't
have to manually invoke a special SCM call to know the service status.
So enable the generic Peripheral Authentication Service (PAS) provided
by the firmware. It acts as the common layer with different TZ
backends plugged in whether it's an SCM implementation or a proper
TEE bus based PAS service implementation.
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
Reviewed-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/firmware/qcom/Kconfig | 8 +
drivers/firmware/qcom/Makefile | 1 +
drivers/firmware/qcom/qcom_pas.c | 291 +++++++++++++++++++++++++
drivers/firmware/qcom/qcom_pas.h | 50 +++++
include/linux/firmware/qcom/qcom_pas.h | 43 ++++
5 files changed, 393 insertions(+)
create mode 100644 drivers/firmware/qcom/qcom_pas.c
create mode 100644 drivers/firmware/qcom/qcom_pas.h
create mode 100644 include/linux/firmware/qcom/qcom_pas.h
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index b477d54b495a..9f66cc774508 100644
--- a/drivers/firmware/qcom/Kconfig
+++ b/drivers/firmware/qcom/Kconfig
@@ -6,6 +6,14 @@
menu "Qualcomm firmware drivers"
+config QCOM_PAS
+ tristate "Qualcomm generic PAS interface driver"
+ help
+ Enable the generic Peripheral Authentication Service (PAS) provided
+ by the firmware. It acts as the common layer with different TZ
+ backends plugged in whether it's an SCM implementation or a proper
+ TEE bus based PAS service implementation.
+
config QCOM_SCM
select QCOM_TZMEM
tristate
diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
index 0be40a1abc13..dc5ab45f906a 100644
--- a/drivers/firmware/qcom/Makefile
+++ b/drivers/firmware/qcom/Makefile
@@ -8,3 +8,4 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o
obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o
obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
+obj-$(CONFIG_QCOM_PAS) += qcom_pas.o
diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c
new file mode 100644
index 000000000000..bc6c42f2b3c6
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_pas.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015 Linaro Ltd.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/device/devres.h>
+#include <linux/firmware/qcom/qcom_pas.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "qcom_pas.h"
+
+static struct qcom_pas_ops *ops_ptr;
+
+/**
+ * devm_qcom_pas_context_alloc() - Allocate peripheral authentication service
+ * context for a given peripheral
+ *
+ * PAS context is device-resource managed, so the caller does not need
+ * to worry about freeing the context memory.
+ *
+ * @dev: PAS firmware device
+ * @pas_id: peripheral authentication service id
+ * @mem_phys: Subsystem reserve memory start address
+ * @mem_size: Subsystem reserve memory size
+ *
+ * Return: The new PAS context, or ERR_PTR() on failure.
+ */
+struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
+ u32 pas_id,
+ phys_addr_t mem_phys,
+ size_t mem_size)
+{
+ struct qcom_pas_context *ctx;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->dev = dev;
+ ctx->pas_id = pas_id;
+ ctx->mem_phys = mem_phys;
+ ctx->mem_size = mem_size;
+
+ return ctx;
+}
+EXPORT_SYMBOL_GPL(devm_qcom_pas_context_alloc);
+
+/**
+ * qcom_pas_init_image() - Initialize peripheral authentication service state
+ * machine for a given peripheral, using the metadata
+ * @pas_id: peripheral authentication service id
+ * @metadata: pointer to memory containing ELF header, program header table
+ * and optional blob of data used for authenticating the metadata
+ * and the rest of the firmware
+ * @size: size of the metadata
+ * @ctx: optional pas context
+ *
+ * Return: 0 on success.
+ *
+ * Upon successful return, the PAS metadata context (@ctx) will be used to
+ * track the metadata allocation, this needs to be released by invoking
+ * qcom_pas_metadata_release() by the caller.
+ */
+int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size,
+ struct qcom_pas_context *ctx)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_init_image);
+
+/**
+ * qcom_pas_metadata_release() - release metadata context
+ * @ctx: pas context
+ */
+void qcom_pas_metadata_release(struct qcom_pas_context *ctx)
+{
+ if (!ops_ptr || !ctx || !ctx->ptr)
+ return;
+
+ ops_ptr->metadata_release(ops_ptr->dev, ctx);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_metadata_release);
+
+/**
+ * qcom_pas_mem_setup() - Prepare the memory related to a given peripheral
+ * for firmware loading
+ * @pas_id: peripheral authentication service id
+ * @addr: start address of memory area to prepare
+ * @size: size of the memory area to prepare
+ *
+ * Return: 0 on success.
+ */
+int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_mem_setup);
+
+/**
+ * qcom_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
+ * for a given peripheral.
+ *
+ * Qualcomm remote processor may rely on both static and dynamic resources for
+ * its functionality. Static resources typically refer to memory-mapped
+ * addresses required by the subsystem and are often embedded within the
+ * firmware binary and dynamic resources, such as shared memory in DDR etc.,
+ * are determined at runtime during the boot process.
+ *
+ * On Qualcomm Technologies devices, it's possible that static resources are
+ * not embedded in the firmware binary and instead are provided by TrustZone.
+ * However, dynamic resources are always expected to come from TrustZone. This
+ * indicates that for Qualcomm devices, all resources (static and dynamic) will
+ * be provided by TrustZone PAS service.
+ *
+ * If the remote processor firmware binary does contain static resources, they
+ * should be passed in input_rt. These will be forwarded to TrustZone for
+ * authentication. TrustZone will then append the dynamic resources and return
+ * the complete resource table in output_rt_tzm.
+ *
+ * If the remote processor firmware binary does not include a resource table,
+ * the caller of this function should set input_rt as NULL and input_rt_size
+ * as zero respectively.
+ *
+ * More about documentation on resource table data structures can be found in
+ * include/linux/remoteproc.h
+ *
+ * @ctx: PAS context
+ * @pas_id: peripheral authentication service id
+ * @input_rt: resource table buffer which is present in firmware binary
+ * @input_rt_size: size of the resource table present in firmware binary
+ * @output_rt_size: TrustZone expects caller should pass worst case size for
+ * the output_rt_tzm.
+ *
+ * Return:
+ * On success, returns a pointer to the allocated buffer containing the final
+ * resource table and output_rt_size will have actual resource table size from
+ * TrustZone. The caller is responsible for freeing the buffer. On failure,
+ * returns ERR_PTR(-errno).
+ */
+struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx,
+ void *input_rt,
+ size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ if (!ops_ptr)
+ return ERR_PTR(-ENODEV);
+ if (!ctx)
+ return ERR_PTR(-EINVAL);
+
+ return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
+ input_rt_size, output_rt_size);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table);
+
+/**
+ * qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware
+ * and reset the remote processor
+ * @pas_id: peripheral authentication service id
+ *
+ * Return: 0 on success.
+ */
+int qcom_pas_auth_and_reset(u32 pas_id)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset);
+
+/**
+ * qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
+ * remote processor
+ *
+ * @ctx: Context saved during call to devm_qcom_pas_context_alloc()
+ *
+ * This function performs the necessary steps to prepare a PAS subsystem,
+ * authenticate it using the provided metadata, and initiate a reset sequence.
+ *
+ * It should be used when Linux is in control setting up the IOMMU hardware
+ * for remote subsystem during secure firmware loading processes. The
+ * preparation step sets up a shmbridge over the firmware memory before
+ * TrustZone accesses the firmware memory region for authentication. The
+ * authentication step verifies the integrity and authenticity of the firmware
+ * or configuration using secure metadata. Finally, the reset step ensures the
+ * subsystem starts in a clean and sane state.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+ if (!ctx)
+ return -EINVAL;
+
+ return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset);
+
+/**
+ * qcom_pas_set_remote_state() - Set the remote processor state
+ * @state: peripheral state
+ * @pas_id: peripheral authentication service id
+ *
+ * Return: 0 on success.
+ */
+int qcom_pas_set_remote_state(u32 state, u32 pas_id)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state);
+
+/**
+ * qcom_pas_shutdown() - Shut down the remote processor
+ * @pas_id: peripheral authentication service id
+ *
+ * Return: 0 on success.
+ */
+int qcom_pas_shutdown(u32 pas_id)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->shutdown(ops_ptr->dev, pas_id);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_shutdown);
+
+/**
+ * qcom_pas_supported() - Check if the peripheral authentication service is
+ * available for the given peripheral
+ * @pas_id: peripheral authentication service id
+ *
+ * Return: true if PAS is supported for this peripheral, otherwise false.
+ */
+bool qcom_pas_supported(u32 pas_id)
+{
+ if (!ops_ptr)
+ return false;
+
+ return ops_ptr->supported(ops_ptr->dev, pas_id);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_supported);
+
+bool qcom_pas_is_available(void)
+{
+ /*
+ * The barrier for ops_ptr is intended to synchronize the data stores
+ * for the ops data structure when client drivers are in parallel
+ * checking for PAS service availability.
+ *
+ * Once the PAS backend becomes available, it is allowed for multiple
+ * threads to enter TZ for parallel bringup of co-processors during
+ * boot.
+ */
+ return !!smp_load_acquire(&ops_ptr);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_is_available);
+
+void qcom_pas_ops_register(struct qcom_pas_ops *ops)
+{
+ if (!qcom_pas_is_available())
+ /* Paired with smp_load_acquire() in qcom_pas_is_available() */
+ smp_store_release(&ops_ptr, ops);
+ else
+ pr_err("qcom_pas: ops already registered by %s\n",
+ ops_ptr->drv_name);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
+
+void qcom_pas_ops_unregister(void)
+{
+ /* Paired with smp_load_acquire() in qcom_pas_is_available() */
+ smp_store_release(&ops_ptr, NULL);
+}
+EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver");
diff --git a/drivers/firmware/qcom/qcom_pas.h b/drivers/firmware/qcom/qcom_pas.h
new file mode 100644
index 000000000000..8643e2760602
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_pas.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QCOM_PAS_INT_H
+#define __QCOM_PAS_INT_H
+
+struct device;
+
+/**
+ * struct qcom_pas_ops - Qcom Peripheral Authentication Service (PAS) ops
+ * @drv_name: PAS driver name.
+ * @dev: PAS device pointer.
+ * @supported: Peripheral supported callback.
+ * @init_image: Peripheral image initialization callback.
+ * @mem_setup: Peripheral memory setup callback.
+ * @get_rsc_table: Peripheral get resource table callback.
+ * @prepare_and_auth_reset: Peripheral prepare firmware authentication and
+ * reset callback.
+ * @auth_and_reset: Peripheral firmware authentication and reset
+ * callback.
+ * @set_remote_state: Peripheral set remote state callback.
+ * @shutdown: Peripheral shutdown callback.
+ * @metadata_release: Image metadata release callback.
+ */
+struct qcom_pas_ops {
+ const char *drv_name;
+ struct device *dev;
+ bool (*supported)(struct device *dev, u32 pas_id);
+ int (*init_image)(struct device *dev, u32 pas_id, const void *metadata,
+ size_t size, struct qcom_pas_context *ctx);
+ int (*mem_setup)(struct device *dev, u32 pas_id, phys_addr_t addr,
+ phys_addr_t size);
+ void *(*get_rsc_table)(struct device *dev, struct qcom_pas_context *ctx,
+ void *input_rt, size_t input_rt_size,
+ size_t *output_rt_size);
+ int (*prepare_and_auth_reset)(struct device *dev,
+ struct qcom_pas_context *ctx);
+ int (*auth_and_reset)(struct device *dev, u32 pas_id);
+ int (*set_remote_state)(struct device *dev, u32 state, u32 pas_id);
+ int (*shutdown)(struct device *dev, u32 pas_id);
+ void (*metadata_release)(struct device *dev,
+ struct qcom_pas_context *ctx);
+};
+
+void qcom_pas_ops_register(struct qcom_pas_ops *ops);
+void qcom_pas_ops_unregister(void);
+
+#endif /* __QCOM_PAS_INT_H */
diff --git a/include/linux/firmware/qcom/qcom_pas.h b/include/linux/firmware/qcom/qcom_pas.h
new file mode 100644
index 000000000000..65b1c9564458
--- /dev/null
+++ b/include/linux/firmware/qcom/qcom_pas.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015 Linaro Ltd.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QCOM_PAS_H
+#define __QCOM_PAS_H
+
+#include <linux/err.h>
+#include <linux/types.h>
+
+struct qcom_pas_context {
+ struct device *dev;
+ u32 pas_id;
+ phys_addr_t mem_phys;
+ size_t mem_size;
+ void *ptr;
+ dma_addr_t phys;
+ ssize_t size;
+ bool use_tzmem;
+};
+
+bool qcom_pas_is_available(void);
+struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
+ u32 pas_id,
+ phys_addr_t mem_phys,
+ size_t mem_size);
+int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size,
+ struct qcom_pas_context *ctx);
+struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx,
+ void *input_rt, size_t input_rt_size,
+ size_t *output_rt_size);
+int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size);
+int qcom_pas_auth_and_reset(u32 pas_id);
+int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx);
+int qcom_pas_set_remote_state(u32 state, u32 pas_id);
+int qcom_pas_shutdown(u32 pas_id);
+bool qcom_pas_supported(u32 pas_id);
+void qcom_pas_metadata_release(struct qcom_pas_context *ctx);
+
+#endif /* __QCOM_PAS_H */
--
2.53.0
^ permalink raw reply related
* [PATCH v8 00/14] firmware: qcom: Add OP-TEE PAS service support
From: Sumit Garg @ 2026-06-26 13:34 UTC (permalink / raw)
To: andersson
Cc: linux-arm-msm, dri-devel, freedreno, linux-media, netdev,
linux-wireless, ath12k, linux-remoteproc, konradybcio, robh,
krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Qcom platforms has the legacy of using non-standard SCM calls
splintered over the various kernel drivers. These SCM calls aren't
compliant with the standard SMC calling conventions which is a
prerequisite to enable migration to the FF-A specifications from Arm.
OP-TEE as an alternative trusted OS to Qualcomm TEE (QTEE) can't
support these non-standard SCM calls. And even for newer architectures
using S-EL2 with Hafnium support, QTEE won't be able to support SCM
calls either with FF-A requirements coming in. And with both OP-TEE
and QTEE drivers well integrated in the TEE subsystem, it makes further
sense to reuse the TEE bus client drivers infrastructure.
The added benefit of TEE bus infrastructure is that there is support
for discoverable/enumerable services. With that client drivers don't
have to manually invoke a special SCM call to know the service status.
So enable the generic Peripheral Authentication Service (PAS) provided
by the firmware. It acts as the common layer with different TZ
backends plugged in whether it's an SCM implementation or a proper
TEE bus based PAS service implementation.
The TEE PAS service ABI is designed to be extensible with additional API
as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
extensions of the PAS service needed while still maintaining backwards
compatibility.
Currently OP-TEE support is being added to provide the backend PAS
service implementation which can be found as part of this PR [1].
This implementation has been tested on Kodiak/RB3Gen2 and lemans
EVK boards. In addition to that WIN/IPQ targets tested OP-TEE with
this service too. Surely the backwards compatibility is maintained and
tested for SCM backend.
Note that kernel PAS service support while running in EL2 is at parity
among OP-TEE vs QTEE. Especially the media (venus/iris) support depends
on proper IOMMU support being worked out on the PAS client end.
Patch summary:
- Patch #1: adds generic PAS service.
- Patch #2: migrates SCM backend to generic PAS service.
- Patch #3: adds TEE/OP-TEE backend for generic PAS service.
- Patch #4-#12: migrates all client drivers to generic PAS service.
- Patch #13: drops legacy PAS SCM exported APIs.
The patch-set is based on qcom tree tip [2] and can be found in git tree
here [3].
Merge strategy:
It is expected due to APIs dependency, the entire patch-set to go via
the Qcom tree. All other subsystem maintainers, it will be great if I
can get acks for the corresponding subsystem patches.
[1] https://github.com/OP-TEE/optee_os/pull/7721 (already merged)
[2] https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git/log/?h=for-next
[3] https://git.kernel.org/pub/scm/linux/kernel/git/sumit.garg/linux.git/log/?h=qcom-pas-v8
---
Changes in v8:
- Rebased on mainline tip (no functional changes).
- Now Lemans EVK is also tested to support OP-TEE PAS here:
https://github.com/OP-TEE/optee_os/pull/7845
- Drop Kodiak DT patch as it is carried independently by Mukesh here:
https://lore.kernel.org/lkml/20260624063952.2242702-1-mukesh.ojha@oss.qualcomm.com/
- Regarding Sashiko comments, I have already replied in v6 the ones that
don't apply but in v7 I got the same comments again. Specific context
reasoning which Shashiko ignores:
- ABI contract between Linux and TZ
- No support for multiple concurrent backends
- The TZ backend doesn’t detach during the entire boot cycle
Changes in v7:
- Rebased to qcom tree (for-next branch) tip.
- Merged patch #5 and #7 due to build dependency.
- Disabled modem for kodiak EL2 as it isn't tested yet.
- Fix an issue found out by sashiko-bot for patch #4.
Changes in v6:
- Rebased to v7.1-rc4 tag.
- Patch #14: fixed ret error print.
- Add Kconfig descriptions for PAS symbols such that they are visible
in menuconfig to update.
Changes in v5:
- Incorporated misc. comments from Mukesh.
- Split up patch #11 into 2 to add an independent commit for passing
proper PAS ID to set_remote_state API.
- Picked up tags.
Changes in v4:
- Incorporate misc. comments on patch #4.
- Picked up an ack for patch #10.
- Clarify in cover letter about state of media support.
Changes in v3:
- Incorporated some style and misc. comments for patch #2, #3 and #4.
- Add QCOM_PAS Kconfig dependency for various subsystems.
- Switch from pseudo TA to proper TA invoke commands.
Changes in v2:
- Fixed kernel doc warnings.
- Polish commit message and comments for patch #2.
- Pass proper PAS ID in set_remote_state API for media firmware drivers.
- Added Maintainer entry and dropped MODULE_AUTHOR.
Sumit Garg (14):
firmware: qcom: Add a generic PAS service
firmware: qcom_scm: Migrate to generic PAS service
firmware: qcom: Add a PAS TEE service
remoteproc: qcom_q6v5_pas: Switch over to generic PAS TZ APIs
remoteproc: qcom_q6v5_mss: Switch to generic PAS TZ APIs
remoteproc: qcom_wcnss: Switch to generic PAS TZ APIs
remoteproc: qcom: Select QCOM_PAS generic service
drm/msm: Switch to generic PAS TZ APIs
media: qcom: Switch to generic PAS TZ APIs
media: qcom: Pass proper PAS ID to set_remote_state API
net: ipa: Switch to generic PAS TZ APIs
wifi: ath12k: Switch to generic PAS TZ APIs
firmware: qcom_scm: Remove SCM PAS wrappers
MAINTAINERS: Add maintainer entry for Qualcomm PAS TZ service
MAINTAINERS | 9 +
drivers/firmware/qcom/Kconfig | 21 +-
drivers/firmware/qcom/Makefile | 2 +
drivers/firmware/qcom/qcom_pas.c | 291 +++++++++++
drivers/firmware/qcom/qcom_pas.h | 50 ++
drivers/firmware/qcom/qcom_pas_tee.c | 477 ++++++++++++++++++
drivers/firmware/qcom/qcom_scm.c | 302 ++++-------
drivers/gpu/drm/msm/Kconfig | 1 +
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 +-
drivers/gpu/drm/msm/adreno/adreno_gpu.c | 11 +-
drivers/media/platform/qcom/iris/Kconfig | 27 +-
.../media/platform/qcom/iris/iris_firmware.c | 9 +-
drivers/media/platform/qcom/venus/Kconfig | 1 +
drivers/media/platform/qcom/venus/firmware.c | 11 +-
drivers/net/ipa/Kconfig | 2 +-
drivers/net/ipa/ipa_main.c | 13 +-
drivers/net/wireless/ath/ath12k/Kconfig | 2 +-
drivers/net/wireless/ath/ath12k/ahb.c | 10 +-
drivers/remoteproc/Kconfig | 4 +-
drivers/remoteproc/qcom_q6v5_mss.c | 5 +-
drivers/remoteproc/qcom_q6v5_pas.c | 51 +-
drivers/remoteproc/qcom_wcnss.c | 12 +-
drivers/soc/qcom/mdt_loader.c | 12 +-
include/linux/firmware/qcom/qcom_pas.h | 43 ++
include/linux/firmware/qcom/qcom_scm.h | 29 --
include/linux/soc/qcom/mdt_loader.h | 6 +-
26 files changed, 1084 insertions(+), 321 deletions(-)
create mode 100644 drivers/firmware/qcom/qcom_pas.c
create mode 100644 drivers/firmware/qcom/qcom_pas.h
create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
create mode 100644 include/linux/firmware/qcom/qcom_pas.h
--
2.53.0
^ permalink raw reply
* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: George Moussalem @ 2026-06-26 11:32 UTC (permalink / raw)
To: Konrad Dybcio, Jens Axboe, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
Konrad Dybcio, Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <38aceb33-b28e-4994-b277-de070b6dae2b@oss.qualcomm.com>
On 6/26/26 15:20, Konrad Dybcio wrote:
> On 6/25/26 4:10 PM, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add support to bring up the M0 core of the bluetooth subsystem found in
>> the IPQ5018 SoC.
>>
>> The signed firmware loaded is authenticated by TrustZone. If successful,
>> the M0 core boots the firmware and the peripheral is taken out of reset
>> using a Secure Channel Manager call to TrustZone.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>
> Can this not fit inside the existing PAS driver?
I've tried but there were two issues with that:
1. a custom way to load the firmware into memory is required because the
loadable segment needs to be offset by the virtual address in the mbn
file (see 0x20250 below). The standard mdt_loader uses the physical
addresses.
readelf -l bt_fw_patch.mbn
Elf file type is EXEC (Executable file)
Entry point 0x20255
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x00000000 0x00000000 0x00094 0x00000 0
NULL 0x001000 0x0001a000 0x0001a000 0x00088 0x01000 0x1000
LOAD 0x002000 0x00020250 0x00000000 0x06154 0x190f8 RWE 0x4
2. memory needs to be ioremapped using ioremap, not ioremap_wc, else TZ
will complain and throw XPU violations due to strict memory alignment
and non-cache requirements.
>
> Konrad
Cheers,
George
^ permalink raw reply
* Re: [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
From: Konrad Dybcio @ 2026-06-26 11:30 UTC (permalink / raw)
To: George Moussalem, Krzysztof Kozlowski
Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <SN7PR19MB673692EBED649CF6DC9833A89DEB2@SN7PR19MB6736.namprd19.prod.outlook.com>
On 6/26/26 1:20 PM, George Moussalem wrote:
> On 6/26/26 14:53, Krzysztof Kozlowski wrote:
>> On Thu, Jun 25, 2026 at 06:10:08PM +0400, George Moussalem wrote:
>>> Document the Qualcomm IPQ5018 Bluetooth controller.
>>>
>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>> ---
[...]
>>> + compatible = "qcom,ipq5018-bt";
>>> +
>>> + qcom,ipc = <&apcs_glb 8 23>;
>>> + interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
>>
>> No firmware to load?
>
> firmware is loaded by the remoteproc in patch 1
>
>>
>> It feels like remoteproc node split is fake. The property qcom,rproc is
>> even more supporting that case. Shouldn't this be simply one device -
>> bluetooth? What sort of two devices do you have exactly? How can I
>> identify them in the hardware?
>
> I wasn't sure how to represent the HW. Should I make this bluetooth node
> a childnode of the rproc? Essentially, this is the transport layer
> (using shared memory space and IPC/interrupt).
>
> Most QCA BT controllers are also childnodes of a serdev/uart node as
> they use serdev for transport.
>
> From what I understand, it's simply BT firmware running on this
> dedicated M0 core in the SoC itself connected to an RF.
Seems like this rhymes with the WPSS remoteproc +ATH1xK_AHB situation
- the Q6 core power sequences and manages the wireless controller,
while Linux gets to drive the device as it would if it were connected
over PCIe/ UART respectively, just with MMIO writes instead.
Konrad
^ permalink raw reply
* Re: [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
From: George Moussalem @ 2026-06-26 11:20 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <20260626-discerning-light-swan-6b599c@quoll>
On 6/26/26 14:53, Krzysztof Kozlowski wrote:
> On Thu, Jun 25, 2026 at 06:10:08PM +0400, George Moussalem wrote:
>> Document the Qualcomm IPQ5018 Bluetooth controller.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>> .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml | 63 ++++++++++++++++++++++
>> 1 file changed, 63 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
>> new file mode 100644
>> index 000000000000..afd33f851858
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
>> @@ -0,0 +1,63 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Qualcomm IPQ5018 Bluetooth
>> +
>> +maintainers:
>> + - George Moussalem <george.moussalem@outlook.com>
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - qcom,ipq5018-bt
>> +
>> + interrupts:
>> + items:
>> + - description:
>> + Interrupt line from the M0 Bluetooth Subsystem to the host processor
>
> What is M0?
it's a low power Cortex M0 core, for Bluetooth processing in this case.
>
> Anyway, this part feels completely redundant. Can "interrupts" property
> be anything else than an interrupt line from the device to the host
> processor?
>
>
>> + to notify it of events such as re
>
> This feels useful, but cut/incomplete.
yeah, c/p error. The interrupt is to notify the host of bluetooth events
running on the m0 core, such as TX/CMD completion and/or availability of
new data frames in the ring buffers.
>
>> +
>> + qcom,ipc:
>> + $ref: /schemas/types.yaml#/definitions/phandle-array
>> + items:
>> + - items:
>> + - description: phandle to a syscon node representing the APCS registers
>> + - description: u32 representing offset to the register within the syscon
>> + - description: u32 representing the ipc bit within the register
>> + description: |
>> + These entries specify the outgoing IPC bit used for signaling the remote
>> + M0 BTSS core of a host event or for sending an ACK if the remote processor
>> + expects it.
>> +
>> + qcom,rproc:
>> + $ref: /schemas/types.yaml#/definitions/phandle
>> + description:
>> + Phandle to the remote processor node representing the M0 BTSS core.
>> +
>> +required:
>> + - compatible
>> + - interrupts
>> + - qcom,ipc
>> + - qcom,rproc
>> +
>> +allOf:
>> + - $ref: bluetooth-controller.yaml#
>> + - $ref: qcom,bluetooth-common.yaml
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +
>> + bluetooth: bluetooth {
>
> Drop unused label
will drop
>
>> + compatible = "qcom,ipq5018-bt";
>> +
>> + qcom,ipc = <&apcs_glb 8 23>;
>> + interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
>
> No firmware to load?
firmware is loaded by the remoteproc in patch 1
>
> It feels like remoteproc node split is fake. The property qcom,rproc is
> even more supporting that case. Shouldn't this be simply one device -
> bluetooth? What sort of two devices do you have exactly? How can I
> identify them in the hardware?
I wasn't sure how to represent the HW. Should I make this bluetooth node
a childnode of the rproc? Essentially, this is the transport layer
(using shared memory space and IPC/interrupt).
Most QCA BT controllers are also childnodes of a serdev/uart node as
they use serdev for transport.
From what I understand, it's simply BT firmware running on this
dedicated M0 core in the SoC itself connected to an RF.
>
>> +
>> + qcom,rproc = <&m0_btss>;
>
> Best regards,
> Krzysztof
>
^ permalink raw reply
* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: Konrad Dybcio @ 2026-06-26 11:20 UTC (permalink / raw)
To: george.moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
Konrad Dybcio, Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <20260625-ipq5018-bluetooth-v1-2-d999be0e04f7@outlook.com>
On 6/25/26 4:10 PM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Add support to bring up the M0 core of the bluetooth subsystem found in
> the IPQ5018 SoC.
>
> The signed firmware loaded is authenticated by TrustZone. If successful,
> the M0 core boots the firmware and the peripheral is taken out of reset
> using a Secure Channel Manager call to TrustZone.
>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
Can this not fit inside the existing PAS driver?
Konrad
^ permalink raw reply
* [PATCH v2] wifi: rtw89: regd: stop gating 6GHz on the ww domain
From: Matthew Leach @ 2026-06-26 11:19 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, kernel, Zong-Zhe Yang,
Matthew Leach
The driver maintains its own block_* bitmaps on top of cfg80211's
regulatory enforcement. These bitmaps are indexed by the position of an
entry in the active regd_map (either the built-in rtw89_regd_map or the
table loaded from a firmware element).
The ww domain is handled outside the regd_map; it lives in a standalone
rtw89_ww_regd object, and rtw89_regd_get_index() returns the
RTW89_REGD_MAX_COUNTRY_NUM sentinel for it. Every block_* lookup site
guards against that sentinel and bails out, which means none of the
block_* checks ever apply to ww. The 6GHz check therefore treats ww as
unconditionally blocked at the driver layer, independently of whatever
cfg80211/wireless-regdb permit.
Make ww a first-class entry in the regd_map instead:
- Reserve slot 0 of rtw89_regd_map for the ww entry and drop the
standalone rtw89_ww_regd object.
- In rtw89_recognize_regd_from_elm(), reserve slot 0 of the
firmware-loaded map for ww and populate it from the built-in
definition via the new rtw89_regd_static_ww_entry() helper.
- Drop the rtw89_regd_is_ww() special case in rtw89_regd_get_index()
and identify ww by alpha2 so the check works for entries in either
map.
- Have rtw89_regd_find_reg_by_name() return NULL on miss instead of
silently substituting ww. Callers that need the previous "fall back
to ww" behaviour (rtw89_regd_notifier_apply()) now do so explicitly
against the active map's slot 0, and rtw89_regd_get_index_by_name()
translates NULL into the RTW89_REGD_MAX_COUNTRY_NUM sentinel so the
existing "unknown alpha2" branches in the block_* setup paths
remain.
With ww at a real index the driver's block_* checks apply uniformly for
all bands and cfg80211 / wireless-regdb become the sole source of truth
for what is permitted on the ww domain.
Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
---
Changes in v2:
- Re-word the commit message, clarifying the motivation behind the
patch (removing the explicit 6GHz disablement).
- Re-work the patch to play nicely with firmware loaded regulatory
tables by reserving slot 0 in both static and firmware regulatory lists
for the WW domain.
- Link to v1: https://patch.msgid.link/20260617-rtw89-6ghz-regd-fixes-v1-1-33d744a07d16@collabora.com
To: Ping-Ke Shih <pkshih@realtek.com>
Cc: linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
drivers/net/wireless/realtek/rtw89/core.h | 1 +
drivers/net/wireless/realtek/rtw89/fw.c | 6 ++++--
drivers/net/wireless/realtek/rtw89/regd.c | 32 +++++++++++++++++++++----------
3 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index fd29dbbb120d..dc9eae2cee69 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -7862,6 +7862,7 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev,
bool rtw89_legacy_rate_to_bitrate(struct rtw89_dev *rtwdev, u8 legacy_rate, u16 *bitrate);
int rtw89_regd_setup(struct rtw89_dev *rtwdev);
int rtw89_regd_init_hint(struct rtw89_dev *rtwdev);
+const struct rtw89_regd *rtw89_regd_static_ww_entry(void);
const char *rtw89_regd_get_string(enum rtw89_regulation_type regd);
void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
struct rtw89_traffic_stats *stats);
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 17704f054727..77f320cae01b 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -1347,10 +1347,11 @@ int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev,
{
const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd;
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
- u32 num_ents = le32_to_cpu(regd_elm->num_ents);
struct rtw89_regd_data *p;
struct rtw89_regd regd;
- u32 i = 0;
+ /* +1 because we reserve index 0 for WW */
+ u32 num_ents = le32_to_cpu(regd_elm->num_ents) + 1;
+ u32 i = 1;
if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) {
rtw89_warn(rtwdev,
@@ -1373,6 +1374,7 @@ int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev,
return -ENOMEM;
p->nr = num_ents;
+ p->map[0] = *rtw89_regd_static_ww_entry();
rtw89_for_each_in_regd_element(®d, regd_elm)
p->map[i++] = regd;
diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c
index 28466cb35ea2..27ac87374e6e 100644
--- a/drivers/net/wireless/realtek/rtw89/regd.c
+++ b/drivers/net/wireless/realtek/rtw89/regd.c
@@ -21,10 +21,8 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request
static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC);
-static const struct rtw89_regd rtw89_ww_regd =
- COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0);
-
static const struct rtw89_regd rtw89_regd_map[] = {
+ COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0),
COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0),
COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
@@ -316,12 +314,12 @@ static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rt
return ®d_ctrl->map[i];
}
- return &rtw89_ww_regd;
+ return NULL;
}
static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)
{
- return regd == &rtw89_ww_regd;
+ return !memcmp(regd->alpha2, "00", 2);
}
static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd)
@@ -331,9 +329,6 @@ static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd
BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM);
- if (rtw89_regd_is_ww(regd))
- return RTW89_REGD_MAX_COUNTRY_NUM;
-
return regd - regd_ctrl->map;
}
@@ -342,6 +337,10 @@ static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alp
const struct rtw89_regd *regd;
regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2);
+
+ if (!regd)
+ return RTW89_REGD_MAX_COUNTRY_NUM;
+
return rtw89_regd_get_index(rtwdev, regd);
}
@@ -721,7 +720,7 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)
return -EINVAL;
chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code);
- if (!rtw89_regd_is_ww(chip_regd)) {
+ if (chip_regd && !rtw89_regd_is_ww(chip_regd)) {
rtwdev->regulatory.regd = chip_regd;
rtwdev->regulatory.programmed = true;
@@ -743,6 +742,11 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)
return 0;
}
+const struct rtw89_regd *rtw89_regd_static_ww_entry(void)
+{
+ return &rtw89_regd_map[0];
+}
+
static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev,
struct wiphy *wiphy)
{
@@ -859,7 +863,15 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
struct wiphy *wiphy,
struct regulatory_request *request)
{
- rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
+ const struct rtw89_regd *regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
+
+ if (!regd) {
+ /* Fallback to WW domain if name not found. */
+ regd = &rtwdev->regulatory.ctrl.map[0];
+ }
+
+ rtwdev->regulatory.regd = regd;
+
/* This notification might be set from the system of distros,
* and it does not expect the regulatory will be modified by
* connecting to an AP (i.e. country ie).
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260616-rtw89-6ghz-regd-fixes-d8f816880bd0
Best regards,
--
Matt
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox