Skip to content

Commit

Permalink
scsi: firmware: qcom_scm: Add support for programming inline crypto keys
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ebiggers authored and martinkpetersen committed Jul 25, 2020
1 parent 3eef38a commit 0f20651
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
101 changes: 101 additions & 0 deletions drivers/firmware/qcom_scm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/**
* 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.
*
Expand Down
4 changes: 4 additions & 0 deletions drivers/firmware/qcom_scm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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_HDCP_INVOKE 0x01

Expand Down
19 changes: 19 additions & 0 deletions include/linux/qcom_scm.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ enum qcom_scm_sec_dev_id {
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_MSS_MSA 0xF
#define QCOM_SCM_VMID_WLAN 0x18
Expand Down Expand Up @@ -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,
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 int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
u32 *resp);
Expand Down Expand Up @@ -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,
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 int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
u32 *resp) { return -ENODEV; }
Expand Down

0 comments on commit 0f20651

Please sign in to comment.