diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig index 208abacef696..f375a91f9585 100644 --- a/drivers/ufs/host/Kconfig +++ b/drivers/ufs/host/Kconfig @@ -79,6 +79,15 @@ config SCSI_UFS_QCOM Select this if you have UFS controller on QCOM chipset. If unsure, say N. +config SCSI_UFS_CRYPTO_QTI + tristate "Vendor specific UFS crypto engine support" + depends on SCSI_UFS_CRYPTO + help + Enable Vendor Crypto Engine Support in UFS + Enabling this allows kernel to use UFS crypto operations defined + and implemented by QTI. This is mainly required for implementing + storage encryption using wrapped keys. + config SCSI_UFS_MEDIATEK tristate "Mediatek specific hooks to UFS controller platform driver" depends on SCSI_UFSHCD_PLATFORM && ARCH_MEDIATEK diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile index 7717ca93e7d5..7ecceabb1050 100644 --- a/drivers/ufs/host/Makefile +++ b/drivers/ufs/host/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o obj-$(CONFIG_SCSI_UFS_MEDIATEK) += ufs-mediatek.o obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o obj-$(CONFIG_SCSI_UFS_TI_J721E) += ti-j721e-ufs.o +obj-$(CONFIG_SCSI_UFS_CRYPTO_QTI) += ufshcd-crypto-qti.o diff --git a/drivers/ufs/host/ufs-qcom-ice.c b/drivers/ufs/host/ufs-qcom-ice.c index 664041a0ab17..cfc3dd6187d1 100644 --- a/drivers/ufs/host/ufs-qcom-ice.c +++ b/drivers/ufs/host/ufs-qcom-ice.c @@ -3,13 +3,17 @@ * Qualcomm ICE (Inline Crypto Engine) support. * * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright 2019 Google LLC */ #include #include #include +#include +#include +#include #include "ufs-qcom.h" #define AES_256_XTS_KEY_SIZE 64 @@ -72,7 +76,7 @@ static bool qcom_ice_supported(struct ufs_qcom_host *host) int step = regval & 0xFFFF; /* For now this driver only supports ICE version 3. */ - if (major != 3) { + if (major < 3) { dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n", major, minor, step); return false; @@ -97,15 +101,18 @@ int ufs_qcom_ice_init(struct ufs_qcom_host *host) struct ufs_hba *hba = host->hba; struct device *dev = hba->dev; struct platform_device *pdev = to_platform_device(dev); - struct resource *res; + struct resource *ice_base_res; +#if IS_ENABLED(CONFIG_QTI_HW_KEY_MANAGER) + struct resource *ice_hwkm_res; +#endif int err; if (!(ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES) & MASK_CRYPTO_SUPPORT)) return 0; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ice"); - if (!res) { + ice_base_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ufs_ice"); + if (!ice_base_res) { dev_warn(dev, "ICE registers not found\n"); goto disable; } @@ -115,13 +122,27 @@ int ufs_qcom_ice_init(struct ufs_qcom_host *host) goto disable; } - host->ice_mmio = devm_ioremap_resource(dev, res); + host->ice_mmio = devm_ioremap_resource(dev, ice_base_res); if (IS_ERR(host->ice_mmio)) { err = PTR_ERR(host->ice_mmio); dev_err(dev, "Failed to map ICE registers; err=%d\n", err); return err; } +#if IS_ENABLED(CONFIG_QTI_HW_KEY_MANAGER) + ice_hwkm_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ufs_ice_hwkm"); + if (!ice_hwkm_res) { + dev_warn(dev, "ICE HWKM registers not found\n"); + goto disable; + } + host->ice_hwkm_mmio = devm_ioremap_resource(dev, ice_hwkm_res); + if (IS_ERR(host->ice_hwkm_mmio)) { + err = PTR_ERR(host->ice_hwkm_mmio); + dev_err(dev, "Failed to map ICE HWKM registers; err=%d\n", err); + return err; + } +#endif + if (!qcom_ice_supported(host)) goto disable; @@ -142,7 +163,7 @@ static void qcom_ice_low_power_mode_enable(struct ufs_qcom_host *host) * Enable low power mode sequence * [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0 */ - regval |= 0x7000; + regval |= 0xF000; qcom_ice_writel(host, regval, QCOM_ICE_REG_ADVANCED_CONTROL); } @@ -152,7 +173,7 @@ static void qcom_ice_optimization_enable(struct ufs_qcom_host *host) /* ICE Optimizations Enable Sequence */ regval = qcom_ice_readl(host, QCOM_ICE_REG_ADVANCED_CONTROL); - regval |= 0xD807100; + regval |= 0xD80F100; /* ICE HPG requires delay before writing */ udelay(5); qcom_ice_writel(host, regval, QCOM_ICE_REG_ADVANCED_CONTROL); @@ -170,6 +191,10 @@ int ufs_qcom_ice_enable(struct ufs_qcom_host *host) void ufs_qcom_ice_disable(struct ufs_qcom_host *host) { + if (!(host->hba->caps & UFSHCD_CAP_CRYPTO)) + return; + if (host->hba->quirks & UFSHCD_QUIRK_CUSTOM_CRYPTO_PROFILE) + return crypto_qti_disable(); } /* Poll until all BIST bits are reset */ @@ -218,6 +243,11 @@ int ufs_qcom_ice_program_key(struct ufs_hba *hba, } key; int i; int err; + struct qtee_shm shm; + + err = qtee_shmbridge_allocate_shm(AES_256_XTS_KEY_SIZE, &shm); + if (err) + return -ENOMEM; if (!(cfg->config_enable & UFS_CRYPTO_CONFIGURATION_ENABLE)) return qcom_scm_ice_invalidate_key(slot); @@ -241,9 +271,20 @@ int ufs_qcom_ice_program_key(struct ufs_hba *hba, for (i = 0; i < ARRAY_SIZE(key.words); i++) __cpu_to_be32s(&key.words[i]); - err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, + memcpy(shm.vaddr, key.bytes, AES_256_XTS_KEY_SIZE); + qtee_shmbridge_flush_shm_buf(&shm); + + err = qcom_scm_config_set_ice_key(slot, shm.paddr, + AES_256_XTS_KEY_SIZE, QCOM_SCM_ICE_CIPHER_AES_256_XTS, - cfg->data_unit_size); + cfg->data_unit_size, UFS_CE); + if (err) + pr_err("%s:SCM call Error: 0x%x slot %d\n", + __func__, err, slot); + + qtee_shmbridge_inv_shm_buf(&shm); + qtee_shmbridge_free_shm(&shm); memzero_explicit(&key, sizeof(key)); + return err; } diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 6e4288d38321..c9c139fd19f5 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -498,6 +498,8 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba) struct ufs_qcom_host *host = ufshcd_get_variant(hba); bool reenable_intr = false; + host->reset_in_progress = true; + if (!host->core_reset) { dev_warn(hba->dev, "%s: reset control not set\n", __func__); goto out; @@ -542,6 +544,7 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba) out: host->vdd_hba_pc = false; + host->reset_in_progress = false; return ret; } @@ -2066,7 +2069,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; #if IS_ENABLED(CONFIG_SCSI_UFS_CRYPTO_QTI) - hba->quirks |= UFSHCD_QUIRK_CUSTOM_KEYSLOT_MANAGER; + hba->quirks |= UFSHCD_QUIRK_CUSTOM_CRYPTO_PROFILE; #endif } diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index 779802c328b6..7c91d61b1aff 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -478,6 +478,7 @@ struct ufs_qcom_host { void __iomem *ice_hwkm_mmio; #endif + bool reset_in_progress; u32 dev_ref_clk_en_mask; /* Bitmask for enabling debug prints */ diff --git a/drivers/ufs/host/ufshcd-crypto-qti.c b/drivers/ufs/host/ufshcd-crypto-qti.c new file mode 100644 index 000000000000..12ca3bf06028 --- /dev/null +++ b/drivers/ufs/host/ufshcd-crypto-qti.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS Crypto ops QTI implementation. + * + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include + +#include +#include "ufs-qcom.h" + +#define MINIMUM_DUN_SIZE 512 +#define MAXIMUM_DUN_SIZE 65536 + +/* Blk-crypto modes supported by UFS crypto */ +static const struct ufs_crypto_alg_entry { + enum ufs_crypto_alg ufs_alg; + enum ufs_crypto_key_size ufs_key_size; +} ufs_crypto_algs[BLK_ENCRYPTION_MODE_MAX] = { + [BLK_ENCRYPTION_MODE_AES_256_XTS] = { + .ufs_alg = UFS_CRYPTO_ALG_AES_XTS, + .ufs_key_size = UFS_CRYPTO_KEY_SIZE_256, + }, +}; + +static void get_mmio_data(struct ice_mmio_data *data, + struct ufs_qcom_host *host) +{ + data->ice_base_mmio = host->ice_mmio; +#if IS_ENABLED(CONFIG_QTI_HW_KEY_MANAGER) + data->ice_hwkm_mmio = host->ice_hwkm_mmio; +#endif +} + +static int ufshcd_crypto_qti_keyslot_program( + struct blk_crypto_profile *profile, + const struct blk_crypto_key *key, + unsigned int slot) +{ + struct ufs_hba *hba = + container_of(profile, struct ufs_hba, crypto_profile); + int err = 0; + u8 data_unit_mask = -1; + int cap_idx = -1; + const union ufs_crypto_cap_entry *ccap_array = hba->crypto_cap_array; + const struct ufs_crypto_alg_entry *alg; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + int i = 0; + struct ice_mmio_data mmio_data; + + if (!key) { + pr_err("Invalid/no key present\n"); + return -EINVAL; + } + + data_unit_mask = key->crypto_cfg.data_unit_size / MINIMUM_DUN_SIZE; + alg = &ufs_crypto_algs[key->crypto_cfg.crypto_mode]; + BUILD_BUG_ON(UFS_CRYPTO_KEY_SIZE_INVALID != 0); + for (i = 0; i < hba->crypto_capabilities.num_crypto_cap; i++) { + if (ccap_array[i].algorithm_id == alg->ufs_alg && + ccap_array[i].key_size == alg->ufs_key_size && + (ccap_array[i].sdus_mask & data_unit_mask)) { + cap_idx = i; + break; + } + } + + if (WARN_ON(cap_idx < 0)) + return -EOPNOTSUPP; + + if (host->reset_in_progress) { + pr_err("UFS host reset in progress, state = 0x%x\n", + hba->ufshcd_state); + return -EINVAL; + } + + err = ufshcd_hold(hba, false); + if (err) { + pr_err("%s: failed to enable clocks, err %d\n", __func__, err); + goto out; + } + + get_mmio_data(&mmio_data, host); + err = crypto_qti_keyslot_program(&mmio_data, key, slot, + data_unit_mask, cap_idx); + if (err) + pr_err("%s: failed with error %d\n", __func__, err); + + ufshcd_release(hba); +out: + + return err; +} + +static int ufshcd_crypto_qti_keyslot_evict( + struct blk_crypto_profile *profile, + const struct blk_crypto_key *key, + unsigned int slot) +{ + int err = 0; + struct ufs_hba *hba = + container_of(profile, struct ufs_hba, crypto_profile); + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + struct ice_mmio_data mmio_data; + + if (host->reset_in_progress) { + pr_err("UFS host reset in progress, state = 0x%x\n", + hba->ufshcd_state); + return -EINVAL; + } + + err = ufshcd_hold(hba, false); + if (err) { + pr_err("%s: failed to enable clocks, err %d\n", __func__, err); + return err; + } + + get_mmio_data(&mmio_data, host); + err = crypto_qti_keyslot_evict(&mmio_data, slot); + if (err) + pr_err("%s: failed with error %d\n", __func__, err); + + ufshcd_release(hba); + return err; +} + +static int ufshcd_crypto_qti_derive_raw_secret( + struct blk_crypto_profile *profile, + const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + int err = 0; + struct ufs_hba *hba = + container_of(profile, struct ufs_hba, crypto_profile); + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + + if (host->reset_in_progress) { + pr_err("UFS host reset in progress, state = 0x%x\n", + hba->ufshcd_state); + return -EINVAL; + } + + err = ufshcd_hold(hba, false); + if (err) { + pr_err("%s: failed to enable clocks, err %d\n", __func__, err); + return err; + } + + err = crypto_qti_derive_raw_secret(wrapped_key, wrapped_key_size, + sw_secret, BLK_CRYPTO_SW_SECRET_SIZE); + if (err) + pr_err("%s: failed with error %d\n", __func__, err); + + ufshcd_release(hba); + return err; +} + +static const struct blk_crypto_ll_ops ufshcd_qti_crypto_ops = { + .keyslot_program = ufshcd_crypto_qti_keyslot_program, + .keyslot_evict = ufshcd_crypto_qti_keyslot_evict, + .derive_sw_secret = ufshcd_crypto_qti_derive_raw_secret, +}; + +static enum blk_crypto_mode_num +ufshcd_find_blk_crypto_mode(union ufs_crypto_cap_entry cap) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ufs_crypto_algs); i++) { + BUILD_BUG_ON(UFS_CRYPTO_KEY_SIZE_INVALID != 0); + if (ufs_crypto_algs[i].ufs_alg == cap.algorithm_id && + ufs_crypto_algs[i].ufs_key_size == cap.key_size) { + return i; + } + } + return BLK_ENCRYPTION_MODE_INVALID; +} + +/** + * ufshcd_hba_init_crypto_capabilities - Read crypto capabilities, init crypto + * fields in hba + * @hba: Per adapter instance + * + * Return: 0 if crypto was initialized or is not supported, else a -errno value. + */ +int ufshcd_qti_hba_init_crypto_capabilities(struct ufs_hba *hba) +{ + int cap_idx; + int err = 0; + enum blk_crypto_mode_num blk_mode_num; + + /* + * Don't use crypto if either the hardware doesn't advertise the + * standard crypto capability bit *or* if the vendor specific driver + * hasn't advertised that crypto is supported. + */ + + if (!(ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES) & + MASK_CRYPTO_SUPPORT)) + goto out; + if (!(hba->caps & UFSHCD_CAP_CRYPTO)) + goto out; + + hba->crypto_capabilities.reg_val = + cpu_to_le32(ufshcd_readl(hba, REG_UFS_CCAP)); + hba->crypto_cfg_register = + (u32)hba->crypto_capabilities.config_array_ptr * 0x100; + hba->crypto_cap_array = + devm_kcalloc(hba->dev, hba->crypto_capabilities.num_crypto_cap, + sizeof(hba->crypto_cap_array[0]), GFP_KERNEL); + if (!hba->crypto_cap_array) { + err = -ENOMEM; + goto out; + } + + /* The actual number of configurations supported is (CFGC+1) */ + err = devm_blk_crypto_profile_init(hba->dev, &hba->crypto_profile, + hba->crypto_capabilities.config_count + 1); + if (err) + goto out; + + hba->crypto_profile.ll_ops = ufshcd_qti_crypto_ops; + /* UFS only supports 8 bytes for any DUN */ + hba->crypto_profile.max_dun_bytes_supported = 8; + hba->crypto_profile.key_types_supported = + BLK_CRYPTO_KEY_TYPE_HW_WRAPPED; + hba->crypto_profile.dev = hba->dev; + + /* + * Cache all the UFS crypto capabilities and advertise the supported + * crypto modes and data unit sizes to the block layer. + */ + for (cap_idx = 0; cap_idx < hba->crypto_capabilities.num_crypto_cap; + cap_idx++) { + hba->crypto_cap_array[cap_idx].reg_val = + cpu_to_le32(ufshcd_readl(hba, + REG_UFS_CRYPTOCAP + + cap_idx * sizeof(__le32))); + blk_mode_num = ufshcd_find_blk_crypto_mode( + hba->crypto_cap_array[cap_idx]); + if (blk_mode_num != BLK_ENCRYPTION_MODE_INVALID) + hba->crypto_profile.modes_supported[blk_mode_num] |= + hba->crypto_cap_array[cap_idx].sdus_mask * 512; + } + + return 0; + +out: + /* Indicate that init failed by clearing UFSHCD_CAP_CRYPTO */ + hba->caps &= ~UFSHCD_CAP_CRYPTO; + return err; +} +EXPORT_SYMBOL(ufshcd_qti_hba_init_crypto_capabilities); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UFS Crypto ops QTI implementation");