scsi: firmware: qcom_scm: Add support for programming inline crypto keys
Add support for the Inline Crypto Engine (ICE) key programming interface that's needed for the ufs-qcom driver to use inline encryption on Snapdragon SoCs. This interface consists of two SCM calls: one to program a key into a keyslot, and one to invalidate a keyslot. Although the UFS specification defines a standard way to do this, on these SoCs the Linux kernel isn't permitted to access the needed crypto configuration registers directly; these SCM calls must be used instead. Link: https://lore.kernel.org/r/20200710072013.177481-2-ebiggers@kernel.org Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
3eef38a143
commit
0f20651474
3 changed files with 124 additions and 0 deletions
|
@ -923,6 +923,107 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qcom_scm_ocmem_unlock);
|
EXPORT_SYMBOL(qcom_scm_ocmem_unlock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qcom_scm_ice_available() - Is the ICE key programming interface available?
|
||||||
|
*
|
||||||
|
* Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and
|
||||||
|
* qcom_scm_ice_set_key() are available.
|
||||||
|
*/
|
||||||
|
bool qcom_scm_ice_available(void)
|
||||||
|
{
|
||||||
|
return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
|
||||||
|
QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
|
||||||
|
__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
|
||||||
|
QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_scm_ice_available);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
|
||||||
|
* @index: the keyslot to invalidate
|
||||||
|
*
|
||||||
|
* The UFSHCI standard defines a standard way to do this, but it doesn't work on
|
||||||
|
* these SoCs; only this SCM call does.
|
||||||
|
*
|
||||||
|
* Return: 0 on success; -errno on failure.
|
||||||
|
*/
|
||||||
|
int qcom_scm_ice_invalidate_key(u32 index)
|
||||||
|
{
|
||||||
|
struct qcom_scm_desc desc = {
|
||||||
|
.svc = QCOM_SCM_SVC_ES,
|
||||||
|
.cmd = QCOM_SCM_ES_INVALIDATE_ICE_KEY,
|
||||||
|
.arginfo = QCOM_SCM_ARGS(1),
|
||||||
|
.args[0] = index,
|
||||||
|
.owner = ARM_SMCCC_OWNER_SIP,
|
||||||
|
};
|
||||||
|
|
||||||
|
return qcom_scm_call(__scm->dev, &desc, NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_scm_ice_invalidate_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qcom_scm_ice_set_key() - Set an inline encryption key
|
||||||
|
* @index: the keyslot into which to set the key
|
||||||
|
* @key: the key to program
|
||||||
|
* @key_size: the size of the key in bytes
|
||||||
|
* @cipher: the encryption algorithm the key is for
|
||||||
|
* @data_unit_size: the encryption data unit size, i.e. the size of each
|
||||||
|
* individual plaintext and ciphertext. Given in 512-byte
|
||||||
|
* units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc.
|
||||||
|
*
|
||||||
|
* Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it
|
||||||
|
* can then be used to encrypt/decrypt UFS I/O requests inline.
|
||||||
|
*
|
||||||
|
* The UFSHCI standard defines a standard way to do this, but it doesn't work on
|
||||||
|
* these SoCs; only this SCM call does.
|
||||||
|
*
|
||||||
|
* Return: 0 on success; -errno on failure.
|
||||||
|
*/
|
||||||
|
int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
|
||||||
|
enum qcom_scm_ice_cipher cipher, u32 data_unit_size)
|
||||||
|
{
|
||||||
|
struct qcom_scm_desc desc = {
|
||||||
|
.svc = QCOM_SCM_SVC_ES,
|
||||||
|
.cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY,
|
||||||
|
.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RW,
|
||||||
|
QCOM_SCM_VAL, QCOM_SCM_VAL,
|
||||||
|
QCOM_SCM_VAL),
|
||||||
|
.args[0] = index,
|
||||||
|
.args[2] = key_size,
|
||||||
|
.args[3] = cipher,
|
||||||
|
.args[4] = data_unit_size,
|
||||||
|
.owner = ARM_SMCCC_OWNER_SIP,
|
||||||
|
};
|
||||||
|
void *keybuf;
|
||||||
|
dma_addr_t key_phys;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 'key' may point to vmalloc()'ed memory, but we need to pass a
|
||||||
|
* physical address that's been properly flushed. The sanctioned way to
|
||||||
|
* do this is by using the DMA API. But as is best practice for crypto
|
||||||
|
* keys, we also must wipe the key after use. This makes kmemdup() +
|
||||||
|
* dma_map_single() not clearly correct, since the DMA API can use
|
||||||
|
* bounce buffers. Instead, just use dma_alloc_coherent(). Programming
|
||||||
|
* keys is normally rare and thus not performance-critical.
|
||||||
|
*/
|
||||||
|
|
||||||
|
keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!keybuf)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy(keybuf, key, key_size);
|
||||||
|
desc.args[1] = key_phys;
|
||||||
|
|
||||||
|
ret = qcom_scm_call(__scm->dev, &desc, NULL);
|
||||||
|
|
||||||
|
memzero_explicit(keybuf, key_size);
|
||||||
|
|
||||||
|
dma_free_coherent(__scm->dev, key_size, keybuf, key_phys);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_scm_ice_set_key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
|
* qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
|
||||||
*
|
*
|
||||||
|
|
|
@ -103,6 +103,10 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||||
#define QCOM_SCM_OCMEM_LOCK_CMD 0x01
|
#define QCOM_SCM_OCMEM_LOCK_CMD 0x01
|
||||||
#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02
|
#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02
|
||||||
|
|
||||||
|
#define QCOM_SCM_SVC_ES 0x10 /* Enterprise Security */
|
||||||
|
#define QCOM_SCM_ES_INVALIDATE_ICE_KEY 0x03
|
||||||
|
#define QCOM_SCM_ES_CONFIG_SET_ICE_KEY 0x04
|
||||||
|
|
||||||
#define QCOM_SCM_SVC_HDCP 0x11
|
#define QCOM_SCM_SVC_HDCP 0x11
|
||||||
#define QCOM_SCM_HDCP_INVOKE 0x01
|
#define QCOM_SCM_HDCP_INVOKE 0x01
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,13 @@ enum qcom_scm_sec_dev_id {
|
||||||
QCOM_SCM_ICE_DEV_ID = 20,
|
QCOM_SCM_ICE_DEV_ID = 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum qcom_scm_ice_cipher {
|
||||||
|
QCOM_SCM_ICE_CIPHER_AES_128_XTS = 0,
|
||||||
|
QCOM_SCM_ICE_CIPHER_AES_128_CBC = 1,
|
||||||
|
QCOM_SCM_ICE_CIPHER_AES_256_XTS = 3,
|
||||||
|
QCOM_SCM_ICE_CIPHER_AES_256_CBC = 4,
|
||||||
|
};
|
||||||
|
|
||||||
#define QCOM_SCM_VMID_HLOS 0x3
|
#define QCOM_SCM_VMID_HLOS 0x3
|
||||||
#define QCOM_SCM_VMID_MSS_MSA 0xF
|
#define QCOM_SCM_VMID_MSS_MSA 0xF
|
||||||
#define QCOM_SCM_VMID_WLAN 0x18
|
#define QCOM_SCM_VMID_WLAN 0x18
|
||||||
|
@ -88,6 +95,12 @@ extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset,
|
||||||
extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset,
|
extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset,
|
||||||
u32 size);
|
u32 size);
|
||||||
|
|
||||||
|
extern bool qcom_scm_ice_available(void);
|
||||||
|
extern int qcom_scm_ice_invalidate_key(u32 index);
|
||||||
|
extern int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
|
||||||
|
enum qcom_scm_ice_cipher cipher,
|
||||||
|
u32 data_unit_size);
|
||||||
|
|
||||||
extern bool qcom_scm_hdcp_available(void);
|
extern bool qcom_scm_hdcp_available(void);
|
||||||
extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
|
extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
|
||||||
u32 *resp);
|
u32 *resp);
|
||||||
|
@ -138,6 +151,12 @@ static inline int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset,
|
||||||
static inline int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id,
|
static inline int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id,
|
||||||
u32 offset, u32 size) { return -ENODEV; }
|
u32 offset, u32 size) { return -ENODEV; }
|
||||||
|
|
||||||
|
static inline bool qcom_scm_ice_available(void) { return false; }
|
||||||
|
static inline int qcom_scm_ice_invalidate_key(u32 index) { return -ENODEV; }
|
||||||
|
static inline int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
|
||||||
|
enum qcom_scm_ice_cipher cipher,
|
||||||
|
u32 data_unit_size) { return -ENODEV; }
|
||||||
|
|
||||||
static inline bool qcom_scm_hdcp_available(void) { return false; }
|
static inline bool qcom_scm_hdcp_available(void) { return false; }
|
||||||
static inline int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
|
static inline int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
|
||||||
u32 *resp) { return -ENODEV; }
|
u32 *resp) { return -ENODEV; }
|
||||||
|
|
Loading…
Add table
Reference in a new issue