Message ID | 20250417061245.497803-4-gokul.sriram.p@oss.qualcomm.com |
---|---|
State | New |
Headers | show |
Series | Add new driver for WCSS secure PIL loading | expand |
On 4/17/25 8:12 AM, Gokul Sriram Palanisamy wrote: > From: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> > > Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx > SoCs support secure Peripheral Image Loading (PIL). > > Secure PIL image is signed firmware image which only trusted software such > as TrustZone (TZ) can authenticate and load. Linux kernel will send a > Peripheral Authentication Service (PAS) request to TZ to authenticate and > load the PIL images. This change also introduces secure firmware > authentication using Trusted Management Engine-Lite (TME-L) which is > supported on IPQ5424 SoC. This driver uses mailbox based PAS request to > TME-L for image authentication if supported, else it will fallback to use > SCM call based PAS request to TZ. > > In order to avoid overloading the existing WCSS driver or PAS driver, we > came up with this new PAS based IPQ WCSS driver. > > Signed-off-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> > Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com> > Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com> > --- [...] > +static int wcss_sec_start(struct rproc *rproc) > +{ > + struct wcss_sec *wcss = rproc->priv; > + struct device *dev = wcss->dev; > + int ret; > + > + ret = qcom_q6v5_prepare(&wcss->q6); > + if (ret) > + return ret; > + > + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { You abort probe if wcss->mbox_chan returns an errno, please rework this to use if (use_tmelcom) or something [...] > +static void wcss_sec_copy_segment(struct rproc *rproc, > + struct rproc_dump_segment *segment, > + void *dest, size_t offset, size_t size) > +{ > + struct wcss_sec *wcss = rproc->priv; > + struct device *dev = wcss->dev; > + > + if (!segment->io_ptr) > + segment->io_ptr = ioremap_wc(segment->da, segment->size); > + > + if (!segment->io_ptr) { > + dev_err(dev, "Failed to ioremap segment %pad size 0x%zx\n", > + &segment->da, segment->size); > + return; > + } > + > + if (offset + size <= segment->size) { I believe this allows an off-by-one (remove '=') [...] > + memcpy(dest, segment->io_ptr + offset, size); > + } else { > + iounmap(segment->io_ptr); > + segment->io_ptr = NULL; > + } > +} > + > +static int wcss_sec_dump_segments(struct rproc *rproc, > + const struct firmware *fw) > +{ > + struct device *dev = rproc->dev.parent; > + struct reserved_mem *rmem = NULL; > + struct device_node *node; > + int num_segs, index; > + int ret; > + > + /* > + * Parse through additional reserved memory regions for the rproc > + * and add them to the coredump segments > + */ > + num_segs = of_count_phandle_with_args(dev->of_node, > + "memory-region", NULL); > + for (index = 0; index < num_segs; index++) { > + node = of_parse_phandle(dev->of_node, > + "memory-region", index); https://lore.kernel.org/linux-arm-msm/20250423-dt-memory-region-v2-v2-0-2fbd6ebd3c88@kernel.org/ [...] > +static const struct wcss_data wcss_sec_ipq5424_res_init = { > + .pasid = MPD_WCSS_PAS_ID, > + .ss_name = "q6wcss", > + .tmelcom = true, "bool tmelcom" is very non-descriptive.. call it something like use_tmelcom, or maybe flip the condition and call it e.g. tz_managed Konrad
On 4/25/2025 5:17 PM, Konrad Dybcio wrote: > On 4/17/25 8:12 AM, Gokul Sriram Palanisamy wrote: >> From: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >> >> Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx >> SoCs support secure Peripheral Image Loading (PIL). >> >> Secure PIL image is signed firmware image which only trusted software such >> as TrustZone (TZ) can authenticate and load. Linux kernel will send a >> Peripheral Authentication Service (PAS) request to TZ to authenticate and >> load the PIL images. This change also introduces secure firmware >> authentication using Trusted Management Engine-Lite (TME-L) which is >> supported on IPQ5424 SoC. This driver uses mailbox based PAS request to >> TME-L for image authentication if supported, else it will fallback to use >> SCM call based PAS request to TZ. >> >> In order to avoid overloading the existing WCSS driver or PAS driver, we >> came up with this new PAS based IPQ WCSS driver. >> >> Signed-off-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >> Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com> >> Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com> >> --- > [...] > >> +static int wcss_sec_start(struct rproc *rproc) >> +{ >> + struct wcss_sec *wcss = rproc->priv; >> + struct device *dev = wcss->dev; >> + int ret; >> + >> + ret = qcom_q6v5_prepare(&wcss->q6); >> + if (ret) >> + return ret; >> + >> + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { > You abort probe if wcss->mbox_chan returns an errno, please rework > this to use if (use_tmelcom) or something Hi Konrad, do you mean to use 'use_tmelcom' variable from driver descriptor? If yes, what if mbox_request_channel( ) failed? or based on wcss->mbox_chan, should I set 'use_tmeeiihcckgddglcom' to true or false and use it? > [...] > >> +static void wcss_sec_copy_segment(struct rproc *rproc, >> + struct rproc_dump_segment *segment, >> + void *dest, size_t offset, size_t size) >> +{ >> + struct wcss_sec *wcss = rproc->priv; >> + struct device *dev = wcss->dev; >> + >> + if (!segment->io_ptr) >> + segment->io_ptr = ioremap_wc(segment->da, segment->size); >> + >> + if (!segment->io_ptr) { >> + dev_err(dev, "Failed to ioremap segment %pad size 0x%zx\n", >> + &segment->da, segment->size); >> + return; >> + } >> + >> + if (offset + size <= segment->size) { > I believe this allows an off-by-one (remove '=') ok, will check and update. > [...] > >> + memcpy(dest, segment->io_ptr + offset, size); >> + } else { >> + iounmap(segment->io_ptr); >> + segment->io_ptr = NULL; >> + } >> +} >> + >> +static int wcss_sec_dump_segments(struct rproc *rproc, >> + const struct firmware *fw) >> +{ >> + struct device *dev = rproc->dev.parent; >> + struct reserved_mem *rmem = NULL; >> + struct device_node *node; >> + int num_segs, index; >> + int ret; >> + >> + /* >> + * Parse thcitejlvhvdriihtheuvhn >> hrough additional reserved memory regions for the rproc >> + * and add them to the coredump segments >> + */ >> + num_segs = of_count_phandle_with_args(dev->of_node, >> + "memory-region", NULL); >> + for (index = 0; index < num_segs; index++) { >> + node = of_parse_phandle(dev->of_node, >> + "memory-region", index); > https://lore.kernel.org/linux-arm-msm/20250423-dt-memory-region-v2-v2-0-2fbd6ebd3c88@kernel.org/ ok, will implement of_reserved_mem_region_to_resource() and of_reserved_mem_region_count(). > > [...] > >> +static const struct wcss_data wcss_sec_ipq5424_res_init = { >> + .pasid = MPD_WCSS_PAS_ID, >> + .ss_name = "q6wcss", >> + .tmelcom = true, > "bool tmelcom" is very non-descriptive.. call it something like > use_tmelcom, or maybe flip the condition and call it e.g. > tz_managed oeiihcckgddgcjnfeeuhjfdfbcfcdenvfnjnuceuntbir eiihcckgddgcgltegldivttfeldnhugbnjvtllkkrujt eiihcckgddgchbegrjtgfnk, will call it 'use_tmelcom'.
On 5/5/25 2:30 PM, Gokul Sriram P wrote: > > On 4/25/2025 5:17 PM, Konrad Dybcio wrote: >> On 4/17/25 8:12 AM, Gokul Sriram Palanisamy wrote: >>> From: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >>> >>> Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx >>> SoCs support secure Peripheral Image Loading (PIL). >>> >>> Secure PIL image is signed firmware image which only trusted software such >>> as TrustZone (TZ) can authenticate and load. Linux kernel will send a >>> Peripheral Authentication Service (PAS) request to TZ to authenticate and >>> load the PIL images. This change also introduces secure firmware >>> authentication using Trusted Management Engine-Lite (TME-L) which is >>> supported on IPQ5424 SoC. This driver uses mailbox based PAS request to >>> TME-L for image authentication if supported, else it will fallback to use >>> SCM call based PAS request to TZ. >>> >>> In order to avoid overloading the existing WCSS driver or PAS driver, we >>> came up with this new PAS based IPQ WCSS driver. >>> >>> Signed-off-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >>> Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com> >>> Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com> >>> --- >> [...] >> >>> +static int wcss_sec_start(struct rproc *rproc) >>> +{ >>> + struct wcss_sec *wcss = rproc->priv; >>> + struct device *dev = wcss->dev; >>> + int ret; >>> + >>> + ret = qcom_q6v5_prepare(&wcss->q6); >>> + if (ret) >>> + return ret; >>> + >>> + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { >> You abort probe if wcss->mbox_chan returns an errno, please rework >> this to use if (use_tmelcom) or something > > Hi Konrad, > > do you mean to use 'use_tmelcom' variable from driver descriptor? If > yes, what if mbox_request_channel( ) failed? > > or based on wcss->mbox_chan, should I set 'use_tmeeiihcckgddglcom' to > true or false and use it? Add 'use_tmelcom' in match data and then make decisions based on it if the mailbox channel get fails and use_tmelcom is true, fail probing etc. Konrad
On 4/25/2025 5:17 PM, Konrad Dybcio wrote: > On 4/17/25 8:12 AM, Gokul Sriram Palanisamy wrote: >> From: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >> >> Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx >> SoCs support secure Peripheral Image Loading (PIL). >> >> Secure PIL image is signed firmware image which only trusted software such >> as TrustZone (TZ) can authenticate and load. Linux kernel will send a >> Peripheral Authentication Service (PAS) request to TZ to authenticate and >> load the PIL images. This change also introduces secure firmware >> authentication using Trusted Management Engine-Lite (TME-L) which is >> supported on IPQ5424 SoC. This driver uses mailbox based PAS request to >> TME-L for image authentication if supported, else it will fallback to use >> SCM call based PAS request to TZ. >> >> In order to avoid overloading the existing WCSS driver or PAS driver, we >> came up with this new PAS based IPQ WCSS driver. >> >> Signed-off-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >> Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com> >> Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com> >> --- > [...] > >> +static int wcss_sec_start(struct rproc *rproc) >> +{ >> + struct wcss_sec *wcss = rproc->priv; >> + struct device *dev = wcss->dev; >> + int ret; >> + >> + ret = qcom_q6v5_prepare(&wcss->q6); >> + if (ret) >> + return ret; >> + >> + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { > You abort probe if wcss->mbox_chan returns an errno, please rework > this to use if (use_tmelcom) or something Hi Konrad, Sorry, please ignore the last respone which was unknowingly sent before completing the email. do you mean to use 'use_tmelcom' variable from driver descriptor? If yes, what if mbox_request_channel( ) failed? or based on wcss->mbox_chan, should I set 'use_tmelcom' to true or false and use it? based on availability of "mboxes = <&tmel_qmp 0>;" use the response from mbox_request_channel( ) to use tmel or tz flow? > [...] > >> +static void wcss_sec_copy_segment(struct rproc *rproc, >> + struct rproc_dump_segment *segment, >> + void *dest, size_t offset, size_t size) >> +{ >> + struct wcss_sec *wcss = rproc->priv; >> + struct device *dev = wcss->dev; >> + >> + if (!segment->io_ptr) >> + segment->io_ptr = ioremap_wc(segment->da, segment->size); >> + >> + if (!segment->io_ptr) { >> + dev_err(dev, "Failed to ioremap segment %pad size 0x%zx\n", >> + &segment->da, segment->size); >> + return; >> + } >> + >> + if (offset + size <= segment->size) { > I believe this allows an off-by-one (remove '=') ok, will check and update. > [...] > >> + memcpy(dest, segment->io_ptr + offset, size); >> + } else { >> + iounmap(segment->io_ptr); >> + segment->io_ptr = NULL; >> + } >> +} >> + >> +static int wcss_sec_dump_segments(struct rproc *rproc, >> + const struct firmware *fw) >> +{ >> + struct device *dev = rproc->dev.parent; >> + struct reserved_mem *rmem = NULL; >> + struct device_node *node; >> + int num_segs, index; >> + int ret; >> + >> + /* >> + * Parse through additional reserved memory regions for the rproc >> + * and add them to the coredump segments >> + */ >> + num_segs = of_count_phandle_with_args(dev->of_node, >> + "memory-region", NULL); >> + for (index = 0; index < num_segs; index++) { >> + node = of_parse_phandle(dev->of_node, >> + "memory-region", index); > https://lore.kernel.org/linux-arm-msm/20250423-dt-memory-region-v2-v2-0-2fbd6ebd3c88@kernel.org/ ok, will implement of_reserved_mem_region_to_resource() and of_reserved_mem_region_count(). > [...] > >> +static const struct wcss_data wcss_sec_ipq5424_res_init = { >> + .pasid = MPD_WCSS_PAS_ID, >> + .ss_name = "q6wcss", >> + .tmelcom = true, > "bool tmelcom" is very non-descriptive.. call it something like > use_tmelcom, or maybe flip the condition and call it e.g. > tz_managed ok, will call it "use_tmelcom". But can I avoid this variable and use tmel or tz based on response from mbox_request_channel( ) as in 1st query above. Regards, Gokul
On 5/5/2025 6:37 PM, Konrad Dybcio wrote: > On 5/5/25 2:30 PM, Gokul Sriram P wrote: >> On 4/25/2025 5:17 PM, Konrad Dybcio wrote: >>> On 4/17/25 8:12 AM, Gokul Sriram Palanisamy wrote: >>>> From: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >>>> >>>> Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx >>>> SoCs support secure Peripheral Image Loading (PIL). >>>> >>>> Secure PIL image is signed firmware image which only trusted software such >>>> as TrustZone (TZ) can authenticate and load. Linux kernel will send a >>>> Peripheral Authentication Service (PAS) request to TZ to authenticate and >>>> load the PIL images. This change also introduces secure firmware >>>> authentication using Trusted Management Engine-Lite (TME-L) which is >>>> supported on IPQ5424 SoC. This driver uses mailbox based PAS request to >>>> TME-L for image authentication if supported, else it will fallback to use >>>> SCM call based PAS request to TZ. >>>> >>>> In order to avoid overloading the existing WCSS driver or PAS driver, we >>>> came up with this new PAS based IPQ WCSS driver. >>>> >>>> Signed-off-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> >>>> Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com> >>>> Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com> >>>> --- >>> [...] >>> >>>> +static int wcss_sec_start(struct rproc *rproc) >>>> +{ >>>> + struct wcss_sec *wcss = rproc->priv; >>>> + struct device *dev = wcss->dev; >>>> + int ret; >>>> + >>>> + ret = qcom_q6v5_prepare(&wcss->q6); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { >>> You abort probe if wcss->mbox_chan returns an errno, please rework >>> this to use if (use_tmelcom) or something >> Hi Konrad, >> >> do you mean to use 'use_tmelcom' variable from driver descriptor? If >> yes, what if mbox_request_channel( ) failed? >> >> or based on wcss->mbox_chan, should I set 'use_tmeeiihcckgddglcom' to >> true or false and use it? > Add 'use_tmelcom' in match data and then make decisions based on it > if the mailbox channel get fails and use_tmelcom is true, fail probing > etc. Got it. Will update. Regards, Gokul
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 83962a114dc9..656cbb12b54d 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -255,6 +255,25 @@ config QCOM_Q6V5_WCSS Hexagon V5 based WCSS remote processors on e.g. IPQ8074. This is a non-TrustZone wireless subsystem. +config QCOM_Q6V5_WCSS_SEC + tristate "Qualcomm Hexagon based WCSS Secure Peripheral Image Loader" + depends on OF && ARCH_QCOM + depends on QCOM_SMEM + depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n + depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n + select QCOM_MDT_LOADER + select QCOM_PIL_INFO + select QCOM_Q6V5_COMMON + select QCOM_RPROC_COMMON + select QCOM_SCM + help + Say y here to support the Qualcomm Secure Peripheral Image Loader + for the Hexagon based remote processors on e.g. IPQ5332. + + This is TrustZone wireless subsystem. The firmware is + verified and booted with the help of the Peripheral Authentication + System (PAS) in TrustZone. + config QCOM_SYSMON tristate "Qualcomm sysmon driver" depends on RPMSG diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 5ff4e2fee4ab..d4971b672812 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o +obj-$(CONFIG_QCOM_Q6V5_WCSS_SEC) += qcom_q6v5_wcss_sec.o obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o qcom_wcnss_pil-y += qcom_wcnss.o diff --git a/drivers/remoteproc/qcom_q6v5_wcss_sec.c b/drivers/remoteproc/qcom_q6v5_wcss_sec.c new file mode 100644 index 000000000000..30422c6c982d --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5_wcss_sec.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Linaro Ltd. + * Copyright (C) 2014 Sony Mobile Communications AB + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include <linux/clk.h> +#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/io.h> +#include <linux/mailbox_client.h> +#include <linux/mailbox/tmelcom-qmp.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/soc/qcom/mdt_loader.h> + +#include "qcom_common.h" +#include "qcom_q6v5.h" +#include "qcom_pil_info.h" + +#define WCSS_CRASH_REASON 421 + +#define WCSS_PAS_ID 0x6 +#define MPD_WCSS_PAS_ID 0xd + +#define Q6_WAIT_TIMEOUT (5 * HZ) + +struct wcss_sec { + struct device *dev; + struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_ssr ssr_subdev; + struct qcom_q6v5 q6; + phys_addr_t mem_phys; + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; + const struct wcss_data *desc; + + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + void *metadata; + size_t metadata_len; +}; + +struct wcss_data { + u32 pasid; + const char *ss_name; + bool auto_boot; + bool tmelcom; +}; + +static int wcss_sec_start(struct rproc *rproc) +{ + struct wcss_sec *wcss = rproc->priv; + struct device *dev = wcss->dev; + int ret; + + ret = qcom_q6v5_prepare(&wcss->q6); + if (ret) + return ret; + + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { + struct tmel_sec_auth tsa; + struct tmel_qmp_msg tqm; + + tsa.data = wcss->metadata; + tsa.size = wcss->metadata_len; + tsa.pas_id = wcss->desc->pasid; + tqm.msg = &tsa; + tqm.msg_id = TMEL_MSG_UID_SECBOOT_SEC_AUTH; + + ret = mbox_send_message(wcss->mbox_chan, (void *)&tqm); + if (ret < 0) { + dev_err(dev, "Failed to send message via mailbox\n"); + goto unprepare; + } + } else { + ret = qcom_scm_pas_auth_and_reset(wcss->desc->pasid); + if (ret) { + dev_err(dev, "wcss_reset failed\n"); + goto unprepare; + } + } + + ret = qcom_q6v5_wait_for_start(&wcss->q6, Q6_WAIT_TIMEOUT); + if (ret == -ETIMEDOUT) + dev_err(dev, "start timed out\n"); + +unprepare: + qcom_q6v5_unprepare(&wcss->q6); + + return ret; +} + +static int wcss_sec_stop(struct rproc *rproc) +{ + struct wcss_sec *wcss = rproc->priv; + struct device *dev = wcss->dev; + int ret; + + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { + struct tmel_sec_auth tsa = {0}; + struct tmel_qmp_msg tqm; + + tsa.pas_id = wcss->desc->pasid; + tqm.msg = &tsa; + tqm.msg_id = TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN; + + mbox_send_message(wcss->mbox_chan, (void *)&tqm); + } else { + ret = qcom_scm_pas_shutdown(wcss->desc->pasid); + if (ret) { + dev_err(dev, "not able to shutdown\n"); + return ret; + } + } + + qcom_q6v5_unprepare(&wcss->q6); + + return 0; +} + +static void *wcss_sec_da_to_va(struct rproc *rproc, u64 da, size_t len, + bool *is_iomem) +{ + struct wcss_sec *wcss = rproc->priv; + int offset; + + offset = da - wcss->mem_reloc; + if (offset < 0 || offset + len > wcss->mem_size) + return NULL; + + return wcss->mem_region + offset; +} + +static int wcss_sec_load(struct rproc *rproc, const struct firmware *fw) +{ + struct wcss_sec *wcss = rproc->priv; + struct device *dev = wcss->dev; + int ret; + + if (!IS_ERR_OR_NULL(wcss->mbox_chan)) { + wcss->metadata = qcom_mdt_read_metadata(fw, &wcss->metadata_len, + rproc->firmware, wcss->dev); + if (IS_ERR(wcss->metadata)) { + ret = PTR_ERR(wcss->metadata); + dev_err(wcss->dev, "error %d reading firmware %s metadata\n", + ret, rproc->firmware); + return ret; + } + + ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, wcss->desc->pasid, + wcss->mem_region, wcss->mem_phys, wcss->mem_size, + &wcss->mem_reloc); + if (ret) { + kfree(wcss->metadata); + return ret; + } + } else { + ret = qcom_mdt_load(dev, fw, rproc->firmware, wcss->desc->pasid, wcss->mem_region, + wcss->mem_phys, wcss->mem_size, &wcss->mem_reloc); + if (ret) + return ret; + } + + qcom_pil_info_store("wcss", wcss->mem_phys, wcss->mem_size); + + return 0; +} + +static unsigned long wcss_sec_panic(struct rproc *rproc) +{ + struct wcss_sec *wcss = rproc->priv; + + return qcom_q6v5_panic(&wcss->q6); +} + +static void wcss_sec_copy_segment(struct rproc *rproc, + struct rproc_dump_segment *segment, + void *dest, size_t offset, size_t size) +{ + struct wcss_sec *wcss = rproc->priv; + struct device *dev = wcss->dev; + + if (!segment->io_ptr) + segment->io_ptr = ioremap_wc(segment->da, segment->size); + + if (!segment->io_ptr) { + dev_err(dev, "Failed to ioremap segment %pad size 0x%zx\n", + &segment->da, segment->size); + return; + } + + if (offset + size <= segment->size) { + memcpy(dest, segment->io_ptr + offset, size); + } else { + iounmap(segment->io_ptr); + segment->io_ptr = NULL; + } +} + +static int wcss_sec_dump_segments(struct rproc *rproc, + const struct firmware *fw) +{ + struct device *dev = rproc->dev.parent; + struct reserved_mem *rmem = NULL; + struct device_node *node; + int num_segs, index; + int ret; + + /* + * Parse through additional reserved memory regions for the rproc + * and add them to the coredump segments + */ + num_segs = of_count_phandle_with_args(dev->of_node, + "memory-region", NULL); + for (index = 0; index < num_segs; index++) { + node = of_parse_phandle(dev->of_node, + "memory-region", index); + if (!node) + return -EINVAL; + + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(dev, "unable to acquire memory-region index %d num_segs %d\n", + index, num_segs); + return -EINVAL; + } + + dev_dbg(dev, "Adding segment 0x%pa size 0x%pa", + &rmem->base, &rmem->size); + ret = rproc_coredump_add_custom_segment(rproc, + rmem->base, + rmem->size, + wcss_sec_copy_segment, + NULL); + if (ret) + return ret; + } + + return 0; +} + +static const struct rproc_ops wcss_sec_ops = { + .start = wcss_sec_start, + .stop = wcss_sec_stop, + .da_to_va = wcss_sec_da_to_va, + .load = wcss_sec_load, + .get_boot_addr = rproc_elf_get_boot_addr, + .panic = wcss_sec_panic, + .parse_fw = wcss_sec_dump_segments, +}; + +static int wcss_sec_alloc_memory_region(struct wcss_sec *wcss) +{ + struct reserved_mem *rmem = NULL; + struct device_node *node; + struct device *dev = wcss->dev; + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "can't find phandle memory-region\n"); + return -EINVAL; + } + + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + + if (!rmem) { + dev_err(dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + wcss->mem_phys = rmem->base; + wcss->mem_reloc = rmem->base; + wcss->mem_size = rmem->size; + wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size); + if (!wcss->mem_region) { + dev_err(dev, "unable to map memory region: %pa+%pa\n", + &rmem->base, &rmem->size); + return -ENOMEM; + } + + return 0; +} + +static int wcss_sec_probe(struct platform_device *pdev) +{ + struct rproc *rproc; + struct wcss_sec *wcss; + struct clk *sleep_clk; + const char *fw_name = NULL; + const struct wcss_data *desc = of_device_get_match_data(&pdev->dev); + int ret; + + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &fw_name); + if (ret < 0) + return ret; + + rproc = devm_rproc_alloc(&pdev->dev, desc->ss_name, &wcss_sec_ops, + fw_name, sizeof(*wcss)); + if (!rproc) { + dev_err(&pdev->dev, "failed to allocate rproc\n"); + return -ENOMEM; + } + + wcss = rproc->priv; + wcss->dev = &pdev->dev; + wcss->desc = desc; + + ret = wcss_sec_alloc_memory_region(wcss); + if (ret) + return ret; + + sleep_clk = devm_clk_get_optional_enabled(&pdev->dev, "sleep"); + if (IS_ERR(sleep_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sleep_clk), + "Failed to get sleep clock\n"); + + ret = qcom_q6v5_init(&wcss->q6, pdev, rproc, + WCSS_CRASH_REASON, NULL, NULL); + if (ret) + return ret; + + qcom_add_glink_subdev(rproc, &wcss->glink_subdev, desc->ss_name); + qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, desc->ss_name); + + rproc->auto_boot = false; + rproc->dump_conf = RPROC_COREDUMP_INLINE; + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); + + if (desc->tmelcom) { + wcss->mbox_client.dev = wcss->dev; + wcss->mbox_client.knows_txdone = true; + wcss->mbox_client.tx_block = true; + wcss->mbox_chan = mbox_request_channel(&wcss->mbox_client, 0); + if (IS_ERR(wcss->mbox_chan)) + return dev_err_probe(wcss->dev, PTR_ERR(wcss->mbox_chan), + "mbox chan for IPC is missing\n"); + } + + ret = devm_rproc_add(&pdev->dev, rproc); + if (ret) + return ret; + + platform_set_drvdata(pdev, rproc); + + return 0; +} + +static void wcss_sec_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct wcss_sec *wcss = rproc->priv; + + mbox_free_channel(wcss->mbox_chan); + qcom_remove_glink_subdev(rproc, &wcss->glink_subdev); + qcom_remove_ssr_subdev(rproc, &wcss->ssr_subdev); + qcom_q6v5_deinit(&wcss->q6); +} + +static const struct wcss_data wcss_sec_ipq5332_res_init = { + .pasid = MPD_WCSS_PAS_ID, + .ss_name = "q6wcss", +}; + +static const struct wcss_data wcss_sec_ipq5424_res_init = { + .pasid = MPD_WCSS_PAS_ID, + .ss_name = "q6wcss", + .tmelcom = true, +}; + +static const struct wcss_data wcss_sec_ipq9574_res_init = { + .pasid = WCSS_PAS_ID, + .ss_name = "q6wcss", +}; + +static const struct of_device_id wcss_sec_of_match[] = { + { .compatible = "qcom,ipq5332-wcss-sec-pil", .data = &wcss_sec_ipq5332_res_init }, + { .compatible = "qcom,ipq5424-wcss-sec-pil", .data = &wcss_sec_ipq5424_res_init }, + { .compatible = "qcom,ipq9574-wcss-sec-pil", .data = &wcss_sec_ipq9574_res_init }, + { }, +}; +MODULE_DEVICE_TABLE(of, wcss_sec_of_match); + +static struct platform_driver wcss_sec_driver = { + .probe = wcss_sec_probe, + .remove = wcss_sec_remove, + .driver = { + .name = "qcom-wcss-secure-pil", + .of_match_table = wcss_sec_of_match, + }, +}; +module_platform_driver(wcss_sec_driver); + +MODULE_DESCRIPTION("Hexagon WCSS Secure Peripheral Image Loader"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b4795698d8c2..7b2159853345 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -472,6 +472,7 @@ enum rproc_dump_mechanism { * @node: list node related to the rproc segment list * @da: device address of the segment * @size: size of the segment + * @io_ptr: ptr to store the ioremapped dump segment * @priv: private data associated with the dump_segment * @dump: custom dump function to fill device memory segment associated * with coredump @@ -483,6 +484,7 @@ struct rproc_dump_segment { dma_addr_t da; size_t size; + void *io_ptr; void *priv; void (*dump)(struct rproc *rproc, struct rproc_dump_segment *segment, void *dest, size_t offset, size_t size);