Message ID | 20240127232436.2632187-6-quic_gaurkash@quicinc.com |
---|---|
State | Superseded |
Headers | show |
Series | Hardware wrapped key support for qcom ice and ufs | expand |
On 1/28/2024 4:44 AM, Gaurav Kashyap wrote: > Now that HWKM support is added to ICE, extend the ICE > driver to support hardware wrapped keys programming coming > in from the storage controllers (ufs and emmc). This is > similar to standard keys where the call is forwarded to > Trustzone, however certain wrapped key and HWKM specific > actions has to be performed around the SCM calls. > > Derive software secret support is also added by forwarding the > call to the corresponding scm api. > > Signed-off-by: Gaurav Kashyap <quic_gaurkash@quicinc.com> > Tested-by: Neil Armstrong <neil.armstrong@linaro.org> > --- Reviewed-by: Om Prakash Singh <quic_omprsing@quicinc.com> > drivers/soc/qcom/ice.c | 119 +++++++++++++++++++++++++++++++++++++---- > include/soc/qcom/ice.h | 4 ++ > 2 files changed, 112 insertions(+), 11 deletions(-) > > diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c > index c718e8153b23..c3b852269dca 100644 > --- a/drivers/soc/qcom/ice.c > +++ b/drivers/soc/qcom/ice.c > @@ -27,6 +27,8 @@ > #define QCOM_ICE_REG_BIST_STATUS 0x0070 > #define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000 > #define QCOM_ICE_REG_CONTROL 0x0 > +#define QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 0x4040 > + > /* QCOM ICE HWKM registers */ > #define QCOM_ICE_REG_HWKM_TZ_KM_CTL 0x1000 > #define QCOM_ICE_REG_HWKM_TZ_KM_STATUS 0x1004 > @@ -48,6 +50,8 @@ > #define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2 > #define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4 > > +#define QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET 0x80 > + > #define QCOM_ICE_HWKM_REG_OFFSET 0x8000 > #define HWKM_OFFSET(reg) ((reg) + QCOM_ICE_HWKM_REG_OFFSET) > > @@ -68,6 +72,16 @@ struct qcom_ice { > bool hwkm_init_complete; > }; > > +union crypto_cfg { > + __le32 regval; > + struct { > + u8 dusize; > + u8 capidx; > + u8 reserved; > + u8 cfge; > + }; > +}; > + > static bool qcom_ice_check_supported(struct qcom_ice *ice) > { > u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); > @@ -273,6 +287,51 @@ int qcom_ice_suspend(struct qcom_ice *ice) > } > EXPORT_SYMBOL_GPL(qcom_ice_suspend); > > +/* > + * HW dictates the internal mapping between the ICE and HWKM slots, > + * which are different for different versions, make the translation > + * here. For v1 however, the translation is done in trustzone. > + */ > +static int translate_hwkm_slot(struct qcom_ice *ice, int slot) > +{ > + return (ice->hwkm_version == 1) ? slot : (slot * 2); > +} > + > +static int qcom_ice_program_wrapped_key(struct qcom_ice *ice, > + const struct blk_crypto_key *key, > + u8 data_unit_size, int slot) > +{ > + union crypto_cfg cfg; > + int hwkm_slot; > + int err; > + > + hwkm_slot = translate_hwkm_slot(ice, slot); > + > + memset(&cfg, 0, sizeof(cfg)); > + cfg.dusize = data_unit_size; > + cfg.capidx = QCOM_SCM_ICE_CIPHER_AES_256_XTS; > + cfg.cfge = 0x80; > + > + /* Clear CFGE */ > + qcom_ice_writel(ice, 0x0, QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 + > + QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET * slot); > + > + /* Call trustzone to program the wrapped key using hwkm */ > + err = qcom_scm_ice_set_key(hwkm_slot, key->raw, key->size, > + QCOM_SCM_ICE_CIPHER_AES_256_XTS, data_unit_size); > + if (err) { > + pr_err("%s:SCM call Error: 0x%x slot %d\n", __func__, err, > + slot); > + return err; > + } > + > + /* Enable CFGE after programming key */ > + qcom_ice_writel(ice, cfg.regval, QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 + > + QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET * slot); > + > + return err; > +} > + > int qcom_ice_program_key(struct qcom_ice *ice, > u8 algorithm_id, u8 key_size, > const struct blk_crypto_key *bkey, > @@ -288,24 +347,39 @@ int qcom_ice_program_key(struct qcom_ice *ice, > > /* Only AES-256-XTS has been tested so far. */ > if (algorithm_id != QCOM_ICE_CRYPTO_ALG_AES_XTS || > - key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256) { > + (key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256 && > + key_size != QCOM_ICE_CRYPTO_KEY_SIZE_WRAPPED)) { > dev_err_ratelimited(dev, > "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n", > algorithm_id, key_size); > return -EINVAL; > } > > - memcpy(key.bytes, bkey->raw, AES_256_XTS_KEY_SIZE); > - > - /* The SCM call requires that the key words are encoded in big endian */ > - for (i = 0; i < ARRAY_SIZE(key.words); i++) > - __cpu_to_be32s(&key.words[i]); > + if (bkey->crypto_cfg.key_type == BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) { > + /* It is expected that HWKM init has completed before programming wrapped keys */ > + if (!ice->use_hwkm || !ice->hwkm_init_complete) { > + dev_err_ratelimited(dev, "HWKM not currently used or initialized\n"); > + return -EINVAL; > + } > + err = qcom_ice_program_wrapped_key(ice, bkey, data_unit_size, > + slot); > + } else { > + if (bkey->size != QCOM_ICE_CRYPTO_KEY_SIZE_256) > + dev_err_ratelimited(dev, > + "Incorrect key size; bkey->size=%d\n", > + algorithm_id); > + return -EINVAL; > + memcpy(key.bytes, bkey->raw, AES_256_XTS_KEY_SIZE); > > - err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, > - QCOM_SCM_ICE_CIPHER_AES_256_XTS, > - data_unit_size); > + /* The SCM call requires that the key words are encoded in big endian */ > + for (i = 0; i < ARRAY_SIZE(key.words); i++) > + __cpu_to_be32s(&key.words[i]); > > - memzero_explicit(&key, sizeof(key)); > + err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, > + QCOM_SCM_ICE_CIPHER_AES_256_XTS, > + data_unit_size); > + memzero_explicit(&key, sizeof(key)); > + } > > return err; > } > @@ -313,7 +387,21 @@ EXPORT_SYMBOL_GPL(qcom_ice_program_key); > > int qcom_ice_evict_key(struct qcom_ice *ice, int slot) > { > - return qcom_scm_ice_invalidate_key(slot); > + int hwkm_slot = slot; > + > + if (ice->use_hwkm) { > + hwkm_slot = translate_hwkm_slot(ice, slot); > + /* > + * Ignore calls to evict key when HWKM is supported and hwkm init > + * is not yet done. This is to avoid the clearing all slots call > + * during a storage reset when ICE is still in legacy mode. HWKM slave > + * in ICE takes care of zeroing out the keytable on reset. > + */ > + if (!ice->hwkm_init_complete) > + return 0; > + } > + > + return qcom_scm_ice_invalidate_key(hwkm_slot); > } > EXPORT_SYMBOL_GPL(qcom_ice_evict_key); > > @@ -323,6 +411,15 @@ bool qcom_ice_hwkm_supported(struct qcom_ice *ice) > } > EXPORT_SYMBOL_GPL(qcom_ice_hwkm_supported); > > +int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 wkey[], > + unsigned int wkey_size, > + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) > +{ > + return qcom_scm_derive_sw_secret(wkey, wkey_size, > + sw_secret, BLK_CRYPTO_SW_SECRET_SIZE); > +} > +EXPORT_SYMBOL_GPL(qcom_ice_derive_sw_secret); > + > static struct qcom_ice *qcom_ice_create(struct device *dev, > void __iomem *base) > { > diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h > index 1f52e82e3e1c..dabe0d3a1fd0 100644 > --- a/include/soc/qcom/ice.h > +++ b/include/soc/qcom/ice.h > @@ -17,6 +17,7 @@ enum qcom_ice_crypto_key_size { > QCOM_ICE_CRYPTO_KEY_SIZE_192 = 0x2, > QCOM_ICE_CRYPTO_KEY_SIZE_256 = 0x3, > QCOM_ICE_CRYPTO_KEY_SIZE_512 = 0x4, > + QCOM_ICE_CRYPTO_KEY_SIZE_WRAPPED = 0x5, > }; > > enum qcom_ice_crypto_alg { > @@ -35,5 +36,8 @@ int qcom_ice_program_key(struct qcom_ice *ice, > u8 data_unit_size, int slot); > int qcom_ice_evict_key(struct qcom_ice *ice, int slot); > bool qcom_ice_hwkm_supported(struct qcom_ice *ice); > +int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 wkey[], > + unsigned int wkey_size, > + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); > struct qcom_ice *of_qcom_ice_get(struct device *dev); > #endif /* __QCOM_ICE_H__ */
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index c718e8153b23..c3b852269dca 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -27,6 +27,8 @@ #define QCOM_ICE_REG_BIST_STATUS 0x0070 #define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000 #define QCOM_ICE_REG_CONTROL 0x0 +#define QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 0x4040 + /* QCOM ICE HWKM registers */ #define QCOM_ICE_REG_HWKM_TZ_KM_CTL 0x1000 #define QCOM_ICE_REG_HWKM_TZ_KM_STATUS 0x1004 @@ -48,6 +50,8 @@ #define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2 #define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4 +#define QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET 0x80 + #define QCOM_ICE_HWKM_REG_OFFSET 0x8000 #define HWKM_OFFSET(reg) ((reg) + QCOM_ICE_HWKM_REG_OFFSET) @@ -68,6 +72,16 @@ struct qcom_ice { bool hwkm_init_complete; }; +union crypto_cfg { + __le32 regval; + struct { + u8 dusize; + u8 capidx; + u8 reserved; + u8 cfge; + }; +}; + static bool qcom_ice_check_supported(struct qcom_ice *ice) { u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); @@ -273,6 +287,51 @@ int qcom_ice_suspend(struct qcom_ice *ice) } EXPORT_SYMBOL_GPL(qcom_ice_suspend); +/* + * HW dictates the internal mapping between the ICE and HWKM slots, + * which are different for different versions, make the translation + * here. For v1 however, the translation is done in trustzone. + */ +static int translate_hwkm_slot(struct qcom_ice *ice, int slot) +{ + return (ice->hwkm_version == 1) ? slot : (slot * 2); +} + +static int qcom_ice_program_wrapped_key(struct qcom_ice *ice, + const struct blk_crypto_key *key, + u8 data_unit_size, int slot) +{ + union crypto_cfg cfg; + int hwkm_slot; + int err; + + hwkm_slot = translate_hwkm_slot(ice, slot); + + memset(&cfg, 0, sizeof(cfg)); + cfg.dusize = data_unit_size; + cfg.capidx = QCOM_SCM_ICE_CIPHER_AES_256_XTS; + cfg.cfge = 0x80; + + /* Clear CFGE */ + qcom_ice_writel(ice, 0x0, QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 + + QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET * slot); + + /* Call trustzone to program the wrapped key using hwkm */ + err = qcom_scm_ice_set_key(hwkm_slot, key->raw, key->size, + QCOM_SCM_ICE_CIPHER_AES_256_XTS, data_unit_size); + if (err) { + pr_err("%s:SCM call Error: 0x%x slot %d\n", __func__, err, + slot); + return err; + } + + /* Enable CFGE after programming key */ + qcom_ice_writel(ice, cfg.regval, QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 + + QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET * slot); + + return err; +} + int qcom_ice_program_key(struct qcom_ice *ice, u8 algorithm_id, u8 key_size, const struct blk_crypto_key *bkey, @@ -288,24 +347,39 @@ int qcom_ice_program_key(struct qcom_ice *ice, /* Only AES-256-XTS has been tested so far. */ if (algorithm_id != QCOM_ICE_CRYPTO_ALG_AES_XTS || - key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256) { + (key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256 && + key_size != QCOM_ICE_CRYPTO_KEY_SIZE_WRAPPED)) { dev_err_ratelimited(dev, "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n", algorithm_id, key_size); return -EINVAL; } - memcpy(key.bytes, bkey->raw, AES_256_XTS_KEY_SIZE); - - /* The SCM call requires that the key words are encoded in big endian */ - for (i = 0; i < ARRAY_SIZE(key.words); i++) - __cpu_to_be32s(&key.words[i]); + if (bkey->crypto_cfg.key_type == BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) { + /* It is expected that HWKM init has completed before programming wrapped keys */ + if (!ice->use_hwkm || !ice->hwkm_init_complete) { + dev_err_ratelimited(dev, "HWKM not currently used or initialized\n"); + return -EINVAL; + } + err = qcom_ice_program_wrapped_key(ice, bkey, data_unit_size, + slot); + } else { + if (bkey->size != QCOM_ICE_CRYPTO_KEY_SIZE_256) + dev_err_ratelimited(dev, + "Incorrect key size; bkey->size=%d\n", + algorithm_id); + return -EINVAL; + memcpy(key.bytes, bkey->raw, AES_256_XTS_KEY_SIZE); - err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, - QCOM_SCM_ICE_CIPHER_AES_256_XTS, - data_unit_size); + /* The SCM call requires that the key words are encoded in big endian */ + for (i = 0; i < ARRAY_SIZE(key.words); i++) + __cpu_to_be32s(&key.words[i]); - memzero_explicit(&key, sizeof(key)); + err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, + QCOM_SCM_ICE_CIPHER_AES_256_XTS, + data_unit_size); + memzero_explicit(&key, sizeof(key)); + } return err; } @@ -313,7 +387,21 @@ EXPORT_SYMBOL_GPL(qcom_ice_program_key); int qcom_ice_evict_key(struct qcom_ice *ice, int slot) { - return qcom_scm_ice_invalidate_key(slot); + int hwkm_slot = slot; + + if (ice->use_hwkm) { + hwkm_slot = translate_hwkm_slot(ice, slot); + /* + * Ignore calls to evict key when HWKM is supported and hwkm init + * is not yet done. This is to avoid the clearing all slots call + * during a storage reset when ICE is still in legacy mode. HWKM slave + * in ICE takes care of zeroing out the keytable on reset. + */ + if (!ice->hwkm_init_complete) + return 0; + } + + return qcom_scm_ice_invalidate_key(hwkm_slot); } EXPORT_SYMBOL_GPL(qcom_ice_evict_key); @@ -323,6 +411,15 @@ bool qcom_ice_hwkm_supported(struct qcom_ice *ice) } EXPORT_SYMBOL_GPL(qcom_ice_hwkm_supported); +int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 wkey[], + unsigned int wkey_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + return qcom_scm_derive_sw_secret(wkey, wkey_size, + sw_secret, BLK_CRYPTO_SW_SECRET_SIZE); +} +EXPORT_SYMBOL_GPL(qcom_ice_derive_sw_secret); + static struct qcom_ice *qcom_ice_create(struct device *dev, void __iomem *base) { diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h index 1f52e82e3e1c..dabe0d3a1fd0 100644 --- a/include/soc/qcom/ice.h +++ b/include/soc/qcom/ice.h @@ -17,6 +17,7 @@ enum qcom_ice_crypto_key_size { QCOM_ICE_CRYPTO_KEY_SIZE_192 = 0x2, QCOM_ICE_CRYPTO_KEY_SIZE_256 = 0x3, QCOM_ICE_CRYPTO_KEY_SIZE_512 = 0x4, + QCOM_ICE_CRYPTO_KEY_SIZE_WRAPPED = 0x5, }; enum qcom_ice_crypto_alg { @@ -35,5 +36,8 @@ int qcom_ice_program_key(struct qcom_ice *ice, u8 data_unit_size, int slot); int qcom_ice_evict_key(struct qcom_ice *ice, int slot); bool qcom_ice_hwkm_supported(struct qcom_ice *ice); +int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 wkey[], + unsigned int wkey_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); struct qcom_ice *of_qcom_ice_get(struct device *dev); #endif /* __QCOM_ICE_H__ */