Message ID | 20200304064942.371978-2-ebiggers@kernel.org (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Inline crypto support on DragonBoard 845c | expand |
Quoting Eric Biggers (2020-03-03 22:49:39) > diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c > index 059bb0fbae9e..7fb9f606250f 100644 > --- a/drivers/firmware/qcom_scm.c > +++ b/drivers/firmware/qcom_scm.c > @@ -926,6 +927,101 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) [...] > + > +/** > + * 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, int key_size, > + enum qcom_scm_ice_cipher cipher, int data_unit_size) Why not make key_size and data_unit_size unsigned? > +{ > + 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, > + }; > + u8 *keybuf; > + dma_addr_t key_phys; > + int ret; > + > + keybuf = kmemdup(key, key_size, GFP_KERNEL); Is this to make the key physically contiguous? Probably worth a comment to help others understand why this is here. > + if (!keybuf) > + return -ENOMEM; > + > + key_phys = dma_map_single(__scm->dev, keybuf, key_size, DMA_TO_DEVICE); > + if (dma_mapping_error(__scm->dev, key_phys)) { > + ret = -ENOMEM; > + goto out; > + } > + desc.args[1] = key_phys; > + > + ret = qcom_scm_call(__scm->dev, &desc, NULL); > + > + dma_unmap_single(__scm->dev, key_phys, key_size, DMA_TO_DEVICE); > +out: > + kzfree(keybuf); And this is because we want to clear key contents out of the slab? What about if the dma_map_single() bounces to a bounce buffer? I think that isn't going to happen because __scm->dev is just some firmware device that doesn't require bounce buffers but it's worth another comment to clarify this. > + return ret; > +} > +EXPORT_SYMBOL(qcom_scm_ice_set_key); > + > /** > * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. > *
On Wed, Mar 04, 2020 at 09:04:49AM -0800, Stephen Boyd wrote: > Quoting Eric Biggers (2020-03-03 22:49:39) > > diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c > > index 059bb0fbae9e..7fb9f606250f 100644 > > --- a/drivers/firmware/qcom_scm.c > > +++ b/drivers/firmware/qcom_scm.c > > @@ -926,6 +927,101 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) > [...] > > + > > +/** > > + * 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, int key_size, > > + enum qcom_scm_ice_cipher cipher, int data_unit_size) > > Why not make key_size and data_unit_size unsigned? No reason other than that 'int' is fewer characters and is a good default when no other particular type is warranted. But I can change them to 'unsigned int' if people prefer to make it clearer that they can't be negative. > > > +{ > > + 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, > > + }; > > + u8 *keybuf; > > + dma_addr_t key_phys; > > + int ret; > > + > > + keybuf = kmemdup(key, key_size, GFP_KERNEL); > > Is this to make the key physically contiguous? Probably worth a comment > to help others understand why this is here. Yes, dma_map_single() requires physically contiguous memory. I'll add a comment. > > > + if (!keybuf) > > + return -ENOMEM; > > + > > + key_phys = dma_map_single(__scm->dev, keybuf, key_size, DMA_TO_DEVICE); > > + if (dma_mapping_error(__scm->dev, key_phys)) { > > + ret = -ENOMEM; > > + goto out; > > + } > > + desc.args[1] = key_phys; > > + > > + ret = qcom_scm_call(__scm->dev, &desc, NULL); > > + > > + dma_unmap_single(__scm->dev, key_phys, key_size, DMA_TO_DEVICE); > > +out: > > + kzfree(keybuf); > > And this is because we want to clear key contents out of the slab? What > about if the dma_map_single() bounces to a bounce buffer? I think that > isn't going to happen because __scm->dev is just some firmware device > that doesn't require bounce buffers but it's worth another comment to > clarify this. Yes, in crypto-related code we always try to wipe keys after use. I don't think a bounce buffer would actually be used here, though it would be preferable to wipe the DMA memory too so that we don't rely on that. But I didn't see how to do that; I'm not a DMA API expert. Maybe dma_alloc_coherent()? This isn't really performance-critical. - Eric
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 059bb0fbae9e..7fb9f606250f 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -9,6 +9,7 @@ #include <linux/dma-direct.h> #include <linux/dma-mapping.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/types.h> #include <linux/qcom_scm.h> #include <linux/of.h> @@ -926,6 +927,101 @@ 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, int key_size, + enum qcom_scm_ice_cipher cipher, int 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, + }; + u8 *keybuf; + dma_addr_t key_phys; + int ret; + + keybuf = kmemdup(key, key_size, GFP_KERNEL); + if (!keybuf) + return -ENOMEM; + + key_phys = dma_map_single(__scm->dev, keybuf, key_size, DMA_TO_DEVICE); + if (dma_mapping_error(__scm->dev, key_phys)) { + ret = -ENOMEM; + goto out; + } + desc.args[1] = key_phys; + + ret = qcom_scm_call(__scm->dev, &desc, NULL); + + dma_unmap_single(__scm->dev, key_phys, key_size, DMA_TO_DEVICE); +out: + kzfree(keybuf); + return ret; +} +EXPORT_SYMBOL(qcom_scm_ice_set_key); + /** * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. * diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index d9ed670da222..38ea614d29fe 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -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 diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 3d6a24697761..8ca90f192aeb 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -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 @@ -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, int key_size, + enum qcom_scm_ice_cipher cipher, + int 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); @@ -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, int key_size, + enum qcom_scm_ice_cipher cipher, + int 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; }