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@xxxxxxxxxxxxxx> --- 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(+) 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 -- 1.8.2.2 -- To unsubscribe from this list: send the line "unsubscribe linux-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html