kernel_samsung_a53x/drivers/scsi/ufs/ufs-exynos-fmp.c
2024-06-15 16:02:09 -03:00

1031 lines
29 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Exynos FMP (Flash Memory Protector) UFS crypto support
*
* Copyright (C) 2020 Samsung Electronics Co., Ltd.
* Copyright 2020 Google LLC
*
* Authors: Boojin Kim <boojin.kim@samsung.com>
* Eric Biggers <ebiggers@google.com>
*/
#include <asm/unaligned.h>
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <soc/samsung/exynos-smc.h>
#include <linux/of.h>
#include "ufshcd.h"
#include "ufshcd-crypto.h"
#include "ufs-vs-mmio.h"
#include "ufs-cal-if.h"
#include "ufs-exynos.h"
#include "ufs-exynos-fmp.h"
#include <trace/hooks/ufshcd.h>
#ifdef CONFIG_EXYNOS_FMP_FIPS
#include <crypto/fmp_fips.h>
#endif
#ifndef CONFIG_KEYS_IN_PRDT
#ifdef CONFIG_HW_KEYS_IN_CUSTOM_KEYSLOT
struct device dev_fmp;
#endif
#define MAX_RETRY_COUNT 0x100
#endif
#ifdef CONFIG_EXYNOS_FIPS_SIMULATOR
static struct fmp_handle *fmp_ufs_handle;
struct fmp_handle *get_fmp_handle()
{
return fmp_ufs_handle;
}
EXPORT_SYMBOL(get_fmp_handle);
static int FIPS_keyslot_num;
void set_fips_keyslot_num(int num)
{
FIPS_keyslot_num = num;
}
EXPORT_SYMBOL(set_fips_keyslot_num);
#endif
#ifdef CONFIG_KEYS_IN_PRDT
static inline __be32 fmp_key_word(const u8 *key, int j)
{
return cpu_to_be32(get_unaligned_le32(
key + AES_KEYSIZE_256 - (j + 1) * sizeof(__le32)));
}
/* Configure FMP on requests that have an encryption context. */
static void exynos_ufs_fmp_fill_prdt(void *ignore, struct ufs_hba *hba, struct ufshcd_lrb *lrbp,
unsigned int segments, int *err)
{
#ifdef CONFIG_EXYNOS_FMP_FIPS
struct exynos_fmp_crypt_info fmp_ci;
int ret;
#endif
const struct bio_crypt_ctx *bc;
const u8 *enckey, *twkey;
u64 dun_lo, dun_hi;
struct fmp_sg_entry *table;
unsigned int i;
*err = 0;
/*
* There's nothing to do for unencrypted requests, since the mode field
* ("FAS") is already 0 (FMP_BYPASS_MODE) by default, as it's in the
* same word as ufshcd_sg_entry::size which was already initialized.
*/
bc = lrbp->cmd->request->crypt_ctx;
BUILD_BUG_ON(FMP_BYPASS_MODE != 0);
#ifdef CONFIG_EXYNOS_FMP_FIPS
if (!bc) {
if (!(lrbp->cmd->request->bio))
return;
if (is_fmp_fips_op(lrbp->cmd->request->bio)) {
fmp_ci.fips = true;
goto encrypt;
}
return;
}
enckey = bc->bc_key->raw;
twkey = enckey + AES_KEYSIZE_256;
dun_lo = bc->bc_dun[0];
dun_hi = bc->bc_dun[1];
fmp_ci.fips = false;
encrypt:
#else /* CONFIG_EXYNOS_FMP_FIPS */
if (!bc)
return;
#endif /* CONFIG_EXYNOS_FMP_FIPS */
/* If FMP wasn't enabled, we shouldn't get any encrypted requests. */
if (WARN_ON_ONCE(!(hba->caps & UFSHCD_CAP_CRYPTO))) {
dev_err(hba->dev, "UFSHCD_CAP_CRYPTO is not enabled\n");
*err = -ENOTSUPP;
return;
}
#ifdef CONFIG_EXYNOS_FMP_FIPS
table = (struct fmp_sg_entry *)lrbp->ucd_prdt_ptr;
for (i = 0; i < segments; i++) {
struct fmp_sg_entry *ent = &table[i];
struct ufshcd_sg_entry *prd = (struct ufshcd_sg_entry *)ent;
/* Each segment must be exactly one data unit. */
if (le32_to_cpu(prd->size) + 1 != FMP_DATA_UNIT_SIZE) {
dev_err(hba->dev, "scatterlist segment is misaligned for FMP\n");
*err = -EINVAL;
return;
}
fmp_ci.enckey = enckey;
fmp_ci.twkey = twkey;
fmp_ci.dun_lo = dun_lo;
fmp_ci.dun_hi = dun_hi;
ret = exynos_fmp_crypt(&fmp_ci, (void *)ent);
if (ret) {
dev_err(hba->dev, "%s: fails to crypt fmp. ret:%d\n", __func__, ret);
*err = ret;
return;
}
/* Increment the data unit number. */
dun_lo++;
if (dun_lo == 0)
dun_hi++;
}
#else
enckey = bc->bc_key->raw;
twkey = enckey + AES_KEYSIZE_256;
dun_lo = bc->bc_dun[0];
dun_hi = bc->bc_dun[1];
/* Reject weak AES-XTS keys. */
if (!crypto_memneq(enckey, twkey, AES_KEYSIZE_256)) {
dev_err(hba->dev, "Can't use weak AES-XTS key\n");
*err = -EINVAL;
return;
}
/* Configure FMP on each segment of the request. */
table = (struct fmp_sg_entry *)lrbp->ucd_prdt_ptr;
for (i = 0; i < segments; i++) {
struct fmp_sg_entry *ent = &table[i];
struct ufshcd_sg_entry *prd = (struct ufshcd_sg_entry *)ent;
size_t j, limit;
/* Each segment must be exactly one data unit. */
if (le32_to_cpu(prd->size) + 1 != FMP_DATA_UNIT_SIZE) {
dev_err(hba->dev, "scatterlist segment is misaligned for FMP\n");
*err = -EINVAL;
return;
}
/* Set the algorithm and key length. */
SET_FAS(ent, FMP_ALGO_MODE_AES_XTS);
SET_KEYLEN(ent, FKL);
/* Set the key. */
for (j = 0, limit = AES_KEYSIZE_256 / sizeof(u32); j < limit; j++) {
ent->file_enckey[j] = fmp_key_word(enckey, j);
ent->file_twkey[j] = fmp_key_word(twkey, j);
}
/* Set the IV. */
ent->file_iv[0] = cpu_to_be32(upper_32_bits(dun_hi));
ent->file_iv[1] = cpu_to_be32(lower_32_bits(dun_hi));
ent->file_iv[2] = cpu_to_be32(upper_32_bits(dun_lo));
ent->file_iv[3] = cpu_to_be32(lower_32_bits(dun_lo));
/* Increment the data unit number. */
dun_lo++;
if (dun_lo == 0)
dun_hi++;
}
#endif
return;
}
#else
static void exynos_ufs_fmp_populate_dt(struct device *dev, struct exynos_fmp* fmp)
{
struct device_node *np = dev->of_node;
struct device_node *child_np;
int ret = 0;
/* Check fmp status for featuring */
child_np = of_get_child_by_name(np, "ufs-protector");
if (!child_np) {
dev_info(dev, "No ufs-protector node, set to 1 for boot\n");
fmp->valid_check = 1;
}
else {
ret = of_property_read_u8(child_np, "valid-check", &fmp->valid_check);
if (ret) {
dev_info(dev, "read valid_check failed = 0x%x, set to 1\n", __func__, ret);
fmp->valid_check = 1;
}
}
}
void exynos_ufs_fmp_set_crypto_cfg(struct ufs_hba *hba)
{
union exynos_ufs_crypto_cfg_entry cfg = {};
struct exynos_ufs *ufs = to_exynos_ufs(hba);
/* Set crypto config register.*/
cfg.data_unit_size = FMP_DATA_UNIT_SIZE / 512;
cfg.config_enable = UFS_CRYPTO_CONFIGURATION_ENABLE;
std_writel(&ufs->handle, le32_to_cpu(cfg.reg_val), CRYPTOCFG);
dev_info(hba->dev, "%s set CRYPTOCFG = 0x%x\n", __func__, std_readl(&ufs->handle, CRYPTOCFG));
return;
}
static void exynos_ufs_fmp_prepare_command(void *ignore, struct ufs_hba *hba, struct request *rq,
struct ufshcd_lrb *lrbp, int *err)
{
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct exynos_fmp *fmp = (struct exynos_fmp*)ufs->fmp;
#ifdef CONFIG_EXYNOS_FMP_FIPS
const struct bio_crypt_ctx *bc;
int ret;
struct exynos_fmp_crypt_info fmp_ci;
BUILD_BUG_ON(FMP_BYPASS_MODE != 0);
bc = lrbp->cmd->request->crypt_ctx;
fmp_ci.fips = false;
if (!bc) {
if (!(lrbp->cmd->request->bio)) {
*err = 0;
return;
}
if (is_fmp_fips_op(lrbp->cmd->request->bio))
fmp_ci.fips = true;
else
goto out;
}
ret = exynos_fmp_crypt(&fmp_ci, (void *)&ufs->handle);
if (ret) {
dev_err(hba->dev, "%s: fail to crypt with fmp. ret:%d\n", __func__, ret);
*err = ret;
return;
}
if (fmp_ci.fips) {
#ifdef CONFIG_EXYNOS_FIPS_SIMULATOR
if ((FIPS_keyslot_num >= 0) && (FIPS_keyslot_num <= 15))
fmp_ci.crypto_key_slot = FIPS_keyslot_num;
#endif
lrbp->crypto_key_slot = fmp_ci.crypto_key_slot;
lrbp->data_unit_num = fmp_ci.data_unit_num;
*err = 0;
return;
}
out:
#endif /* CONFIG_EXYNOS_FMP_FIPS */
if ((lrbp->crypto_key_slot >= 0) && (fmp->valid_check == 1))
lrbp->crypto_key_slot++; /* account for hardware quirk */
*err = 0;
}
#endif
#ifdef CONFIG_KEYS_IN_PRDT
void exynos_ufs_fmp_init(struct ufs_hba *hba)
{
unsigned long ret;
#ifndef CONFIG_EXYNOS_FMP_FIPS
dev_info(hba->dev, "Exynos FMP Version: %s\n", FMP_DRV_VERSION);
#endif
dev_info(hba->dev, "KEYS_IN_PRDT\n");
ret = exynos_smc(SMC_CMD_SMU, SMU_INIT, FMP_EMBEDDED, 0);
if (ret)
dev_warn(hba->dev, "SMC_CMD_SMU(SMU_INIT) failed: %ld\n", ret);
ret = exynos_smc(SMC_CMD_FMP_SECURITY, 0, FMP_EMBEDDED, CFG_DESCTYPE_3);
if (ret) {
dev_warn(hba->dev, "SMC_CMD_FMP_SECURITY failed on init: %ld\n", ret);
goto disable;
}
else
hba->sg_entry_size = sizeof(struct fmp_sg_entry);
ret = exynos_smc(SMC_CMD_FMP_KW_MODE, 0, FMP_EMBEDDED, SWKEY_MODE);
if (ret) {
dev_warn(hba->dev, "SMC_CMD_FMP_KW_MODE failed on init: %ld\n", ret);
goto disable;
}
/* Advertise crypto support to ufshcd-core. */
hba->caps |= UFSHCD_CAP_CRYPTO;
/* Advertise crypto quirks to ufshcd-core. */
hba->quirks |= UFSHCD_QUIRK_FMP_MODE_SPECIFIC |
UFSHCD_QUIRK_FMP_SOC_SPECIFIC;
dev_info(hba->dev, "Exynos FMP quirks: 0x%x\n", hba->quirks);
/* Advertise crypto capabilities to the block layer. */
blk_ksm_init_passthrough(&hba->ksm);
hba->ksm.max_dun_bytes_supported = AES_BLOCK_SIZE;
hba->ksm.features = BLK_CRYPTO_FEATURE_STANDARD_KEYS;
hba->ksm.dev = hba->dev;
hba->ksm.crypto_modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] =
FMP_DATA_UNIT_SIZE;
register_trace_android_vh_ufs_fill_prdt(exynos_ufs_fmp_fill_prdt, NULL);
return;
disable:
dev_warn(hba->dev, "Disabling inline encryption support\n");
hba->caps &= ~UFSHCD_CAP_CRYPTO;
}
#endif /* CONFIG_KEYS_IN_PRDT */
#ifdef CONFIG_HW_KEYS_IN_CUSTOM_KEYSLOT
static int exynos_ufs_fmp_keyslot_program(struct blk_keyslot_manager *ksm,
const struct blk_crypto_key *key,
unsigned int keyslot)
{
struct ufs_hba *hba = container_of(ksm, struct ufs_hba, ksm);
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct exynos_fmp *fmp = (struct exynos_fmp*)ufs->fmp;
unsigned int num_keyslots = NUM_KEYSLOTS;
unsigned int slot = keyslot;
size_t i, limit;
u32 count = 0;
u32 kw_keyvalid;
u32 kw_tagabort;
u32 kw_pbkabort;
u32 kw_control;
u32 slot_offset = FMP_KW_SECUREKEY + slot*FMP_KW_SECUREKEY_OFFSET;
u32 tag_offset = FMP_KW_TAG + slot*FMP_KW_TAG_OFFSET;
u32 secret_size = SECRET_SIZE + AES_GCM_TAG_SIZE;
union {
u8 bytes[AES_256_XTS_KEY_SIZE + AES_GCM_TAG_SIZE];
u32 words[(AES_256_XTS_KEY_SIZE + AES_GCM_TAG_SIZE) / sizeof(u32)];
} fmp_key;
if (fmp->valid_check == 1) {
slot += 1;
num_keyslots -= 1;
}
slot_offset = FMP_KW_SECUREKEY + slot*FMP_KW_SECUREKEY_OFFSET;
tag_offset = FMP_KW_TAG + slot*FMP_KW_TAG_OFFSET;
/* To Do
* This is from ufshcd-crypto. Should check if this is needed.
*/
// ufshcd_hold(hba, false);
/* Only AES-256-XTS is supported */
if (key->crypto_cfg.crypto_mode != BLK_ENCRYPTION_MODE_AES_256_XTS ||
(key->size - secret_size) != (AES_256_XTS_KEY_SIZE + AES_GCM_TAG_SIZE)) {
dev_err(hba->dev,
"Unhandled crypto capability; crypto_mode=%d, key_size=%d\n",
key->crypto_cfg.crypto_mode, key->size - secret_size);
return -EINVAL;
}
dev_info(hba->dev, "%s slot = %d/%d\n",__func__, slot, num_keyslots);
/* In XTS mode, the blk_crypto_key's size is already doubled */
memcpy(fmp_key.bytes, key->raw, key->size - secret_size);
/* Key program in keyslot */
/* Swap File key and Tweak key */
for (i = 0, limit = AES_256_XTS_KEY_SIZE / (sizeof(u32) * 2); i < limit; i++) {
ufsp_writel(&ufs->handle, le32_to_cpu(fmp_key.words[i]),
slot_offset + ((i + AES_256_XTS_TWK_OFFSET) * sizeof(u32)));
ufsp_writel(&ufs->handle, le32_to_cpu(fmp_key.words[i + AES_256_XTS_TWK_OFFSET]),
slot_offset + (i * sizeof(u32)));
}
/* KEY TAG */
for (i = 0, limit = AES_GCM_TAG_SIZE / sizeof(u32); i < limit; i++) {
ufsp_writel(&ufs->handle, le32_to_cpu(fmp_key.words[i + HW_WRAPPED_KEY_TAG_OFFSET]),
tag_offset + (i * sizeof(u32)));
}
memzero_explicit(&fmp_key, key->size - secret_size);
/* Unwrap command */
kw_control = (1<<slot);
ufsp_writel(&ufs->handle, kw_control, FMP_KW_CONTROL);
/* Keyslot should be valid for crypto IO */
do {
kw_keyvalid = ufsp_readl(&ufs->handle, FMP_KW_KEYVALID);
if (!(kw_keyvalid & (0x1 << slot))) {
dev_warn(hba->dev, "Key slot #%d is not valid yet\n", slot);
udelay(2);
count++;
continue;
}
else {
break;
}
} while (count < MAX_RETRY_COUNT);
if (!(kw_keyvalid & (0x1 << slot))) {
dev_err(hba->dev, "Key slot #%d is not valid\n", slot);
kw_tagabort = ufsp_readl(&ufs->handle, FMP_KW_TAGABORT);
kw_pbkabort = ufsp_readl(&ufs->handle, FMP_KW_PBKABORT);
dev_err(hba->dev, "tag abort = 0x%x, pbk abort = 0x%x\n", kw_tagabort, kw_pbkabort);
// set to clear
if (kw_tagabort)
ufsp_writel(&ufs->handle, 1 << slot, FMP_KW_TAGABORT);
if (kw_pbkabort)
ufsp_writel(&ufs->handle, 1, FMP_KW_PBKABORT);
kw_tagabort = ufsp_readl(&ufs->handle, FMP_KW_TAGABORT);
kw_pbkabort = ufsp_readl(&ufs->handle, FMP_KW_PBKABORT);
dev_err(hba->dev, "set to clear tag abort = 0x%x, pbk abort = 0x%x\n", kw_tagabort, kw_pbkabort);
return -EINVAL;
}
else {
dev_info(hba->dev, "%s Key valid = 0x%x\n", __func__, kw_keyvalid);
}
/* To Do
* This is from ufshcd-crypto. Should check if this is needed.
*/
// ufshcd_release(hba);
/* To Do
* This should be fixed to return by goto label in error cases if ufshcd control is needed.
*/
return 0;
}
static int exynos_ufs_fmp_keyslot_evict(struct blk_keyslot_manager *ksm,
const struct blk_crypto_key *key,
unsigned int slot)
{
/* Nothing To Do */
return 0;
}
static int exynos_ufs_fmp_derive_raw_secret(struct blk_keyslot_manager *ksm,
const u8 *wrapped_key,
unsigned int wrapped_key_size,
u8 *secret, unsigned int secret_size)
{
struct ufs_hba *hba = container_of(ksm, struct ufs_hba, ksm);
unsigned long smc_ret = 0;
int ret = 0;
u8 *fmp_key;
dma_addr_t fmp_key_pa;
u8 *secret_key;
dma_addr_t secret_key_pa;
/* Allocate key buffer as non-cacheable */
fmp_key = dmam_alloc_coherent(&dev_fmp, wrapped_key_size, &fmp_key_pa, GFP_KERNEL);
if (!fmp_key) {
dev_err(hba->dev, "Fail to allocate memory for fmp_key\n");
ret = -ENOMEM;
goto out;
}
/* Allocate secret buffer as non-cacheable */
secret_key = dmam_alloc_coherent(&dev_fmp, secret_size, &secret_key_pa, GFP_KERNEL);
if (!secret_key) {
dev_err(hba->dev, "Fail to allocate memory for secret_key\n");
ret = -ENOMEM;
goto free_fmp_key;
}
memcpy(fmp_key, wrapped_key, wrapped_key_size);
smc_ret = exynos_smc(SMC_CMD_FMP_SECRET, fmp_key_pa, secret_key_pa, secret_size);
if (smc_ret) {
dev_err(hba->dev, "SMC_CMD_FMP_SECRET failed: %ld\n", smc_ret);
ret = -EINVAL;
goto free_secret_key;
}
memcpy(secret, secret_key, secret_size);
free_secret_key:
memzero_explicit(secret_key, secret_size);
dmam_free_coherent(&dev_fmp, secret_size, secret_key, secret_key_pa);
free_fmp_key:
memzero_explicit(fmp_key, wrapped_key_size);
dmam_free_coherent(&dev_fmp, wrapped_key_size, fmp_key, fmp_key_pa);
out:
return ret;
}
static const struct blk_ksm_ll_ops exynos_ufs_fmp_ksm_ll_ops = {
.keyslot_program = exynos_ufs_fmp_keyslot_program,
.keyslot_evict = exynos_ufs_fmp_keyslot_evict,
.derive_raw_secret = exynos_ufs_fmp_derive_raw_secret,
};
void key_program_slot0(struct ufs_hba *hba)
{
struct exynos_ufs *ufs = to_exynos_ufs(hba);
unsigned long ret;
size_t i, limit;
u32 kw_keyvalid;
u32 kw_control;
u32 count = 0;
int slot = 0;
u32 *fmp_key;
u32 *tag;
/* Key program in keyslot #0 */
u32 slot_offset = FMP_KW_SECUREKEY;
u32 tag_offset = FMP_KW_TAG;
/* The Galois/Counter Mode of Operation (GCM) Test Case #15 */
/* P: plaine text = {
0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55
};
*/
/* C: cipher text */
u8 c[AES_256_XTS_KEY_SIZE] = {
0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad
};
/* T: tag */
u8 t[AES_GCM_TAG_SIZE] = {
0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd,
0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c
};
fmp_key = (u32 *)c;
tag = (u32 *)t;
/* Set key, tag, iv, perboot keky */
ret = exynos_smc(SMC_CMD_FMP_KW_SYSREG_DUMMY, 0, 0, 0);
if (ret) {
dev_err(hba->dev, "SMC_CMD_FMP_KW_SYSREG_DUMMY failed: %ld\n", ret);
return;
}
/* Hw wrapped key */
/* Swap File key and Tweak key */
for (i = 0, limit = AES_256_XTS_KEY_SIZE / (sizeof(u32) * 2); i < limit; i++) {
ufsp_writel(&ufs->handle, le32_to_cpu(*(fmp_key + i)),
slot_offset + (i + AES_256_XTS_TWK_OFFSET) * sizeof(u32));
ufsp_writel(&ufs->handle, le32_to_cpu(*(fmp_key + i + AES_256_XTS_TWK_OFFSET)),
slot_offset + (i * sizeof(u32)));
}
/* tag */
for (i = 0, limit = AES_GCM_TAG_SIZE / sizeof(u32); i < limit; i++) {
ufsp_writel(&ufs->handle, le32_to_cpu(*(tag + i)),
tag_offset + (i * sizeof(u32)));
}
/* Unwrap command */
kw_control = (1<<slot);
ufsp_writel(&ufs->handle, kw_control, FMP_KW_CONTROL);
/* Keyslot #0 should be valid for normal IO */
do {
kw_keyvalid = ufsp_readl(&ufs->handle, FMP_KW_KEYVALID);
if (!(kw_keyvalid & (0x1 << slot))) {
dev_warn(hba->dev, "Key slot #%d is not valid yet\n", slot);
udelay(2);
count++;
continue;
}
else {
break;
}
} while (count < MAX_RETRY_COUNT);
if (!(kw_keyvalid & (0x1 << slot)))
dev_err(hba->dev, "Key slot #%d is not valid\n", slot);
else
dev_info(hba->dev, "%s Key valid = 0x%x\n", __func__, kw_keyvalid);
}
void exynos_ufs_fmp_init(struct ufs_hba *hba)
{
unsigned long ret;
int err;
unsigned int kw_indataswap;
int num_keyslots = NUM_KEYSLOTS;
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct exynos_fmp *fmp;
#ifndef CONFIG_EXYNOS_FMP_FIPS
dev_info(hba->dev, "Exynos FMP Version: %s\n", FMP_DRV_VERSION);
#endif
dev_info(hba->dev, "HW_KEYS_IN_CUSTOM_KEYSLOT\n");
#ifdef CONFIG_EXYNOS_FIPS_SIMULATOR
fmp_ufs_handle = (struct fmp_handle *)&ufs->handle;
FIPS_keyslot_num = -1;
#endif
ufs->fmp = devm_kzalloc(ufs->dev, sizeof(struct exynos_fmp), GFP_KERNEL);
if (ufs->fmp == NULL) {
dev_warn(hba->dev, "failed to alloc ufs->fmp\n");
goto disable_nofree;
}
fmp = (struct exynos_fmp *)ufs->fmp;
ret = exynos_smc(SMC_CMD_SMU, SMU_INIT, FMP_EMBEDDED, 0);
if (ret)
dev_warn(hba->dev, "SMC_CMD_SMU(SMU_INIT) failed: %ld\n", ret);
ret = exynos_smc(SMC_CMD_FMP_SECURITY, 0, FMP_EMBEDDED, CFG_DESCTYPE_0);
if (ret) {
dev_warn(hba->dev, "SMC_CMD_FMP_SECURITY failed on init: %ld\n", ret);
goto disable;
}
ret = exynos_smc(SMC_CMD_FMP_KW_MODE, 0, FMP_EMBEDDED, SECUREKEY_MODE | UNWRAP_EN | WAP0_NS);
if (ret) {
dev_warn(hba->dev, "SMC_CMD_FMP_KW_MODE failed on init: %ld\n", ret);
goto disable;
}
/* Advertise crypto support to ufshcd-core. */
hba->caps |= UFSHCD_CAP_CRYPTO;
/* Advertise crypto quirks to ufshcd-core. */
hba->quirks |= UFSHCD_QUIRK_FMP_MODE_SPECIFIC |
UFSHCD_QUIRK_FMP_SOC_SPECIFIC;
dev_info(hba->dev, "Exynos FMP quirks: 0x%x\n", hba->quirks);
/*
* Set data swap mode
* secure key/tag swap options are for swapping key at writing key in slot
* Ohter options are for key unwrap and as same for all secure key mode
*/
kw_indataswap = 0;
kw_indataswap |= (SECURE_FILEKEY_WORDSWAP | SECURE_TWEAKKEY_WORDSWAP |
SECURE_FILEKEY_BYTESWAP | SECURE_TWEAKKEY_BYTESWAP |
PBK_WORDSWAP | IV_WORDSWAP | TAG_WORDSWAP |
PBK_BYTESWAP | IV_BYTESWAP | TAG_BYTESWAP);
ret = exynos_smc(SMC_CMD_FMP_KW_INDATASWAP, 0, FMP_EMBEDDED, kw_indataswap);
if (ret) {
dev_err(hba->dev, "SMC_CMD_FMP_KW_INDATASWAP failed: %ld\n", ret);
goto disable;
}
exynos_ufs_fmp_populate_dt(ufs->dev, fmp);
dev_info(ufs->dev, "ufs->fmp valid_check = %d\n", fmp->valid_check);
/* Key program slot #0 to make it valid for non crypto IO. */
if (fmp->valid_check == 1) {
num_keyslots -= 1;
key_program_slot0(hba);
}
/* Write Perboot key, IV and Valid bit setting */
ret = exynos_smc(SMC_CMD_FMP_KW_SYSREG, 0, 0, 0);
if (ret) {
dev_err(hba->dev, "SMC_CMD_FMP_KW_SYSREG failed: %ld\n", ret);
goto disable;
}
err = blk_ksm_init(&hba->ksm, num_keyslots);
if (err) {
dev_warn(hba->dev, "blk_ksm_init failed: %d\n", err);
goto disable;
}
hba->ksm.ksm_ll_ops = exynos_ufs_fmp_ksm_ll_ops;
hba->ksm.max_dun_bytes_supported = 8;
hba->ksm.features = BLK_CRYPTO_FEATURE_WRAPPED_KEYS;
hba->ksm.dev = hba->dev;
hba->ksm.crypto_modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] =
FMP_DATA_UNIT_SIZE;
device_initialize(&dev_fmp);
err = dma_coerce_mask_and_coherent(&dev_fmp, DMA_BIT_MASK(36));
if (err) {
dev_warn(hba->dev, "No suitable DMA available: %d\n", err);
goto disable;
}
register_trace_android_vh_ufs_prepare_command(exynos_ufs_fmp_prepare_command, NULL);
return;
disable:
devm_kfree(ufs->dev, ufs->fmp);
disable_nofree:
dev_warn(hba->dev, "Disabling inline encryption support\n");
hba->caps &= ~UFSHCD_CAP_CRYPTO;
}
#endif /* CONFIG_HW_KEYS_IN_CUSTOM_KEYSLOT */
#ifdef CONFIG_KEYS_IN_CUSTOM_KEYSLOT
/*
* Program a key into a FMP custom keyslot, or evict a keyslot.
*/
static int exynos_ufs_fmp_keyslot_program(struct blk_keyslot_manager *ksm,
const struct blk_crypto_key *key,
unsigned int keyslot)
{
struct ufs_hba *hba = container_of(ksm, struct ufs_hba, ksm);
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct exynos_fmp *fmp = (struct exynos_fmp *)ufs->fmp;
unsigned int num_keyslots = NUM_KEYSLOTS;
unsigned int slot = keyslot;
#ifdef CONFIG_EXYNOS_FMP_FIPS
int ret;
struct exynos_fmp_key_info fmp_ki;
#else
size_t i, limit;
u32 count = 0;
u32 kw_keyvalid;
u32 slot_offset;
const u8 *enckey, *twkey;
union {
u8 bytes[AES_256_XTS_KEY_SIZE];
u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
} fmp_key;
#endif
if(fmp->valid_check == 1) {
slot += 1;
num_keyslots -= 1;
}
/* To Do
* This is from ufshcd-crypto. Should check if this is needed.
*/
// ufshcd_hold(hba, false);
/* Only AES-256-XTS is supported */
if (key->crypto_cfg.crypto_mode != BLK_ENCRYPTION_MODE_AES_256_XTS ||
key->size != AES_256_XTS_KEY_SIZE) {
dev_err(hba->dev,
"Unhandled crypto capability; crypto_mode=%d, key_size=%d\n",
key->crypto_cfg.crypto_mode, key->size);
return -EINVAL;
}
dev_info(hba->dev, "%s slot = %d/%d\n",__func__, slot, num_keyslots);
#ifdef CONFIG_EXYNOS_FMP_FIPS
fmp_ki.raw = key->raw;
fmp_ki.size = key->size;
fmp_ki.slot = slot;
ret = exynos_fmp_setkey(&fmp_ki, (struct fmp_handle *)&ufs->handle);
if (ret) {
dev_err(hba->dev, "%s: Fail to set FMP key in keyslot (ret: %d)\n", __func__, ret);
return ret;
}
#else
/* In XTS mode, the blk_crypto_key's size is already doubled */
memcpy(fmp_key.bytes, key->raw, key->size);
enckey = key->raw;
twkey = enckey + AES_KEYSIZE_256;
slot_offset = FMP_KW_SECUREKEY + slot*FMP_KW_SECUREKEY_OFFSET;
/* Reject weak AES-XTS keys */
if (!crypto_memneq(enckey, twkey, AES_KEYSIZE_256)) {
dev_err(hba->dev, "Can't use weak AES-XTS key\n");
return -EINVAL;
}
/* Key program in keyslot */
/* Swap File key and Tweak key */
for (i = 0, limit = AES_256_XTS_KEY_SIZE / (sizeof(u32) * 2); i < limit; i++) {
ufsp_writel(&ufs->handle, le32_to_cpu(fmp_key.words[i]),
slot_offset + ((i + AES_256_XTS_TWK_OFFSET) * sizeof(u32)));
ufsp_writel(&ufs->handle, le32_to_cpu(fmp_key.words[i + AES_256_XTS_TWK_OFFSET]),
slot_offset + (i * sizeof(u32)));
}
/* Zeroise the key */
memzero_explicit(&fmp_key, key->size);
/* Keyslot should be valid for crypto IO */
do {
kw_keyvalid = ufsp_readl(&ufs->handle, FMP_KW_KEYVALID);
if (!(kw_keyvalid & (0x1 << slot))) {
dev_warn(hba->dev, "Key slot #%d is not valid yet\n", slot);
udelay(2);
count++;
continue;
} else {
break;
}
} while (count < MAX_RETRY_COUNT);
if (!(kw_keyvalid & (0x1 << slot))) {
dev_err(hba->dev, "Key slot #%d is not valid\n", slot);
return -EINVAL;
}
dev_info(hba->dev, "%s Key valid = 0x%x\n", __func__, kw_keyvalid);
#endif
/* To Do
* This is from ufshcd-crypto. Should check if this is needed.
*/
// ufshcd_release(hba);
/* To Do
* This should be fixed to return by goto label in error cases if ufshcd control is needed.
*/
return 0;
}
static int exynos_ufs_fmp_keyslot_evict(struct blk_keyslot_manager *ksm,
const struct blk_crypto_key *key,
unsigned int slot)
{
/* Nothing To Do */
return 0;
}
static const struct blk_ksm_ll_ops exynos_ufs_fmp_ksm_ll_ops = {
.keyslot_program = exynos_ufs_fmp_keyslot_program,
.keyslot_evict = exynos_ufs_fmp_keyslot_evict,
};
void key_program_slot0(struct ufs_hba *hba)
{
struct exynos_ufs *ufs = to_exynos_ufs(hba);
size_t i, limit;
u32 kw_keyvalid;
u32 count = 0;
int slot = 0;
u32 fmp_key = 0;
/* Key program in keyslot #0 */
u32 slot_offset = FMP_KW_SECUREKEY;
/* Swap File key and Tweak key */
for (i = 0, limit = AES_256_XTS_KEY_SIZE / sizeof(u32); i < limit; i++)
ufsp_writel(&ufs->handle, le32_to_cpu(fmp_key), slot_offset + (i * sizeof(fmp_key)));
/* Keyslot #0 should be valid for normal IO */
do {
kw_keyvalid = ufsp_readl(&ufs->handle, FMP_KW_KEYVALID);
if (!(kw_keyvalid & 0x1)) {
dev_warn(hba->dev, "Key slot #%d is not valid yet\n", slot);
udelay(2);
count++;
continue;
}
else {
break;
}
} while (count < MAX_RETRY_COUNT);
if (!(kw_keyvalid & (0x1 << slot)))
dev_err(hba->dev, "Key slot #%d is not valid\n", slot);
else
dev_info(hba->dev, "%s Key valid = 0x%x\n", __func__, kw_keyvalid);
}
void exynos_ufs_fmp_init(struct ufs_hba *hba)
{
unsigned long ret;
int err;
unsigned int kw_indataswap;
int num_keyslots = NUM_KEYSLOTS;
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct exynos_fmp *fmp;
#ifndef CONFIG_EXYNOS_FMP_FIPS
dev_info(hba->dev, "Exynos FMP Version: %s\n", FMP_DRV_VERSION);
#endif
dev_info(hba->dev, "KEYS_IN_CUSTOM_KEYSLOT\n");
#ifdef CONFIG_EXYNOS_FIPS_SIMULATOR
fmp_ufs_handle = (struct fmp_handle *)&ufs->handle;
FIPS_keyslot_num = -1;
#endif
ufs->fmp = devm_kzalloc(ufs->dev, sizeof(struct exynos_fmp), GFP_KERNEL);
if (ufs->fmp == NULL) {
dev_warn(hba->dev, "failed to alloc ufs->fmp\n");
goto disable_nofree;
}
fmp = (struct exynos_fmp *)ufs->fmp;
ret = exynos_smc(SMC_CMD_SMU, SMU_INIT, FMP_EMBEDDED, 0);
if (ret)
dev_warn(hba->dev, "SMC_CMD_SMU(SMU_INIT) failed: %ld\n", ret);
ret = exynos_smc(SMC_CMD_FMP_SECURITY, 0, FMP_EMBEDDED, CFG_DESCTYPE_0);
if (ret) {
dev_warn(hba->dev, "SMC_CMD_FMP_SECURITY failed on init: %ld\n", ret);
goto disable;
}
ret = exynos_smc(SMC_CMD_FMP_KW_MODE, 0, FMP_EMBEDDED, SECUREKEY_MODE | UNWRAP_BYPASS);
if (ret) {
dev_warn(hba->dev, "SMC_CMD_FMP_KW_MODE failed on init: %ld\n", ret);
goto disable;
}
/* Advertise crypto support to ufshcd-core. */
hba->caps |= UFSHCD_CAP_CRYPTO;
/* Advertise crypto quirks to ufshcd-core. */
hba->quirks |= UFSHCD_QUIRK_FMP_MODE_SPECIFIC |
UFSHCD_QUIRK_FMP_SOC_SPECIFIC;
dev_info(hba->dev, "Exynos FMP quirks: 0x%x\n", hba->quirks);
/*
* Set data swap mode
* secure key swap option is for swapping key at writing key in slot
* Ohter options doensn't affect bypass mode but for setting as same for all secure key mode
*/
kw_indataswap = 0;
kw_indataswap |= (SECURE_FILEKEY_WORDSWAP | SECURE_TWEAKKEY_WORDSWAP |
SECURE_FILEKEY_BYTESWAP | SECURE_TWEAKKEY_BYTESWAP |
PBK_WORDSWAP | IV_WORDSWAP | TAG_WORDSWAP |
PBK_BYTESWAP | IV_BYTESWAP | TAG_BYTESWAP);
ret = exynos_smc(SMC_CMD_FMP_KW_INDATASWAP, 0, FMP_EMBEDDED, kw_indataswap);
if (ret) {
dev_err(hba->dev, "SMC_CMD_FMP_KW_INDATASWAP failed: %ld\n", ret);
goto disable;
}
exynos_ufs_fmp_populate_dt(ufs->dev, fmp);
dev_info(ufs->dev, "ufs->fmp valid_check = %d\n", fmp->valid_check);
/* Key program slot #0 to make it valid for non crypto IO. */
if (fmp->valid_check == 1) {
num_keyslots -= 1;
key_program_slot0(hba);
}
/* Advertise crypto capabilities to the block layer. */
err = blk_ksm_init(&hba->ksm, num_keyslots);
if (err) {
dev_warn(hba->dev, "blk_ksm_init failed: %d\n", err);
goto disable;
}
hba->ksm.ksm_ll_ops = exynos_ufs_fmp_ksm_ll_ops;
hba->ksm.max_dun_bytes_supported = 8;
hba->ksm.features |= BLK_CRYPTO_FEATURE_STANDARD_KEYS;
hba->ksm.dev = hba->dev;
hba->ksm.crypto_modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] =
FMP_DATA_UNIT_SIZE;
register_trace_android_vh_ufs_prepare_command(exynos_ufs_fmp_prepare_command, NULL);
return;
disable:
devm_kfree(ufs->dev, ufs->fmp);
disable_nofree:
dev_warn(hba->dev, "Disabling inline encryption support\n");
hba->caps &= ~UFSHCD_CAP_CRYPTO;
}
#endif
void exynos_ufs_fmp_resume(struct ufs_hba *hba)
{
#ifndef CONFIG_KEYS_IN_PRDT
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct exynos_fmp *fmp = (struct exynos_fmp*)ufs->fmp;
#endif
unsigned long ret;
/* Restore all fmp registers on init - Security, Kwmode, kwindataswap */
ret = exynos_smc(SMC_CMD_FMP_SMU_RESUME, 0, FMP_EMBEDDED, 0);
if (ret)
dev_err(hba->dev, "SMC_CMD_FMP_SMU_RESUME failed on resume: %ld\n", ret);
/* Key program slot #0 to make it valid for non crypto IO. */
#ifndef CONFIG_KEYS_IN_PRDT
if (fmp->valid_check == 1)
key_program_slot0(hba);
#endif
#ifdef CONFIG_HW_KEYS_IN_CUSTOM_KEYSLOT
/* Restore Perboot key, IV and Valid bit setting */
ret = exynos_smc(SMC_CMD_FMP_KW_SYSREG, 0, 0, 0);
if (ret) {
dev_err(hba->dev, "SMC_CMD_FMP_KW_SYSREG failed: %ld\n", ret);
return;
}
#endif
}
void exynos_ufs_fmp_dump_info(struct ufs_hba *hba)
{
int i = 0;
dev_err(hba->dev, ": --------------------------------------------------- \n");
dev_err(hba->dev, ": \t\tFMP REGs\n");
dev_err(hba->dev, ": --------------------------------------------------- \n");
for( i = 0; i < FMP_REGS; i++) {
ufs_fmp_log_sfr[i].val = exynos_smc(SMC_CMD_FMP_SMU_DUMP, 0, FMP_EMBEDDED, ufs_fmp_log_sfr[i].offset);
dev_err(hba->dev, ": %-30s\t0x%-012x\t0x%-014x\n",
ufs_fmp_log_sfr[i].name, ufs_fmp_log_sfr[i].offset, ufs_fmp_log_sfr[i].val);
}
}