firmware: qcom: scm: Peripheral Authentication Service
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>
This commit is contained in:
Родитель
3680a4a974
Коммит
f01e90fe34
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,13 @@
|
|||
#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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче