diff mbox

firmware: qcom: scm: Peripheral Authentication Service

Message ID 1435693579-16109-1-git-send-email-bjorn.andersson@sonymobile.com (mailing list archive)
State Superseded, archived
Delegated to: Andy Gross
Headers show

Commit Message

Bjorn Andersson June 30, 2015, 7:46 p.m. UTC
This adds the Peripheral Authentication Service (PAS) interface to the
Qualcomm SCM interface. The API is used to authenticate and boot a range
of external processors in various Qualcomm platforms.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---

While adding the 64 bit interface it might be of interest to extract the dma
allocation in __qcom_scm_pas_init_image() into the common code and pass the
physical pointer to instead, but I haven't looked at the differences to the 64
bit interface.

 drivers/firmware/qcom_scm-32.c | 93 ++++++++++++++++++++++++++++++++++++++++++
 drivers/firmware/qcom_scm.c    | 78 +++++++++++++++++++++++++++++++++++
 drivers/firmware/qcom_scm.h    | 12 ++++++
 include/linux/qcom_scm.h       |  6 +++
 4 files changed, 189 insertions(+)

Comments

Andy Gross June 30, 2015, 11:22 p.m. UTC | #1
On Tue, Jun 30, 2015 at 12:46:19PM -0700, Bjorn Andersson wrote:

<snip>

> While adding the 64 bit interface it might be of interest to extract the dma
> allocation in __qcom_scm_pas_init_image() into the common code and pass the
> physical pointer to instead, but I haven't looked at the differences to the 64
> bit interface.

I am currently delving into this.  I'll take this patch and see what needs to be
done.

<snip>

> +int __qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
> +{
> +	dma_addr_t mdata_phys;
> +	void *mdata_buf;
> +	u32 scm_ret;
> +	int ret;
> +	struct pas_init_image_req {
> +		u32 proc;
> +		u32 image_addr;
> +	} request;
> +
> +	/*
> +	 * During the scm call memory protection will be enabled for the meta
> +	 * data blob, so make sure it's physically contiguous, 4K aligned and
> +	 * non-cachable to avoid XPU violations.
> +	 */
> +	mdata_buf = dma_alloc_coherent(NULL, size, &mdata_phys, GFP_KERNEL);

Not sure this gives you a 4k aligned buffer every time.

<snip>

Otherwise everything looks fine to me.
Stephen Boyd July 2, 2015, 4:19 p.m. UTC | #2
On 06/30/2015 12:46 PM, Bjorn Andersson wrote:
> +
> +int __qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
> +{
> +	dma_addr_t mdata_phys;
> +	void *mdata_buf;
> +	u32 scm_ret;
> +	int ret;
> +	struct pas_init_image_req {
> +		u32 proc;
> +		u32 image_addr;

Please mark these as __le32 and do the appropriate conversions to little
endian. Also consider removing the struct name and just make it
anonymous, i.e.

    struct {
        __le32 proc;
        __le32 image_addr;
    } cmd;

> +	} request;
> +
> +	/*
> +	 * During the scm call memory protection will be enabled for the meta
> +	 * data blob, so make sure it's physically contiguous, 4K aligned and
> +	 * non-cachable to avoid XPU violations.
> +	 */
> +	mdata_buf = dma_alloc_coherent(NULL, size, &mdata_phys, GFP_KERNEL);
> +	if (!mdata_buf) {
> +		pr_err("Allocation of metadata buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	memcpy(mdata_buf, metadata, size);
> +
> +	request.proc = peripheral;
> +	request.image_addr = mdata_phys;
> +
> +	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
> +			    &request, sizeof(request),
> +			    &scm_ret, sizeof(scm_ret));
> +
> +	dma_free_coherent(NULL, size, mdata_buf, mdata_phys);
> +
> +	return ret ? : scm_ret;
> +}
> +
> +int __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
> +{
> +	u32 scm_ret;
> +	int ret;
> +	struct pas_init_image_req {
> +		u32 proc;
> +		u32 addr;
> +		u32 len;
> +	} request;

Same comment here.

> +
> +	request.proc = peripheral;
> +	request.addr = addr;
> +	request.len = size;
> +
> +	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD,
> +			    &request, sizeof(request),
> +			    &scm_ret, sizeof(scm_ret));
> +
> +	return ret ? : scm_ret;
> +}
> +
>
Bjorn Andersson July 2, 2015, 5:37 p.m. UTC | #3
On Thu 02 Jul 09:19 PDT 2015, Stephen Boyd wrote:

> On 06/30/2015 12:46 PM, Bjorn Andersson wrote:
> > +
> > +int __qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
> > +{
> > +	dma_addr_t mdata_phys;
> > +	void *mdata_buf;
> > +	u32 scm_ret;
> > +	int ret;
> > +	struct pas_init_image_req {
> > +		u32 proc;
> > +		u32 image_addr;
> 
> Please mark these as __le32 and do the appropriate conversions to little
> endian.

Just out of curiosity, are there any big endian systems out there that
will run this code?

Either way I will fix this, to follow the style of the other parts of
this file.

> Also consider removing the struct name and just make it
> anonymous, i.e.
> 
>     struct {
>         __le32 proc;
>         __le32 image_addr;
>     } cmd;
> 

Right, there's no reason to name these.

Thanks,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Boyd July 2, 2015, 6:37 p.m. UTC | #4
On 07/02/2015 10:37 AM, Bjorn Andersson wrote:
> On Thu 02 Jul 09:19 PDT 2015, Stephen Boyd wrote:
>
>> On 06/30/2015 12:46 PM, Bjorn Andersson wrote:
>>> +
>>> +int __qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
>>> +{
>>> +	dma_addr_t mdata_phys;
>>> +	void *mdata_buf;
>>> +	u32 scm_ret;
>>> +	int ret;
>>> +	struct pas_init_image_req {
>>> +		u32 proc;
>>> +		u32 image_addr;
>> Please mark these as __le32 and do the appropriate conversions to little
>> endian.
> Just out of curiosity, are there any big endian systems out there that
> will run this code?

The option is available to turn on if someone wants to. I'm not aware of
anyone that's doing it besides me though.
diff mbox

Patch

diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c
index 1bd6f9c34331..5674d36a9df9 100644
--- a/drivers/firmware/qcom_scm-32.c
+++ b/drivers/firmware/qcom_scm-32.c
@@ -23,6 +23,7 @@ 
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/qcom_scm.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/outercache.h>
 #include <asm/cacheflush.h>
@@ -501,3 +502,95 @@  int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
 	return qcom_scm_call(QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP,
 		req, req_cnt * sizeof(*req), resp, sizeof(*resp));
 }
+
+bool __qcom_scm_pas_supported(u32 peripheral)
+{
+	u32 ret_val;
+	int ret;
+
+	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_IS_SUPPORTED_CMD,
+			    &peripheral, sizeof(peripheral),
+			    &ret_val, sizeof(ret_val));
+
+	return ret ? false : !!ret_val;
+}
+
+int __qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
+{
+	dma_addr_t mdata_phys;
+	void *mdata_buf;
+	u32 scm_ret;
+	int ret;
+	struct pas_init_image_req {
+		u32 proc;
+		u32 image_addr;
+	} request;
+
+	/*
+	 * During the scm call memory protection will be enabled for the meta
+	 * data blob, so make sure it's physically contiguous, 4K aligned and
+	 * non-cachable to avoid XPU violations.
+	 */
+	mdata_buf = dma_alloc_coherent(NULL, size, &mdata_phys, GFP_KERNEL);
+	if (!mdata_buf) {
+		pr_err("Allocation of metadata buffer failed.\n");
+		return -ENOMEM;
+	}
+	memcpy(mdata_buf, metadata, size);
+
+	request.proc = peripheral;
+	request.image_addr = mdata_phys;
+
+	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
+			    &request, sizeof(request),
+			    &scm_ret, sizeof(scm_ret));
+
+	dma_free_coherent(NULL, size, mdata_buf, mdata_phys);
+
+	return ret ? : scm_ret;
+}
+
+int __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
+{
+	u32 scm_ret;
+	int ret;
+	struct pas_init_image_req {
+		u32 proc;
+		u32 addr;
+		u32 len;
+	} request;
+
+	request.proc = peripheral;
+	request.addr = addr;
+	request.len = size;
+
+	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD,
+			    &request, sizeof(request),
+			    &scm_ret, sizeof(scm_ret));
+
+	return ret ? : scm_ret;
+}
+
+int __qcom_scm_pas_auth_and_reset(u32 peripheral)
+{
+	u32 scm_ret;
+	int ret;
+
+	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
+			    &peripheral, sizeof(peripheral),
+			    &scm_ret, sizeof(scm_ret));
+
+	return ret ? : scm_ret;
+}
+
+int __qcom_scm_pas_shutdown(u32 peripheral)
+{
+	u32 scm_ret;
+	int ret;
+
+	ret = qcom_scm_call(QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD,
+			    &peripheral, sizeof(peripheral),
+			    &scm_ret, sizeof(scm_ret));
+
+	return ret ? : scm_ret;
+}
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 45c008d68891..ee7150d93b29 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -94,3 +94,81 @@  int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
 	return __qcom_scm_hdcp_req(req, req_cnt, resp);
 }
 EXPORT_SYMBOL(qcom_scm_hdcp_req);
+
+/**
+ * qcom_scm_pas_supported() - Check if the peripheral authentication service is
+ *			      available for the given peripherial
+ * @peripheral:	peripheral id
+ *
+ * Returns true if PAS is supported for this peripheral, otherwise false.
+ */
+bool qcom_scm_pas_supported(u32 peripheral)
+{
+	int ret;
+
+	ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_PIL,
+					   QCOM_SCM_PAS_IS_SUPPORTED_CMD);
+	if (ret <= 0)
+		return false;
+
+	return __qcom_scm_pas_supported(peripheral);
+}
+EXPORT_SYMBOL(qcom_scm_pas_supported);
+
+/**
+ * qcom_scm_pas_init_image() - Initialize peripheral authentication service
+ *			       state machine for a given peripheral, using the
+ *			       metadata
+ * @peripheral: peripheral 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
+ *
+ * Returns 0 on success.
+ */
+int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
+{
+	return __qcom_scm_pas_init_image(peripheral, metadata, size);
+}
+EXPORT_SYMBOL(qcom_scm_pas_init_image);
+
+/**
+ * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
+ *			      for firmware loading
+ * @peripheral:	peripheral 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 peripheral, phys_addr_t addr, phys_addr_t size)
+{
+	return __qcom_scm_pas_mem_setup(peripheral, addr, size);
+}
+EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
+
+/**
+ * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
+ *				   and reset the remote processor
+ * @peripheral:	peripheral id
+ *
+ * Return 0 on success.
+ */
+int qcom_scm_pas_auth_and_reset(u32 peripheral)
+{
+	return __qcom_scm_pas_auth_and_reset(peripheral);
+}
+EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
+
+/**
+ * qcom_scm_pas_shutdown() - Shut down the remote processor
+ * @peripheral: peripheral id
+ *
+ * Returns 0 on success.
+ */
+int qcom_scm_pas_shutdown(u32 peripheral)
+{
+	return __qcom_scm_pas_shutdown(peripheral);
+}
+EXPORT_SYMBOL(qcom_scm_pas_shutdown);
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index 2cce75c08b99..c0700590193a 100644
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -36,6 +36,18 @@  extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id);
 extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
 		u32 *resp);
 
+#define QCOM_SCM_SVC_PIL		0x2
+#define QCOM_SCM_PAS_INIT_IMAGE_CMD	0x1
+#define QCOM_SCM_PAS_MEM_SETUP_CMD	0x2
+#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD	0x5
+#define QCOM_SCM_PAS_SHUTDOWN_CMD	0x6
+#define QCOM_SCM_PAS_IS_SUPPORTED_CMD	0x7
+extern bool __qcom_scm_pas_supported(u32 peripheral);
+extern int  __qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size);
+extern int  __qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size);
+extern int  __qcom_scm_pas_auth_and_reset(u32 peripheral);
+extern int  __qcom_scm_pas_shutdown(u32 peripheral);
+
 /* common error codes */
 #define QCOM_SCM_ENOMEM		-5
 #define QCOM_SCM_EOPNOTSUPP	-4
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index 6e7d5ec65838..46d41e44d432 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -27,6 +27,12 @@  extern bool qcom_scm_hdcp_available(void);
 extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
 		u32 *resp);
 
+extern bool qcom_scm_pas_supported(u32 peripheral);
+extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size);
+extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size);
+extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
+extern int qcom_scm_pas_shutdown(u32 peripheral);
+
 #define QCOM_SCM_CPU_PWR_DOWN_L2_ON	0x0
 #define QCOM_SCM_CPU_PWR_DOWN_L2_OFF	0x1