ufs: host: data encryption support snapshot

Add snapshot from msm-5.15 to support in UFS to
exercise data encryption flow using wrapped keys.
commit b9c7626e6 ("Enable qrtr-mhi transport for VM").

Change-Id: I2d24d300ea90209c4833bc834dfb6a505ec6f063
Signed-off-by: Gaurav Kashyap <quic_gaurkash@quicinc.com>
This commit is contained in:
Gaurav Kashyap 2022-10-28 10:32:42 -07:00
parent 043bb02f96
commit 824f39cd19
6 changed files with 325 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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 <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/qcom_scm.h>
#include <linux/qtee_shmbridge.h>
#include <ufs/ufshcd-crypto.h>
#include <linux/crypto-qti-common.h>
#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;
}

View File

@ -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
}

View File

@ -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 */

View File

@ -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 <crypto/algapi.h>
#include <linux/platform_device.h>
#include <linux/crypto-qti-common.h>
#include <ufs/ufshcd-crypto-qti.h>
#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");