1640 lines
43 KiB
C
1640 lines
43 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Common crypto library for storage encryption.
|
|
*
|
|
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/crypto-qti-common.h>
|
|
#include <linux/module.h>
|
|
#include "crypto-qti-ice-regs.h"
|
|
#include "crypto-qti-platform.h"
|
|
|
|
#include <linux/of.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/genhd.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/stringify.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/qseecom_kernel.h>
|
|
|
|
#if IS_ENABLED(CONFIG_QTI_CRYPTO_FDE)
|
|
#define CRYPTO_ICE_TYPE_NAME_LEN 8
|
|
#define CRYPTO_ICE_ENCRYPT 0x1
|
|
#define CRYPTO_ICE_DECRYPT 0x2
|
|
#define CRYPTO_SECT_LEN_IN_BYTE 512
|
|
#define TOTAL_NUMBER_ICE_SLOTS 32
|
|
#define CRYPTO_ICE_UDEV_PARTITION_NAME 96
|
|
|
|
#define CRYPTO_ICE_FDE_KEY_INDEX 31
|
|
#define CRYPTO_UD_VOLNAME "userdata"
|
|
#define CRYPTO_ICE_HASH_SIZE 32
|
|
#define CRYPTO_ICE_KEY_ID_SIZE 32
|
|
|
|
#define CRYPTO_ICE_FDE_UFS_KEYID "UFS ICE Full Disk Encryption "
|
|
#define CRYPTO_ICE_FDE_EMMC_KEYID "SDCC ICE Full Disk Encryption "
|
|
|
|
/* add the legacy key_id to support ICE OTA */
|
|
#define CRYPTO_ICE_FDE_UFS_LEGACY_KEYID "UFS ICE Full Disk Encryption"
|
|
#define CRYPTO_ICE_FDE_EMMC_LEGACY_KEYID "SDCC ICE Full Disk Encryption"
|
|
|
|
#define CRYPTO_ICE_UFS_DEV_NAME "ufshcd-qcom"
|
|
#define CRYPTO_ICE_EMMC_DEV_NAME "sdhci_msm"
|
|
#define QSEECOM_KEY_ID_EXISTS -65
|
|
|
|
#define _INIT_ATTRIBUTE(_name, _mode) \
|
|
{ \
|
|
.name = __stringify(_name), \
|
|
.mode = (_mode), \
|
|
}
|
|
|
|
#define PART_CFG_ATTR_RW(_name) \
|
|
struct kobj_attribute part_cfg_attr_##_name = { \
|
|
.attr = _INIT_ATTRIBUTE(_name, 0660), \
|
|
.show = crypto_qti_ice_part_cfg_show, \
|
|
.store = crypto_qti_ice_part_cfg_store, \
|
|
}
|
|
|
|
/* Set the "add_partition" node attributes, allow user to add partitions */
|
|
#define __ATTR_WR_PARTITIONS(_name) { \
|
|
.attr = { .name = __stringify(_name), .mode = 0220 }, \
|
|
.store = _name##_store, \
|
|
}
|
|
|
|
/*
|
|
* Encapsulates configuration information for each supported partition
|
|
*/
|
|
struct ice_part_cfg {
|
|
struct list_head list;
|
|
char volname[PARTITION_META_INFO_VOLNAMELTH + 1]; /* NULL terminated */
|
|
uint32_t key_slot; /*ICE keyslot 0..31 */
|
|
uint32_t add_partition_result; /*Add partition operation res*/
|
|
bool encr_bypass;
|
|
bool decr_bypass;
|
|
struct kobject kobj;
|
|
struct kobj_type kobj_type;
|
|
};
|
|
|
|
struct ice_clk_info {
|
|
struct list_head list;
|
|
struct clk *clk;
|
|
const char *name;
|
|
u32 max_freq;
|
|
u32 min_freq;
|
|
u32 curr_freq;
|
|
bool enabled;
|
|
};
|
|
|
|
static LIST_HEAD(ice_devices);
|
|
|
|
/*
|
|
* ICE HW device structure.
|
|
*/
|
|
struct ice_device {
|
|
struct list_head list;
|
|
struct kset *fde_partitions;
|
|
struct mutex mutex;
|
|
struct device *pdev;
|
|
dev_t device_no;
|
|
void __iomem *mmio;
|
|
int irq;
|
|
bool is_ice_enabled;
|
|
ice_error_cb error_cb;
|
|
void *host_controller_data; /* UFS/EMMC/other? */
|
|
struct list_head clk_list_head;
|
|
u32 ice_hw_version;
|
|
bool is_ice_clk_available;
|
|
char ice_instance_type[CRYPTO_ICE_TYPE_NAME_LEN];
|
|
struct regulator *reg;
|
|
bool is_regulator_available;
|
|
|
|
bool sysfs_groups_created;
|
|
|
|
/* Partition list for full disk encryption */
|
|
struct list_head part_cfg_list;
|
|
struct kobj_type partitions_kobj_type;
|
|
u32 num_fde_slots;
|
|
u32 num_fde_slots_in_use;
|
|
};
|
|
|
|
static int crypto_qti_ice_init(struct ice_device *ice_dev,
|
|
void *host_controller_data,
|
|
ice_error_cb error_cb);
|
|
#endif /* CONFIG_QTI_CRYPTO_FDE */
|
|
|
|
static int ice_check_fuse_setting(void __iomem *ice_mmio)
|
|
{
|
|
uint32_t regval;
|
|
uint32_t version, major, minor;
|
|
|
|
version = ice_readl(ice_mmio, ICE_REGS_VERSION);
|
|
major = (version & ICE_CORE_MAJOR_REV_MASK) >>
|
|
ICE_CORE_MAJOR_REV;
|
|
minor = (version & ICE_CORE_MINOR_REV_MASK) >>
|
|
ICE_CORE_MINOR_REV;
|
|
|
|
/* Check fuse setting is not supported on ICE 3.2 onwards */
|
|
if ((major == 0x03) && (minor >= 0x02))
|
|
return 0;
|
|
regval = ice_readl(ice_mmio, ICE_REGS_FUSE_SETTING);
|
|
regval &= (ICE_FUSE_SETTING_MASK |
|
|
ICE_FORCE_HW_KEY0_SETTING_MASK |
|
|
ICE_FORCE_HW_KEY1_SETTING_MASK);
|
|
|
|
if (regval) {
|
|
pr_err("%s: error: ICE_ERROR_HW_DISABLE_FUSE_BLOWN\n", __func__);
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ice_check_version(void __iomem *ice_mmio)
|
|
{
|
|
uint32_t version, major, minor, step;
|
|
|
|
version = ice_readl(ice_mmio, ICE_REGS_VERSION);
|
|
major = (version & ICE_CORE_MAJOR_REV_MASK) >> ICE_CORE_MAJOR_REV;
|
|
minor = (version & ICE_CORE_MINOR_REV_MASK) >> ICE_CORE_MINOR_REV;
|
|
step = (version & ICE_CORE_STEP_REV_MASK) >> ICE_CORE_STEP_REV;
|
|
|
|
if (major < ICE_CORE_CURRENT_MAJOR_VERSION) {
|
|
pr_err("%s: Unknown ICE device at %lu, rev %d.%d.%d\n",
|
|
__func__, (unsigned long)ice_mmio,
|
|
major, minor, step);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int crypto_qti_init_crypto(void *mmio_data)
|
|
{
|
|
int err = 0;
|
|
void __iomem *ice_mmio = (void __iomem *) mmio_data;
|
|
|
|
err = ice_check_version(ice_mmio);
|
|
if (err) {
|
|
pr_err("%s: check version failed, err %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
err = ice_check_fuse_setting(ice_mmio);
|
|
if (err)
|
|
pr_err("%s: check fuse failed, err %d\n", __func__, err);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_init_crypto);
|
|
|
|
static void ice_low_power_and_optimization_enable(void __iomem *ice_mmio)
|
|
{
|
|
uint32_t regval;
|
|
|
|
regval = ice_readl(ice_mmio, ICE_REGS_ADVANCED_CONTROL);
|
|
/* Enable low power mode sequence
|
|
* [0]-0,[1]-0,[2]-0,[3]-7,[4]-0,[5]-0,[6]-0,[7]-0,
|
|
* Enable CONFIG_CLK_GATING, STREAM2_CLK_GATING and STREAM1_CLK_GATING
|
|
*/
|
|
regval |= 0x7000;
|
|
/* Optimization enable sequence
|
|
*/
|
|
regval |= 0xD807100;
|
|
ice_writel(ice_mmio, regval, ICE_REGS_ADVANCED_CONTROL);
|
|
/*
|
|
* Memory barrier - to ensure write completion before next transaction
|
|
*/
|
|
wmb();
|
|
}
|
|
|
|
static int ice_wait_bist_status(void __iomem *ice_mmio)
|
|
{
|
|
int count;
|
|
uint32_t regval;
|
|
|
|
for (count = 0; count < QTI_ICE_MAX_BIST_CHECK_COUNT; count++) {
|
|
regval = ice_readl(ice_mmio, ICE_REGS_BIST_STATUS);
|
|
if (!(regval & ICE_BIST_STATUS_MASK))
|
|
break;
|
|
udelay(50);
|
|
}
|
|
|
|
if (regval) {
|
|
pr_err("%s: wait bist status failed, reg %d\n", __func__, regval);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int crypto_qti_enable(void *mmio_data)
|
|
{
|
|
int err = 0;
|
|
void __iomem *ice_mmio = (void __iomem *) mmio_data;
|
|
|
|
ice_low_power_and_optimization_enable(ice_mmio);
|
|
err = ice_wait_bist_status(ice_mmio);
|
|
if (err)
|
|
return err;
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_enable);
|
|
|
|
void crypto_qti_disable(void)
|
|
{
|
|
crypto_qti_disable_platform();
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_disable);
|
|
|
|
int crypto_qti_resume(void *mmio_data)
|
|
{
|
|
void __iomem *ice_mmio = (void __iomem *) mmio_data;
|
|
|
|
return ice_wait_bist_status(ice_mmio);
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_resume);
|
|
|
|
static void ice_dump_test_bus(void __iomem *ice_mmio)
|
|
{
|
|
uint32_t regval = 0x1;
|
|
uint32_t val;
|
|
uint8_t bus_selector;
|
|
uint8_t stream_selector;
|
|
|
|
pr_err("ICE TEST BUS DUMP:\n");
|
|
|
|
for (bus_selector = 0; bus_selector <= 0xF; bus_selector++) {
|
|
regval = 0x1; /* enable test bus */
|
|
regval |= bus_selector << 28;
|
|
if (bus_selector == 0xD)
|
|
continue;
|
|
ice_writel(ice_mmio, regval, ICE_REGS_TEST_BUS_CONTROL);
|
|
/*
|
|
* make sure test bus selector is written before reading
|
|
* the test bus register
|
|
*/
|
|
wmb();
|
|
val = ice_readl(ice_mmio, ICE_REGS_TEST_BUS_REG);
|
|
pr_err("ICE_TEST_BUS_CONTROL: 0x%08x | ICE_TEST_BUS_REG: 0x%08x\n",
|
|
regval, val);
|
|
}
|
|
|
|
pr_err("ICE TEST BUS DUMP (ICE_STREAM1_DATAPATH_TEST_BUS):\n");
|
|
for (stream_selector = 0; stream_selector <= 0xF; stream_selector++) {
|
|
regval = 0xD0000001; /* enable stream test bus */
|
|
regval |= stream_selector << 16;
|
|
ice_writel(ice_mmio, regval, ICE_REGS_TEST_BUS_CONTROL);
|
|
/*
|
|
* make sure test bus selector is written before reading
|
|
* the test bus register
|
|
*/
|
|
wmb();
|
|
val = ice_readl(ice_mmio, ICE_REGS_TEST_BUS_REG);
|
|
pr_err("ICE_TEST_BUS_CONTROL: 0x%08x | ICE_TEST_BUS_REG: 0x%08x\n",
|
|
regval, val);
|
|
}
|
|
}
|
|
|
|
|
|
int crypto_qti_debug(const struct ice_mmio_data *mmio_data)
|
|
{
|
|
void __iomem *ice_mmio = mmio_data->ice_base_mmio;
|
|
|
|
pr_err("ICE Control: 0x%08x | ICE Reset: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_CONTROL),
|
|
ice_readl(ice_mmio, ICE_REGS_RESET));
|
|
|
|
pr_err("ICE Version: 0x%08x | ICE FUSE: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_VERSION),
|
|
ice_readl(ice_mmio, ICE_REGS_FUSE_SETTING));
|
|
|
|
pr_err("%s: ICE Param1: 0x%08x | ICE Param2: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_PARAMETERS_1),
|
|
ice_readl(ice_mmio, ICE_REGS_PARAMETERS_2));
|
|
|
|
pr_err("%s: ICE Param3: 0x%08x | ICE Param4: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_PARAMETERS_3),
|
|
ice_readl(ice_mmio, ICE_REGS_PARAMETERS_4));
|
|
|
|
pr_err("%s: ICE Param5: 0x%08x | ICE IRQ STTS: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_PARAMETERS_5),
|
|
ice_readl(ice_mmio, ICE_REGS_NON_SEC_IRQ_STTS));
|
|
|
|
pr_err("%s: ICE IRQ MASK: 0x%08x | ICE IRQ CLR: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_NON_SEC_IRQ_MASK),
|
|
ice_readl(ice_mmio, ICE_REGS_NON_SEC_IRQ_CLR));
|
|
|
|
pr_err("%s: ICE INVALID CCFG ERR STTS: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_INVALID_CCFG_ERR_STTS));
|
|
|
|
pr_err("%s: ICE BIST Sts: 0x%08x | ICE Bypass Sts: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_BIST_STATUS),
|
|
ice_readl(ice_mmio, ICE_REGS_BYPASS_STATUS));
|
|
|
|
pr_err("%s: ICE ADV CTRL: 0x%08x | ICE ENDIAN SWAP: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_ADVANCED_CONTROL),
|
|
ice_readl(ice_mmio, ICE_REGS_ENDIAN_SWAP));
|
|
|
|
pr_err("%s: ICE_STM1_ERR_SYND1: 0x%08x | ICE_STM1_ERR_SYND2: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_ERROR_SYNDROME1),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_ERROR_SYNDROME2));
|
|
|
|
pr_err("%s: ICE_STM2_ERR_SYND1: 0x%08x | ICE_STM2_ERR_SYND2: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_ERROR_SYNDROME1),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_ERROR_SYNDROME2));
|
|
|
|
pr_err("%s: ICE_STM1_COUNTER1: 0x%08x | ICE_STM1_COUNTER2: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS1),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS2));
|
|
|
|
pr_err("%s: ICE_STM1_COUNTER3: 0x%08x | ICE_STM1_COUNTER4: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS3),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS4));
|
|
|
|
pr_err("%s: ICE_STM2_COUNTER1: 0x%08x | ICE_STM2_COUNTER2: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS1),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS2));
|
|
|
|
pr_err("%s: ICE_STM2_COUNTER3: 0x%08x | ICE_STM2_COUNTER4: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS3),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS4));
|
|
|
|
pr_err("%s: ICE_STM1_CTR5_MSB: 0x%08x | ICE_STM1_CTR5_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS5_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS5_LSB));
|
|
|
|
pr_err("%s: ICE_STM1_CTR6_MSB: 0x%08x | ICE_STM1_CTR6_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS6_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS6_LSB));
|
|
|
|
pr_err("%s: ICE_STM1_CTR7_MSB: 0x%08x | ICE_STM1_CTR7_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS7_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS7_LSB));
|
|
|
|
pr_err("%s: ICE_STM1_CTR8_MSB: 0x%08x | ICE_STM1_CTR8_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS8_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS8_LSB));
|
|
|
|
pr_err("%s: ICE_STM1_CTR9_MSB: 0x%08x | ICE_STM1_CTR9_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS9_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS9_LSB));
|
|
|
|
pr_err("%s: ICE_STM2_CTR5_MSB: 0x%08x | ICE_STM2_CTR5_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS5_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS5_LSB));
|
|
|
|
pr_err("%s: ICE_STM2_CTR6_MSB: 0x%08x | ICE_STM2_CTR6_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS6_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS6_LSB));
|
|
|
|
pr_err("%s: ICE_STM2_CTR7_MSB: 0x%08x | ICE_STM2_CTR7_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS7_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS7_LSB));
|
|
|
|
pr_err("%s: ICE_STM2_CTR8_MSB: 0x%08x | ICE_STM2_CTR8_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS8_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS8_LSB));
|
|
|
|
pr_err("%s: ICE_STM2_CTR9_MSB: 0x%08x | ICE_STM2_CTR9_LSB: 0x%08x\n",
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS9_MSB),
|
|
ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS9_LSB));
|
|
|
|
ice_dump_test_bus(ice_mmio);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_debug);
|
|
|
|
int crypto_qti_keyslot_program(const struct ice_mmio_data *mmio_data,
|
|
const struct blk_crypto_key *key,
|
|
unsigned int slot,
|
|
u8 data_unit_mask, int capid, int storage_type)
|
|
{
|
|
int err = 0;
|
|
|
|
err = crypto_qti_program_key(mmio_data, key, slot,
|
|
data_unit_mask, capid, storage_type);
|
|
if (err) {
|
|
pr_err("%s: program key failed with error %d\n", __func__, err);
|
|
err = crypto_qti_invalidate_key(mmio_data, slot, storage_type);
|
|
if (err) {
|
|
pr_err("%s: invalidate key failed with error %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_keyslot_program);
|
|
|
|
int crypto_qti_keyslot_evict(const struct ice_mmio_data *mmio_data,
|
|
unsigned int slot, int storage_type)
|
|
{
|
|
int err = 0;
|
|
|
|
err = crypto_qti_invalidate_key(mmio_data, slot, storage_type);
|
|
if (err) {
|
|
pr_err("%s: invalidate key failed with error %d\n",
|
|
__func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_keyslot_evict);
|
|
|
|
int crypto_qti_derive_raw_secret(const u8 *wrapped_key,
|
|
unsigned int wrapped_key_size, u8 *secret,
|
|
unsigned int secret_size)
|
|
{
|
|
int err = 0;
|
|
|
|
if (wrapped_key_size <= RAW_SECRET_SIZE) {
|
|
pr_err("%s: Invalid wrapped_key_size: %u\n",
|
|
__func__, wrapped_key_size);
|
|
err = -EINVAL;
|
|
return err;
|
|
}
|
|
if (secret_size != RAW_SECRET_SIZE) {
|
|
pr_err("%s: Invalid secret size: %u\n", __func__, secret_size);
|
|
err = -EINVAL;
|
|
return err;
|
|
}
|
|
|
|
if (wrapped_key_size > 64)
|
|
err = crypto_qti_derive_raw_secret_platform(wrapped_key,
|
|
wrapped_key_size, secret, secret_size);
|
|
else
|
|
memcpy(secret, wrapped_key, secret_size);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_derive_raw_secret);
|
|
|
|
#if IS_ENABLED(CONFIG_QTI_CRYPTO_FDE)
|
|
static int get_key_id(char *key_id, uint32_t usage, bool hash_enable)
|
|
{
|
|
/* only legacy hash will get the legacy key id, otherwise return new key id */
|
|
if (hash_enable) {
|
|
switch (usage) {
|
|
case QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION:
|
|
memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_UFS_LEGACY_KEYID,
|
|
strlen(CRYPTO_ICE_FDE_UFS_LEGACY_KEYID));
|
|
break;
|
|
case QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION:
|
|
memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_EMMC_LEGACY_KEYID,
|
|
strlen(CRYPTO_ICE_FDE_EMMC_LEGACY_KEYID));
|
|
break;
|
|
default:
|
|
pr_err("unsupported usage %d\n", usage);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
switch (usage) {
|
|
case QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION:
|
|
memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_UFS_KEYID,
|
|
strlen(CRYPTO_ICE_FDE_UFS_KEYID));
|
|
break;
|
|
case QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION:
|
|
memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_EMMC_KEYID,
|
|
strlen(CRYPTO_ICE_FDE_EMMC_KEYID));
|
|
break;
|
|
default:
|
|
pr_err("unsupported usage %d\n", usage);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ice_part_cfg *crypto_qti_ice_get_part_cfg(struct ice_device *ice_dev,
|
|
char const * const volname)
|
|
{
|
|
struct ice_part_cfg *part_cfg = NULL;
|
|
|
|
if (!ice_dev) {
|
|
pr_err_ratelimited("%s: %s: no ICE device\n", __func__, volname);
|
|
return NULL;
|
|
}
|
|
|
|
list_for_each_entry(part_cfg, &ice_dev->part_cfg_list, list) {
|
|
if (!strcmp(part_cfg->volname, volname))
|
|
return part_cfg;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Stub function to satisfy kobject lifecycle
|
|
*/
|
|
static inline void crypto_qti_ice_release_kobj(struct kobject *kobj)
|
|
{
|
|
char const *name = kobject_name(kobj);
|
|
|
|
if (name)
|
|
pr_debug("Releasing %s\n", name);
|
|
|
|
/* Nothing to do */
|
|
}
|
|
|
|
static inline struct ice_part_cfg *to_ice_part_cfg(struct kobject *kobject)
|
|
{
|
|
return container_of(kobject, struct ice_part_cfg, kobj);
|
|
}
|
|
|
|
/*
|
|
* Attribute "show" method for per-partition attributes.
|
|
*/
|
|
static ssize_t crypto_qti_ice_part_cfg_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct ice_part_cfg *cfg = to_ice_part_cfg(kobj);
|
|
|
|
if (!strcmp(attr->attr.name, "decr_bypass"))
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", cfg->decr_bypass);
|
|
if (!strcmp(attr->attr.name, "encr_bypass"))
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", cfg->encr_bypass);
|
|
if (!strcmp(attr->attr.name, "add_partition_result"))
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", cfg->add_partition_result);
|
|
|
|
pr_err("%s: Unhandled attribute %s\n", __func__, buf);
|
|
return -EFAULT;
|
|
}
|
|
|
|
/*
|
|
* Attribute "store" method for per-partition attributes.
|
|
*/
|
|
static ssize_t crypto_qti_ice_part_cfg_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf,
|
|
size_t count)
|
|
{
|
|
struct ice_part_cfg *cfg = to_ice_part_cfg(kobj);
|
|
int op_result = -EFAULT;
|
|
|
|
/*
|
|
* the decr_bypass/encr_bypass attribute can be configured
|
|
* from userspace side, only "0" or "1" are accepted.
|
|
*/
|
|
if (count > 2)
|
|
return -EINVAL;
|
|
|
|
if (!strcmp(attr->attr.name, "decr_bypass"))
|
|
op_result = kstrtobool(buf, &cfg->decr_bypass);
|
|
else if (!strcmp(attr->attr.name, "encr_bypass"))
|
|
op_result = kstrtobool(buf, &cfg->encr_bypass);
|
|
else
|
|
pr_err("%s: Unhandled attribute %s\n", __func__, buf);
|
|
|
|
/* Notify userspace of the change */
|
|
if (!op_result) {
|
|
pr_info("%s: Change %s:%s=%s\n", __func__, cfg->volname,
|
|
attr->attr.name, buf);
|
|
kobject_uevent(kobj, KOBJ_CHANGE);
|
|
return count;
|
|
}
|
|
|
|
pr_err("%s: Failed %s:%s=%s : %d\n", __func__, cfg->volname,
|
|
attr->attr.name, buf, op_result);
|
|
return op_result;
|
|
}
|
|
|
|
/*
|
|
* crypto_qti_ice_part_cfg_show and crypto_qti_ice_part_cfg_store must ahead of referring to them
|
|
*/
|
|
static PART_CFG_ATTR_RW(add_partition_result);
|
|
static PART_CFG_ATTR_RW(decr_bypass);
|
|
static PART_CFG_ATTR_RW(encr_bypass);
|
|
|
|
static struct attribute *ice_part_cfg_attrs[] = {
|
|
&part_cfg_attr_add_partition_result.attr,
|
|
&part_cfg_attr_decr_bypass.attr,
|
|
&part_cfg_attr_encr_bypass.attr,
|
|
NULL,
|
|
};
|
|
|
|
/**
|
|
* Add a new partition for the ICE to manage
|
|
*
|
|
* Full encryption/decryption is enabled by default!
|
|
*
|
|
* @ice_dev: ICE device node
|
|
* @new_volname: Null-terminated volume name partition
|
|
* @slot: the ICE keyslot number
|
|
* @new_key 0 is key exist, 1 is new key
|
|
*/
|
|
static int crypto_qti_ice_add_new_partition(struct ice_device *ice_dev,
|
|
const char * const new_volname,
|
|
unsigned int slot,
|
|
bool new_key)
|
|
{
|
|
struct list_head *new_pos = NULL;
|
|
struct ice_part_cfg *elem = NULL;
|
|
int rc = -EINVAL;
|
|
char *envp[2] = {0};
|
|
char part_name[CRYPTO_ICE_UDEV_PARTITION_NAME] = {0};
|
|
|
|
/* Check if the partition is already in the list */
|
|
list_for_each(new_pos, &ice_dev->part_cfg_list) {
|
|
elem = list_entry(new_pos, struct ice_part_cfg, list);
|
|
if (!strcmp(new_volname, elem->volname)) {
|
|
rc = 0;
|
|
goto out; /* Already in list, bail */
|
|
}
|
|
}
|
|
|
|
dev_info(ice_dev->pdev, "Adding %s to ICE partition list\n", new_volname);
|
|
|
|
/* Didn't find it, add new entry at the end */
|
|
elem = kzalloc(sizeof(struct ice_part_cfg), GFP_KERNEL);
|
|
if (!elem) {
|
|
rc = -ENOMEM;
|
|
dev_err(ice_dev->pdev,
|
|
"%s: Error %d allocating memory for partition %s\n",
|
|
__func__, rc, new_volname);
|
|
goto out;
|
|
}
|
|
|
|
/* Add the new partition to the KSet */
|
|
elem->kobj.kset = ice_dev->fde_partitions;
|
|
/* Set up the sysfs node */
|
|
elem->kobj_type.release = crypto_qti_ice_release_kobj;
|
|
elem->kobj_type.sysfs_ops = &kobj_sysfs_ops;
|
|
elem->kobj_type.default_attrs = ice_part_cfg_attrs;
|
|
rc = kobject_init_and_add(&elem->kobj, &elem->kobj_type,
|
|
NULL, new_volname);
|
|
if (rc) {
|
|
dev_err(ice_dev->pdev, "%s: Error %d adding sysfs for %s\n",
|
|
__func__, rc, new_volname);
|
|
kobject_put(&elem->kobj);
|
|
kfree(elem);
|
|
goto out;
|
|
}
|
|
|
|
/* Null terminated */
|
|
strscpy(elem->volname, new_volname, PARTITION_META_INFO_VOLNAMELTH + 1);
|
|
|
|
/* Default - encryption is enabled */
|
|
elem->decr_bypass = false;
|
|
elem->encr_bypass = false;
|
|
|
|
/* Start using key slots from the end */
|
|
if (ice_dev->num_fde_slots_in_use >= TOTAL_NUMBER_ICE_SLOTS) {
|
|
/* Shouldn't be here , just some defensive check */
|
|
rc = -ENOMEM;
|
|
dev_err(ice_dev->pdev, "%s: Allocating more slots than available %s\n", __func__);
|
|
kobject_put(&elem->kobj);
|
|
kfree(elem);
|
|
goto out;
|
|
}
|
|
|
|
elem->key_slot = slot;
|
|
elem->add_partition_result = new_key;
|
|
list_add_tail(&elem->list, new_pos);
|
|
/* Generate the UEvent with the partition name to notify userpsace */
|
|
snprintf(part_name, CRYPTO_ICE_UDEV_PARTITION_NAME, "FDE_PARTITION=%s", new_volname);
|
|
envp[0] = part_name;
|
|
envp[1] = NULL;
|
|
kobject_uevent_env(&elem->kobj, KOBJ_ADD, envp);
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Attribute "store" method for add_partition
|
|
*
|
|
* Add a partition to the list with default configuration.
|
|
*/
|
|
static ssize_t add_partition_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf,
|
|
size_t count)
|
|
{
|
|
struct ice_device *ice_dev = dev_get_drvdata(dev);
|
|
ssize_t ret = count;
|
|
char label[PARTITION_META_INFO_VOLNAMELTH + 1] = {0};
|
|
char key_id[CRYPTO_ICE_KEY_ID_SIZE] = {0};
|
|
char *flag = "-hash";
|
|
char *hash_index = NULL;
|
|
bool hash_enable = false;
|
|
size_t partition_count = count;
|
|
int rc = 0;
|
|
struct list_head *new_pos = NULL;
|
|
struct ice_part_cfg *elem = NULL;
|
|
unsigned int slot = 0;
|
|
bool new_key_generated = false;
|
|
int key_res = 0;
|
|
|
|
/* The count of the buf include hash size and flag size */
|
|
if (count > PARTITION_META_INFO_VOLNAMELTH + CRYPTO_ICE_HASH_SIZE + strlen(flag)) {
|
|
dev_err(dev, "Invalid partition '%s' (%u)\n", buf, count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!buf) {
|
|
dev_err(dev, "Invalid buf\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ice_dev) {
|
|
dev_err(dev, "Invalid ICE device!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* check if the userspace input the hash to generate ICE key
|
|
* -hash means input hash with legacy key id
|
|
*/
|
|
hash_index = strnstr(buf, flag, count);
|
|
if (hash_index) {
|
|
/*
|
|
* It must follow the right format to input partition name and hash,
|
|
* partition name can't be null.
|
|
*/
|
|
if (hash_index > buf + 1) {
|
|
hash_enable = true;
|
|
/* skip the blank and get the partition name count */
|
|
partition_count = hash_index - buf - 1;
|
|
/* skip to the header pointer of hash */
|
|
hash_index += strlen(flag) + 1;
|
|
/* exclude "\r" that in the end of input buf, and check hash size */
|
|
if (strlen(hash_index) - 1 != CRYPTO_ICE_HASH_SIZE) {
|
|
dev_err(dev, "Invalid hash\n");
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
dev_err(dev, "Invalid partition\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (!hash_enable) {
|
|
/* Copy into a temporary buffer, stripping out newlines */
|
|
partition_count = strcspn(buf, "\n\r");
|
|
if (!partition_count) {
|
|
dev_err(dev, "Invalid partition '%s' (%u)\n", buf, partition_count);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* copy the partition name */
|
|
memcpy(label, buf, partition_count);
|
|
label[partition_count] = '\0';
|
|
|
|
mutex_lock(&ice_dev->mutex);
|
|
/* Check if the partition is already in the list */
|
|
list_for_each(new_pos, &ice_dev->part_cfg_list)
|
|
{
|
|
elem = list_entry(new_pos, struct ice_part_cfg, list);
|
|
if (!strcmp(label, elem->volname)) {
|
|
dev_info(dev, "partition exist already, needn't to add\n");
|
|
ret = -EINVAL; /* Already in the list , return */
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* New partition , check if we have a free slot available */
|
|
if (ice_dev->num_fde_slots_in_use >= ice_dev->num_fde_slots) {
|
|
dev_err(dev, "All ICE slots are in use!\n");
|
|
ret = -EINVAL; /* Already in the list , return */
|
|
goto out;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE)
|
|
/* Don't increment , using same slot for all partitions */
|
|
slot = CRYPTO_ICE_FDE_KEY_INDEX;
|
|
#else
|
|
/* Increment number of slots in use */
|
|
++ice_dev->num_fde_slots_in_use;
|
|
slot = TOTAL_NUMBER_ICE_SLOTS - ice_dev->num_fde_slots_in_use;
|
|
#endif
|
|
|
|
/*
|
|
* First need to generate/restore key and set it into the ice slot
|
|
* Request is accroding to storage type UFS/eMMC
|
|
* The unique key is generated via partition name , or legacy key if configured
|
|
*/
|
|
if (strcmp(ice_dev->ice_instance_type, "ufs") == 0) {
|
|
#if IS_ENABLED(CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE)
|
|
/* In legacy mode, only one common key is needed */
|
|
if (list_empty(&ice_dev->part_cfg_list)) {
|
|
/* get the key_id that depends on if inhash_enable or not */
|
|
if (get_key_id(key_id, QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION,
|
|
hash_enable)) {
|
|
dev_err(dev, "Fail to get the ice key_id\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
key_res = qseecom_create_key_in_slot(
|
|
QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION,
|
|
slot, key_id, hash_index);
|
|
} else {
|
|
/* Key is already generated and set,contune */
|
|
key_res = QSEECOM_KEY_ID_EXISTS;
|
|
}
|
|
#else
|
|
key_res = qseecom_create_key_in_slot(
|
|
QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION,
|
|
slot, label, NULL);
|
|
#endif
|
|
} else if (strcmp(ice_dev->ice_instance_type, "sdcc") == 0) {
|
|
#if IS_ENABLED(CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE)
|
|
/* In legacy mode, only one common key is needed */
|
|
if (list_empty(&ice_dev->part_cfg_list)) {
|
|
/* get the key_id that depends on if inhash_enable or not */
|
|
if (get_key_id(key_id, QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION,
|
|
hash_enable)) {
|
|
dev_err(dev, "Fail to get the ice key_id\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
key_res = qseecom_create_key_in_slot(
|
|
QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION,
|
|
slot, key_id, hash_index);
|
|
} else {
|
|
/* Key is already generated and set,contune */
|
|
key_res = QSEECOM_KEY_ID_EXISTS;
|
|
}
|
|
#else
|
|
key_res = qseecom_create_key_in_slot(
|
|
QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION,
|
|
slot, label, NULL);
|
|
#endif
|
|
} else {
|
|
dev_err(dev, "Not supported storage type!\n");
|
|
ret = -EINVAL; /* Already in the list , return */
|
|
goto out;
|
|
}
|
|
|
|
/* Check the qseecom_create_key_in_slot result */
|
|
if (key_res) {
|
|
if (key_res == QSEECOM_KEY_ID_EXISTS) {
|
|
dev_info(dev, "ICE key exist\n");
|
|
new_key_generated = true;
|
|
} else {
|
|
dev_err(dev, "Failed to generate and set key!\n");
|
|
#ifndef CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE
|
|
/* Take the slot back */
|
|
--ice_dev->num_fde_slots_in_use;
|
|
#endif
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Error logged in function */
|
|
rc = crypto_qti_ice_add_new_partition(ice_dev, label, slot, new_key_generated);
|
|
if (rc)
|
|
ret = rc;
|
|
|
|
out:
|
|
mutex_unlock(&ice_dev->mutex);
|
|
return ret;
|
|
}
|
|
|
|
/* add_partition_store must ahead of referring it */
|
|
static struct device_attribute dev_attr_add_partition = __ATTR_WR_PARTITIONS(add_partition);
|
|
|
|
static struct attribute *ice_attrs[] = {
|
|
&dev_attr_add_partition.attr,
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(ice);
|
|
|
|
unsigned int crypto_qti_ice_get_num_fde_slots(void)
|
|
{
|
|
struct ice_device *ice_dev = NULL;
|
|
|
|
ice_dev = list_first_entry_or_null(&ice_devices, struct ice_device, list);
|
|
if (ice_dev)
|
|
return ice_dev->num_fde_slots;
|
|
else
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_ice_get_num_fde_slots);
|
|
|
|
static int crypto_qti_ice_get_vreg(struct ice_device *ice_dev)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!ice_dev->is_regulator_available)
|
|
return ret;
|
|
|
|
if (ice_dev->reg)
|
|
return ret;
|
|
|
|
ice_dev->reg = devm_regulator_get(ice_dev->pdev, "vdd-hba");
|
|
if (IS_ERR(ice_dev->reg)) {
|
|
ret = PTR_ERR(ice_dev->reg);
|
|
dev_err(ice_dev->pdev, "%s: %s get failed, err=%d\n", __func__,
|
|
"vdd-hba-supply", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* configure setting to partition */
|
|
static int crypto_qti_ice_setting_config(struct request *req,
|
|
struct ice_crypto_setting *crypto_data,
|
|
struct ice_data_setting *setting,
|
|
bool encr_bypass,
|
|
bool decr_bypass)
|
|
{
|
|
if (!setting)
|
|
return -EINVAL;
|
|
|
|
if ((short)(crypto_data->key_index) >= 0) {
|
|
memcpy(&setting->crypto_data, crypto_data,
|
|
sizeof(setting->crypto_data));
|
|
|
|
if (rq_data_dir(req) == WRITE) {
|
|
setting->encr_bypass = encr_bypass;
|
|
} else if (rq_data_dir(req) == READ) {
|
|
setting->decr_bypass = decr_bypass;
|
|
} else {
|
|
/* Should I say BUG_ON */
|
|
pr_err("%s unhandled request 0x%x\n", __func__, req->cmd_flags);
|
|
setting->encr_bypass = true;
|
|
setting->decr_bypass = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void crypto_qti_ice_disable_intr(struct ice_device *ice_dev)
|
|
{
|
|
unsigned int reg;
|
|
|
|
reg = crypto_qti_ice_readl(ice_dev, ICE_REGS_NON_SEC_IRQ_MASK);
|
|
reg |= ICE_NON_SEC_IRQ_MASK;
|
|
crypto_qti_ice_writel(ice_dev, reg, ICE_REGS_NON_SEC_IRQ_MASK);
|
|
/*
|
|
* Ensure previous instructions was completed before issuing next
|
|
* ICE initialization/optimization instruction
|
|
*/
|
|
mb();
|
|
}
|
|
|
|
static int crypto_qti_ice_parse_ice_instance_type(struct platform_device *pdev,
|
|
struct ice_device *ice_dev)
|
|
{
|
|
int ret;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
const char *type;
|
|
|
|
ret = of_property_read_string_index(np, "qcom,instance-type", 0, &type);
|
|
if (ret) {
|
|
pr_err("%s: Could not get ICE instance type\n", __func__);
|
|
return ret;
|
|
}
|
|
strscpy(ice_dev->ice_instance_type, type, CRYPTO_ICE_TYPE_NAME_LEN);
|
|
return 0;
|
|
}
|
|
|
|
static void crypto_qti_ice_parse_number_of_fde_slots(struct platform_device *pdev,
|
|
struct ice_device *ice_dev)
|
|
{
|
|
int ret = 0;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
u32 num_of_slots = 0;
|
|
|
|
ret = of_property_read_u32(np, "qcom,num-fde-slots", &num_of_slots);
|
|
if (ret) {
|
|
/* Backwards compatibility , assume one slot if DTS property is not deifined */
|
|
pr_info("%s: No num of FDE slots defined in dts,using 1 slot\n", __func__);
|
|
ice_dev->num_fde_slots = 1;
|
|
return;
|
|
}
|
|
if (num_of_slots >= TOTAL_NUMBER_ICE_SLOTS) {
|
|
/* Limit the number of slots */
|
|
pr_info("%s: Limiting num of FDE slots\n", __func__);
|
|
ice_dev->num_fde_slots = TOTAL_NUMBER_ICE_SLOTS - 1;
|
|
} else {
|
|
ice_dev->num_fde_slots = num_of_slots;
|
|
}
|
|
}
|
|
|
|
static int crypto_qti_ice_parse_clock_info(struct platform_device *pdev,
|
|
struct ice_device *ice_dev)
|
|
{
|
|
int cnt, i, len;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
char *name;
|
|
struct ice_clk_info *clki;
|
|
u32 *clkfreq = NULL;
|
|
int ret = -EINVAL;
|
|
|
|
if (!np)
|
|
goto out;
|
|
|
|
cnt = of_property_count_strings(np, "clock-names");
|
|
if (cnt <= 0) {
|
|
dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", __func__);
|
|
ret = cnt;
|
|
goto out;
|
|
}
|
|
|
|
if (!of_get_property(np, "qcom,op-freq-hz", &len)) {
|
|
dev_info(dev, "qcom,op-freq-hz property not specified\n");
|
|
goto out;
|
|
}
|
|
|
|
len = len/sizeof(*clkfreq);
|
|
if (len != cnt)
|
|
goto out;
|
|
|
|
clkfreq = devm_kzalloc(dev, len * sizeof(*clkfreq), GFP_KERNEL);
|
|
if (!clkfreq) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
ret = of_property_read_u32_array(np, "qcom,op-freq-hz", clkfreq, len);
|
|
|
|
INIT_LIST_HEAD(&ice_dev->clk_list_head);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
ret = of_property_read_string_index(np, "clock-names", i, (const char **)&name);
|
|
if (ret)
|
|
goto out;
|
|
|
|
clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
|
|
if (!clki) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
clki->max_freq = clkfreq[i];
|
|
clki->name = kstrdup(name, GFP_KERNEL);
|
|
list_add_tail(&clki->list, &ice_dev->clk_list_head);
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int crypto_qti_ice_get_dts_data(struct platform_device *pdev,
|
|
struct ice_device *ice_dev)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
ice_dev->mmio = NULL;
|
|
if (!of_parse_phandle(pdev->dev.of_node, "vdd-hba-supply", 0)) {
|
|
pr_err("%s: No vdd-hba-supply regulator, assuming not needed\n", __func__);
|
|
ice_dev->is_regulator_available = false;
|
|
} else {
|
|
ice_dev->is_regulator_available = true;
|
|
}
|
|
ice_dev->is_ice_clk_available = of_property_read_bool((&pdev->dev)->of_node,
|
|
"qcom,enable-ice-clk");
|
|
|
|
if (ice_dev->is_ice_clk_available) {
|
|
ret = crypto_qti_ice_parse_clock_info(pdev, ice_dev);
|
|
if (ret) {
|
|
pr_err("%s: crypto_qti_ice_parse_clock_info failed (%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = crypto_qti_ice_parse_ice_instance_type(pdev, ice_dev);
|
|
if (ret) {
|
|
pr_err("%s: fail to parse ice instance type (%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
crypto_qti_ice_parse_number_of_fde_slots(pdev, ice_dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* ICE HW instance can exist in UFS or eMMC based storage HW
|
|
* Userspace does not know what kind of ICE it is dealing with.
|
|
* Though userspace can find which storage device it is booting
|
|
* from but all kind of storage types dont support ICE from
|
|
* beginning. So ICE device is created for user space to ping
|
|
* if ICE exist for that kind of storage
|
|
*/
|
|
static const struct file_operations crypto_qti_ice_fops = {
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
/**
|
|
* Remove all sysfs resources associated with the driver
|
|
*
|
|
* @ice_dev: ICE device node
|
|
*/
|
|
static void crypto_qti_ice_cleanup_sysfs(struct ice_device *ice_dev)
|
|
{
|
|
struct list_head *pos = NULL;
|
|
struct list_head *n = NULL;
|
|
struct ice_part_cfg *elem = NULL;
|
|
|
|
/* Platform device attributes */
|
|
if (ice_dev->sysfs_groups_created) {
|
|
sysfs_remove_groups(&ice_dev->pdev->kobj, ice_groups);
|
|
ice_dev->sysfs_groups_created = false;
|
|
}
|
|
|
|
/* Partition configuration list and sysfs nodes */
|
|
list_for_each_safe(pos, n, &ice_dev->part_cfg_list) {
|
|
elem = list_entry(pos, struct ice_part_cfg, list);
|
|
dev_dbg(ice_dev->pdev, "Removing %s", elem->volname);
|
|
|
|
if (elem->kobj.state_in_sysfs)
|
|
kobject_put(&elem->kobj);
|
|
|
|
list_del(pos);
|
|
kfree(elem);
|
|
}
|
|
if (ice_dev->fde_partitions != NULL)
|
|
kset_unregister(ice_dev->fde_partitions);
|
|
}
|
|
|
|
static int crypto_qti_ice_probe(struct platform_device *pdev)
|
|
{
|
|
struct ice_device *ice_dev;
|
|
int rc = -EINVAL;
|
|
|
|
if (!pdev) {
|
|
pr_err("%s: Invalid platform_device passed\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
ice_dev = kzalloc(sizeof(struct ice_device), GFP_KERNEL);
|
|
|
|
if (!ice_dev) {
|
|
rc = -ENOMEM;
|
|
pr_err("%s: Error %d allocating memory for ICE device:\n", __func__, rc);
|
|
goto out;
|
|
}
|
|
|
|
/* Initialize device data to a known state */
|
|
INIT_LIST_HEAD(&ice_dev->part_cfg_list);
|
|
ice_dev->pdev = &pdev->dev;
|
|
if (!ice_dev->pdev) {
|
|
rc = -EINVAL;
|
|
pr_err("%s: Invalid device passed in platform_device\n", __func__);
|
|
goto err_ice_dev;
|
|
}
|
|
|
|
if (pdev->dev.of_node)
|
|
rc = crypto_qti_ice_get_dts_data(pdev, ice_dev);
|
|
else {
|
|
rc = -EINVAL;
|
|
pr_err("%s: ICE device node not found\n", __func__);
|
|
}
|
|
|
|
if (rc)
|
|
goto err_ice_dev;
|
|
|
|
/*
|
|
* If ICE is enabled here, it would be waste of power.
|
|
* We would enable ICE when first request for crypto
|
|
* operation arrives.
|
|
*/
|
|
rc = crypto_qti_ice_init(ice_dev, NULL, NULL);
|
|
if (rc) {
|
|
pr_err("ice_init failed.\n");
|
|
goto err_ice_dev;
|
|
}
|
|
mutex_init(&ice_dev->mutex);
|
|
ice_dev->is_ice_enabled = true;
|
|
platform_set_drvdata(pdev, ice_dev);
|
|
list_add_tail(&ice_dev->list, &ice_devices);
|
|
|
|
dev_info(&pdev->dev, "Initialized OK\n");
|
|
|
|
goto out;
|
|
|
|
err_ice_dev:
|
|
crypto_qti_ice_cleanup_sysfs(ice_dev);
|
|
|
|
kfree(ice_dev);
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
static int crypto_qti_ice_remove(struct platform_device *pdev)
|
|
{
|
|
struct ice_device *ice_dev;
|
|
|
|
ice_dev = (struct ice_device *)platform_get_drvdata(pdev);
|
|
|
|
if (!ice_dev)
|
|
return 0;
|
|
|
|
mutex_destroy(&ice_dev->mutex);
|
|
crypto_qti_ice_cleanup_sysfs(ice_dev);
|
|
|
|
crypto_qti_ice_disable_intr(ice_dev);
|
|
|
|
device_init_wakeup(&pdev->dev, false);
|
|
if (ice_dev->mmio)
|
|
iounmap(ice_dev->mmio);
|
|
|
|
list_del_init(&ice_dev->list);
|
|
kfree(ice_dev);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int crypto_qti_ice_config_start(struct request *req, struct ice_data_setting *setting)
|
|
{
|
|
struct ice_crypto_setting ice_data = {0};
|
|
struct ice_device *ice_dev;
|
|
struct ice_part_cfg *part_cfg;
|
|
int ret = -EINVAL;
|
|
|
|
if (!req)
|
|
goto out;
|
|
|
|
/*
|
|
* It is not an error to have a request with no bio
|
|
* Such requests must bypass ICE. So first set bypass and then
|
|
* return if bio is not available in request
|
|
*/
|
|
if (setting) {
|
|
setting->encr_bypass = true;
|
|
setting->decr_bypass = true;
|
|
}
|
|
|
|
ice_dev = list_first_entry_or_null(&ice_devices, struct ice_device, list);
|
|
if (req->part && req->part->bd_meta_info && req->part->bd_meta_info->volname[0]) {
|
|
part_cfg = crypto_qti_ice_get_part_cfg(ice_dev,
|
|
req->part->bd_meta_info->volname);
|
|
if (part_cfg) {
|
|
ice_data.key_index = part_cfg->key_slot;
|
|
/* the partition info has remove __sector/nr_sects/start_sect, so don't
|
|
* try to check the sector. TBD
|
|
*/
|
|
ret = crypto_qti_ice_setting_config(req,
|
|
&ice_data, setting,
|
|
part_cfg->encr_bypass,
|
|
part_cfg->decr_bypass);
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_ice_config_start);
|
|
|
|
static int crypto_qti_ice_enable_clocks(struct ice_device *ice, bool enable)
|
|
{
|
|
int ret = 0;
|
|
struct ice_clk_info *clki = NULL;
|
|
struct device *dev = ice->pdev;
|
|
struct list_head *head = &ice->clk_list_head;
|
|
|
|
if (!head || list_empty(head)) {
|
|
dev_err(dev, "%s:ICE Clock list null/empty\n", __func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (!ice->is_ice_clk_available) {
|
|
dev_err(dev, "%s:ICE Clock not available\n", __func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
list_for_each_entry(clki, head, list) {
|
|
if (!clki->name)
|
|
continue;
|
|
|
|
if (enable)
|
|
ret = clk_prepare_enable(clki->clk);
|
|
else
|
|
clk_disable_unprepare(clki->clk);
|
|
|
|
if (ret) {
|
|
dev_err(dev, "Unable to %s ICE core clk\n", enable?"enable":"disable");
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static struct ice_device *crypto_qti_get_ice_device_from_storage_type(const char *storage_type)
|
|
{
|
|
struct ice_device *ice_dev = NULL;
|
|
|
|
if (list_empty(&ice_devices)) {
|
|
ice_dev = ERR_PTR(-EPROBE_DEFER);
|
|
goto out;
|
|
}
|
|
|
|
list_for_each_entry(ice_dev, &ice_devices, list) {
|
|
if (!strcmp(ice_dev->ice_instance_type, storage_type)) {
|
|
pr_debug("%s: ice device %pK\n", __func__, ice_dev);
|
|
return ice_dev;
|
|
}
|
|
}
|
|
out:
|
|
return NULL;
|
|
}
|
|
|
|
static int crypto_qti_ice_enable_setup(struct ice_device *ice_dev)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
/* Setup Regulator */
|
|
if (ice_dev->is_regulator_available) {
|
|
if (crypto_qti_ice_get_vreg(ice_dev)) {
|
|
pr_err("%s: Could not get regulator\n", __func__);
|
|
goto out;
|
|
}
|
|
ret = regulator_enable(ice_dev->reg);
|
|
if (ret) {
|
|
pr_err("%s:%pK: Could not enable regulator\n", __func__, ice_dev);
|
|
goto out;
|
|
}
|
|
} else {
|
|
pr_info("%s: No need to get regulator\n", __func__);
|
|
ret = 0;
|
|
}
|
|
|
|
/* Setup Clocks */
|
|
if (crypto_qti_ice_enable_clocks(ice_dev, true)) {
|
|
pr_err("%s:%pK:%s Could not enable clocks\n", __func__,
|
|
ice_dev, ice_dev->ice_instance_type);
|
|
if (ice_dev->is_regulator_available) {
|
|
if (crypto_qti_ice_get_vreg(ice_dev)) {
|
|
pr_err("%s: Could not get regulator\n", __func__);
|
|
goto out;
|
|
}
|
|
ret = regulator_disable(ice_dev->reg);
|
|
if (ret)
|
|
pr_err("%s:%pK: Could not disable regulator\n", __func__, ice_dev);
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int crypto_qti_ice_disable_setup(struct ice_device *ice_dev)
|
|
{
|
|
int ret;
|
|
|
|
/* Setup Clocks */
|
|
ret = crypto_qti_ice_enable_clocks(ice_dev, false);
|
|
if (ret) {
|
|
pr_err("%s:%pK:%s Could not disable clocks\n", __func__,
|
|
ice_dev, ice_dev->ice_instance_type);
|
|
goto out;
|
|
}
|
|
|
|
/* Setup Regulator */
|
|
if (ice_dev->is_regulator_available) {
|
|
ret = crypto_qti_ice_get_vreg(ice_dev);
|
|
if (ret) {
|
|
pr_err("%s: Could not get regulator\n", __func__);
|
|
goto out;
|
|
}
|
|
ret = regulator_disable(ice_dev->reg);
|
|
if (ret) {
|
|
pr_err("%s:%pK: Could not disable regulator\n", __func__, ice_dev);
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int crypto_qti_ice_init_clocks(struct ice_device *ice)
|
|
{
|
|
int ret = -EINVAL;
|
|
struct ice_clk_info *clki = NULL;
|
|
struct device *dev = ice->pdev;
|
|
struct list_head *head = &ice->clk_list_head;
|
|
|
|
if (!head || list_empty(head)) {
|
|
dev_err(dev, "%s:ICE Clock list null/empty\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
list_for_each_entry(clki, head, list) {
|
|
if (!clki->name)
|
|
continue;
|
|
|
|
clki->clk = devm_clk_get(dev, clki->name);
|
|
if (IS_ERR(clki->clk)) {
|
|
ret = PTR_ERR(clki->clk);
|
|
dev_err(dev, "%s: %s clk get failed, %d\n",
|
|
__func__, clki->name, ret);
|
|
goto out;
|
|
}
|
|
|
|
/* Not all clocks would have a rate to be set */
|
|
ret = 0;
|
|
if (clki->max_freq) {
|
|
ret = clk_set_rate(clki->clk, clki->max_freq);
|
|
if (ret) {
|
|
dev_err(dev,
|
|
"%s: %s clk set rate(%dHz) failed, %d\n",
|
|
__func__, clki->name,
|
|
clki->max_freq, ret);
|
|
goto out;
|
|
}
|
|
clki->curr_freq = clki->max_freq;
|
|
dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__,
|
|
clki->name, clk_get_rate(clki->clk));
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int crypto_qti_ice_finish_init(struct ice_device *ice_dev)
|
|
{
|
|
int err = 0;
|
|
|
|
if (!ice_dev) {
|
|
pr_err("%s: Null data received\n", __func__);
|
|
err = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
if (ice_dev->is_ice_clk_available) {
|
|
err = crypto_qti_ice_init_clocks(ice_dev);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int crypto_qti_ice_init(struct ice_device *ice_dev,
|
|
void *host_controller_data,
|
|
ice_error_cb error_cb)
|
|
{
|
|
/*
|
|
* A completion event for host controller would be triggered upon
|
|
* initialization completion
|
|
* When ICE is initialized, it would put ICE into Global Bypass mode
|
|
* When any request for data transfer is received, it would enable
|
|
* the ICE for that particular request
|
|
*/
|
|
ice_dev->error_cb = error_cb;
|
|
ice_dev->host_controller_data = host_controller_data;
|
|
|
|
return crypto_qti_ice_finish_init(ice_dev);
|
|
}
|
|
|
|
int crypto_qti_ice_init_fde_node(struct device *dev)
|
|
{
|
|
|
|
struct ice_device *ice_dev = NULL;
|
|
int rc = -EINVAL;
|
|
|
|
if (!dev) {
|
|
pr_err("%s: Invalid platform_device passed\n", __func__);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Only one ice device is supported , remove the extra one(DTS has more than one node) */
|
|
|
|
if (!strcmp(CRYPTO_ICE_UFS_DEV_NAME, dev_driver_string(dev))) {
|
|
/* ufs found , remove eMMC node if exists */
|
|
ice_dev = crypto_qti_get_ice_device_from_storage_type("sdcc");
|
|
if (ice_dev) {
|
|
mutex_destroy(&ice_dev->mutex);
|
|
|
|
if (ice_dev->mmio)
|
|
iounmap(ice_dev->mmio);
|
|
|
|
list_del(&ice_dev->list);
|
|
kfree(ice_dev);
|
|
}
|
|
/* Get UFS instance */
|
|
ice_dev = crypto_qti_get_ice_device_from_storage_type("ufs");
|
|
if (ice_dev == NULL) {
|
|
pr_err("ice_dev NULL !\n");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(CRYPTO_ICE_EMMC_DEV_NAME, dev_driver_string(dev))) {
|
|
ice_dev = crypto_qti_get_ice_device_from_storage_type("ufs");
|
|
if (ice_dev) {
|
|
/* eMMC found , remove UFS node if exists */
|
|
mutex_destroy(&ice_dev->mutex);
|
|
if (ice_dev->mmio)
|
|
iounmap(ice_dev->mmio);
|
|
|
|
list_del(&ice_dev->list);
|
|
kfree(ice_dev);
|
|
}
|
|
|
|
/* Get eMMC instance */
|
|
ice_dev = crypto_qti_get_ice_device_from_storage_type("sdcc");
|
|
if (ice_dev == NULL) {
|
|
pr_err("ice_dev NULL !\n");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (!ice_dev) {
|
|
pr_err("%s: No ice device found\n", __func__);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Create a Kset for the encrypted partitions */
|
|
ice_dev->fde_partitions = kset_create_and_add("fde_partitions", NULL, &ice_dev->pdev->kobj);
|
|
if (ice_dev->fde_partitions == NULL) {
|
|
rc = -EINVAL;
|
|
pr_err("%s: Failed to create partitons KSet\n", __func__);
|
|
goto err_ice_dev;
|
|
}
|
|
|
|
|
|
/* Set up platform device attributes in sysfs */
|
|
rc = sysfs_create_groups(&ice_dev->pdev->kobj, ice_groups);
|
|
if (rc) {
|
|
pr_err("%s: Failed to add pdev attributes: %d\n", __func__, rc);
|
|
goto err_ice_dev;
|
|
} else {
|
|
ice_dev->sysfs_groups_created = true;
|
|
}
|
|
|
|
/* Set up partition config folder in sysfs */
|
|
ice_dev->partitions_kobj_type.release = crypto_qti_ice_release_kobj;
|
|
|
|
goto out;
|
|
|
|
err_ice_dev:
|
|
crypto_qti_ice_cleanup_sysfs(ice_dev);
|
|
out:
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_ice_init_fde_node);
|
|
|
|
int crypto_qti_ice_setup_ice_hw(const char *storage_type, int enable)
|
|
{
|
|
int ret = -EINVAL;
|
|
struct ice_device *ice_dev = NULL;
|
|
|
|
ice_dev = crypto_qti_get_ice_device_from_storage_type(storage_type);
|
|
if (ice_dev == ERR_PTR(-EPROBE_DEFER))
|
|
return -EPROBE_DEFER;
|
|
|
|
if (!ice_dev || !ice_dev->is_ice_enabled)
|
|
return ret;
|
|
if (enable)
|
|
return crypto_qti_ice_enable_setup(ice_dev);
|
|
else
|
|
return crypto_qti_ice_disable_setup(ice_dev);
|
|
}
|
|
EXPORT_SYMBOL(crypto_qti_ice_setup_ice_hw);
|
|
|
|
#else /* dumy ufsice driver */
|
|
|
|
static int crypto_qti_ice_probe(struct platform_device *pdev)
|
|
{
|
|
dev_info(&pdev->dev, "Proxy probing\n");
|
|
return 0;
|
|
}
|
|
|
|
static int crypto_qti_ice_remove(struct platform_device *pdev)
|
|
{
|
|
dev_info(&pdev->dev, "Proxy removing\n");
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_QTI_CRYPTO_FDE */
|
|
|
|
/* Following struct is required to match device with driver from dts file */
|
|
|
|
static const struct of_device_id crypto_qti_ice_match[] = {
|
|
{ .compatible = "qcom,ice" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, crypto_qti_ice_match);
|
|
|
|
static struct platform_driver crypto_qti_ice_driver = {
|
|
.probe = crypto_qti_ice_probe,
|
|
.remove = crypto_qti_ice_remove,
|
|
.driver = {
|
|
.name = "qcom_ice",
|
|
.of_match_table = crypto_qti_ice_match,
|
|
},
|
|
};
|
|
module_platform_driver(crypto_qti_ice_driver);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("Common crypto library for storage encryption");
|