firmware: qcom: scm: Peripheral Authentication Service
authorBjorn Andersson <bjorn.andersson@sonymobile.com>
Wed, 23 Sep 2015 19:56:12 +0000 (12:56 -0700)
committerAndy Gross <andy.gross@linaro.org>
Sat, 25 Jun 2016 03:53:50 +0000 (22:53 -0500)
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>
Signed-off-by: Andy Gross <andy.gross@linaro.org>
drivers/firmware/qcom_scm-32.c
drivers/firmware/qcom_scm-64.c
drivers/firmware/qcom_scm.c
drivers/firmware/qcom_scm.h
include/linux/qcom_scm.h

index bbf1780b8781ad4ddb5f683e634a1035bc87b4ac..7cb6a5d648fa5dda535f39f17827d5c802f36471 100644 (file)
@@ -458,3 +458,92 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
 void __qcom_scm_init(void)
 {
 }
+
+bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
+{
+       __le32 out;
+       __le32 in;
+       int ret;
+
+       in = cpu_to_le32(peripheral);
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                           QCOM_SCM_PAS_IS_SUPPORTED_CMD,
+                           &in, sizeof(in),
+                           &out, sizeof(out));
+
+       return ret ? false : !!out;
+}
+
+int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
+                             dma_addr_t metadata_phys)
+{
+       __le32 scm_ret;
+       int ret;
+       struct {
+               __le32 proc;
+               __le32 image_addr;
+       } request;
+
+       request.proc = cpu_to_le32(peripheral);
+       request.image_addr = cpu_to_le32(metadata_phys);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                           QCOM_SCM_PAS_INIT_IMAGE_CMD,
+                           &request, sizeof(request),
+                           &scm_ret, sizeof(scm_ret));
+
+       return ret ? : le32_to_cpu(scm_ret);
+}
+
+int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
+                            phys_addr_t addr, phys_addr_t size)
+{
+       __le32 scm_ret;
+       int ret;
+       struct {
+               __le32 proc;
+               __le32 addr;
+               __le32 len;
+       } request;
+
+       request.proc = cpu_to_le32(peripheral);
+       request.addr = cpu_to_le32(addr);
+       request.len = cpu_to_le32(size);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                           QCOM_SCM_PAS_MEM_SETUP_CMD,
+                           &request, sizeof(request),
+                           &scm_ret, sizeof(scm_ret));
+
+       return ret ? : le32_to_cpu(scm_ret);
+}
+
+int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
+{
+       __le32 out;
+       __le32 in;
+       int ret;
+
+       in = cpu_to_le32(peripheral);
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                           QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
+                           &in, sizeof(in),
+                           &out, sizeof(out));
+
+       return ret ? : le32_to_cpu(out);
+}
+
+int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
+{
+       __le32 out;
+       __le32 in;
+       int ret;
+
+       in = cpu_to_le32(peripheral);
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                           QCOM_SCM_PAS_SHUTDOWN_CMD,
+                           &in, sizeof(in),
+                           &out, sizeof(out));
+
+       return ret ? : le32_to_cpu(out);
+}
index 01949f1828f49ba59bae4d3101d13b1ca6af82f7..3ac249037dbfa5475d9ce3a68efc2a7fc626b99a 100644 (file)
 #define MAX_QCOM_SCM_ARGS 10
 #define MAX_QCOM_SCM_RETS 3
 
+enum qcom_scm_arg_types {
+       QCOM_SCM_VAL,
+       QCOM_SCM_RO,
+       QCOM_SCM_RW,
+       QCOM_SCM_BUFVAL,
+};
+
 #define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\
                           (((a) & 0x3) << 4) | \
                           (((b) & 0x3) << 6) | \
@@ -253,3 +260,85 @@ void __qcom_scm_init(void)
        else
                qcom_smccc_convention = ARM_SMCCC_SMC_32;
 }
+
+bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
+{
+       int ret;
+       struct qcom_scm_desc desc = {0};
+       struct arm_smccc_res res;
+
+       desc.args[0] = peripheral;
+       desc.arginfo = QCOM_SCM_ARGS(1);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                               QCOM_SCM_PAS_IS_SUPPORTED_CMD,
+                               &desc, &res);
+
+       return ret ? false : !!res.a1;
+}
+
+int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
+                             dma_addr_t metadata_phys)
+{
+       int ret;
+       struct qcom_scm_desc desc = {0};
+       struct arm_smccc_res res;
+
+       desc.args[0] = peripheral;
+       desc.args[1] = metadata_phys;
+       desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
+                               &desc, &res);
+
+       return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
+                             phys_addr_t addr, phys_addr_t size)
+{
+       int ret;
+       struct qcom_scm_desc desc = {0};
+       struct arm_smccc_res res;
+
+       desc.args[0] = peripheral;
+       desc.args[1] = addr;
+       desc.args[2] = size;
+       desc.arginfo = QCOM_SCM_ARGS(3);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD,
+                               &desc, &res);
+
+       return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
+{
+       int ret;
+       struct qcom_scm_desc desc = {0};
+       struct arm_smccc_res res;
+
+       desc.args[0] = peripheral;
+       desc.arginfo = QCOM_SCM_ARGS(1);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+                               QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
+                               &desc, &res);
+
+       return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
+{
+       int ret;
+       struct qcom_scm_desc desc = {0};
+       struct arm_smccc_res res;
+
+       desc.args[0] = peripheral;
+       desc.arginfo = QCOM_SCM_ARGS(1);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD,
+                       &desc, &res);
+
+       return ret ? : res.a1;
+}
index fca0744c1b73cc2ccb925031393f36373346ed09..ec46c68e3e832915c1c850102857b62d5a9f7440 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/export.h>
+#include <linux/dma-mapping.h>
 #include <linux/types.h>
 #include <linux/qcom_scm.h>
 #include <linux/of.h>
@@ -149,6 +150,139 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *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(__scm->dev, QCOM_SCM_SVC_PIL,
+                                          QCOM_SCM_PAS_IS_SUPPORTED_CMD);
+       if (ret <= 0)
+               return false;
+
+       return __qcom_scm_pas_supported(__scm->dev, 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)
+{
+       dma_addr_t mdata_phys;
+       void *mdata_buf;
+       int ret;
+
+       /*
+        * 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(__scm->dev, size, &mdata_phys,
+                                      GFP_KERNEL);
+       if (!mdata_buf) {
+               dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
+               return -ENOMEM;
+       }
+       memcpy(mdata_buf, metadata, size);
+
+       ret = qcom_scm_clk_enable();
+       if (ret)
+               goto free_metadata;
+
+       ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
+
+       qcom_scm_clk_disable();
+
+free_metadata:
+       dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
+
+       return ret;
+}
+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)
+{
+       int ret;
+
+       ret = qcom_scm_clk_enable();
+       if (ret)
+               return ret;
+
+       ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
+       qcom_scm_clk_disable();
+
+       return ret;
+}
+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)
+{
+       int ret;
+
+       ret = qcom_scm_clk_enable();
+       if (ret)
+               return ret;
+
+       ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
+       qcom_scm_clk_disable();
+
+       return ret;
+}
+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)
+{
+       int ret;
+
+       ret = qcom_scm_clk_enable();
+       if (ret)
+               return ret;
+
+       ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
+       qcom_scm_clk_disable();
+
+       return ret;
+}
+EXPORT_SYMBOL(qcom_scm_pas_shutdown);
+
 static int qcom_scm_probe(struct platform_device *pdev)
 {
        struct qcom_scm *scm;
index 0ea55d7fb076111a35e01eb2214e01f392682efd..1a16ff925d6d259037ec6337c1a5455961015038 100644 (file)
@@ -40,6 +40,20 @@ extern int __qcom_scm_hdcp_req(struct device *dev,
 
 extern void __qcom_scm_init(void);
 
+#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(struct device *dev, u32 peripheral);
+extern int  __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
+               dma_addr_t metadata_phys);
+extern int  __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
+               phys_addr_t addr, phys_addr_t size);
+extern int  __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral);
+extern int  __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral);
+
 /* common error codes */
 #define QCOM_SCM_V2_EBUSY      -12
 #define QCOM_SCM_ENOMEM                -5
index 9e12000914b3451107abdde161e1ea64b7cfeb5e..cc32ab852fbcb3b83e90989330ee80e512faf61d 100644 (file)
@@ -29,6 +29,14 @@ 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