mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 03:59:40 +02:00
Merge "ufs: host: data encryption support snapshot"
This commit is contained in:
commit
d8d5f72e35
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -2079,7 +2082,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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
260
drivers/ufs/host/ufshcd-crypto-qti.c
Normal file
260
drivers/ufs/host/ufshcd-crypto-qti.c
Normal 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");
|
||||
Loading…
Reference in New Issue
Block a user