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

894 lines
22 KiB
C
Executable file

/*
* Exynos FMP driver
*
* Copyright (C) 2015 Samsung Electronics Co., Ltd.
* Authors: Boojin Kim <boojin.kim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <linux/crypto.h>
#include <linux/dma-mapping.h>
#include <soc/samsung/exynos-smc.h>
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <crypto/fmp.h>
#include "fmp_fips_main.h"
#include "fmp_test.h"
#include "fmp_fips_info.h"
#ifndef CONFIG_KEYS_IN_PRDT
#include "ufs-vs-mmio.h"
#endif
#include <linux/genhd.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
#define WORD_SIZE 4
#define FMP_IV_MAX_IDX (FMP_IV_SIZE_16 / WORD_SIZE)
#define byte2word(b0, b1, b2, b3) \
(((unsigned int)(b0) << 24) | \
((unsigned int)(b1) << 16) | \
((unsigned int)(b2) << 8) | (b3))
#define get_word(x, c) byte2word(((unsigned char *)(x) + 4 * (c))[0], \
((unsigned char *)(x) + 4 * (c))[1], \
((unsigned char *)(x) + 4 * (c))[2], \
((unsigned char *)(x) + 4 * (c))[3])
/* Legacy Operation */
#define FKL BIT(26)
#define DKL BIT(27)
#define SET_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v)
#define SET_FAS(d, v) \
((d)->des3 = ((d)->des3 & 0xcfffffff) | v << 28)
#define SET_DAS(d, v) \
((d)->des3 = ((d)->des3 & 0x3fffffff) | v << 30)
#define GET_FAS(d) ((d)->des3 & 0x30000000)
#define GET_DAS(d) ((d)->des3 & 0xc0000000)
#define GET_LENGTH(d) \
((d)->des3 & 0x3ffffff)
static struct device *fmp_dev;
struct exynos_fmp *get_fmp(void)
{
return dev_get_drvdata(fmp_dev);
}
EXPORT_SYMBOL(get_fmp);
static inline void dump_ci(struct fmp_crypto_info *c)
{
if (c) {
pr_info
("%s: algo:%d enc:%d key_size:%d\n",
__func__, c->algo_mode, c->enc_mode,c->key_size);
if (c->enc_mode == EXYNOS_FMP_FILE_ENC)
print_hex_dump(KERN_CONT, "key:",
DUMP_PREFIX_OFFSET, 16, 1, c->key,
sizeof(c->key), false);
}
}
static inline void dump_table(struct fmp_table_setting *table)
{
print_hex_dump(KERN_CONT, "dump:", DUMP_PREFIX_OFFSET, 16, 1,
table, sizeof(struct fmp_table_setting), false);
}
static inline int is_supported_ivsize(u32 ivlen)
{
if (ivlen && (ivlen <= FMP_IV_SIZE_16))
return TRUE;
else
return FALSE;
}
static inline int check_aes_xts_size(struct fmp_table_setting *table,
bool cmdq_enabled)
{
int size;
if (cmdq_enabled)
size = GET_CMDQ_LENGTH(table);
else
size = GET_LENGTH(table);
return (size > MAX_AES_XTS_TRANSFER_SIZE) ? size : 0;
}
/* check for fips that no allow same keys */
static inline int check_aes_xts_key(char *key,
enum fmp_crypto_key_size key_size)
{
char *enckey, *twkey;
enckey = key;
twkey = key + key_size;
return (memcmp(enckey, twkey, key_size)) ? 0 : -1;
}
int fmplib_set_algo_mode(struct fmp_table_setting *table,
struct fmp_crypto_info *crypto, bool cmdq_enabled)
{
int ret;
enum fmp_crypto_fips_algo_mode algo_mode = crypto->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK;
if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) {
ret = check_aes_xts_size(table, cmdq_enabled);
if (ret) {
pr_err("%s: Fail FMP XTS due to invalid size(%d), cmdq:%d\n",
__func__, ret, cmdq_enabled);
return -EINVAL;
}
}
switch (crypto->enc_mode) {
case EXYNOS_FMP_FILE_ENC:
if (cmdq_enabled)
SET_CMDQ_FAS(table, algo_mode);
else
SET_FAS(table, algo_mode);
break;
default:
pr_err("%s: Invalid fmp enc mode %d\n", __func__,
crypto->enc_mode);
return -EINVAL;
}
return 0;
}
#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)));
}
static int fmplib_set_file_key(struct fmp_table_setting *table,
struct fmp_crypto_info *crypto)
{
enum fmp_crypto_fips_algo_mode algo_mode = crypto->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK;
enum fmp_crypto_key_size key_size = crypto->fmp_key_size;
char *key = crypto->key;
int idx, max;
if (!key || (crypto->enc_mode != EXYNOS_FMP_FILE_ENC) ||
((key_size != EXYNOS_FMP_KEY_SIZE_16) &&
(key_size != EXYNOS_FMP_KEY_SIZE_32))) {
pr_err("%s: Invalid key_size:%d enc_mode:%d\n",
__func__, key_size, crypto->enc_mode);
return -EINVAL;
}
if ((algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS)
&& check_aes_xts_key(key, key_size)) {
pr_err("%s: Fail FMP XTS due to the same enc and twkey\n",
__func__);
return -EINVAL;
}
if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_CBC) {
max = key_size / WORD_SIZE;
for (idx = 0; idx < max; idx++)
*(&table->file_enckey0 + idx) =
get_word(key, max - (idx + 1));
} else if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) {
key_size *= 2;
max = key_size / WORD_SIZE;
for (idx = 0; idx < (max / 2); idx++)
*(&table->file_enckey0 + idx) =
get_word(key, (max / 2) - (idx + 1));
for (idx = 0; idx < (max / 2); idx++)
*(&table->file_twkey0 + idx) =
get_word(key, max - (idx + 1));
}
return 0;
}
#else
static int fmplib_set_file_key(struct fmp_handle *handle, struct fmp_crypto_info *ci)
{
int ret = 0;
enum fmp_crypto_key_size key_size = ci->fmp_key_size;
enum fmp_crypto_fips_algo_mode algo_mode = ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK;
struct exynos_fmp_key_info fmp_key_info;
if (IS_ERR_OR_NULL(ci->key) || (ci->enc_mode != EXYNOS_FMP_FILE_ENC) ||
((key_size != EXYNOS_FMP_KEY_SIZE_32))) {
pr_err("%s: Invalid key_size:%d enc_mode:%d\n",
__func__, key_size, ci->enc_mode);
return -EINVAL;
}
if ((algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS)
&& check_aes_xts_key(ci->key, key_size)) {
pr_err("%s: Fail FMP XTS due to the same enc and twkey\n",
__func__);
return -EINVAL;
}
fmp_key_info.raw = ci->key;
fmp_key_info.size = ci->fmp_key_size * 2;
fmp_key_info.slot = EXYNOS_FMP_FIPS_KEYSLOT;
ret = exynos_fmp_setkey(&fmp_key_info, handle);
if (ret) {
pr_err("%s: Fail to set FMP key in keyslot (ret: %d)\n", __func__, ret);
return ret;
}
return ret;
}
unsigned long exynos_fmp_set_kw_mode(uint64_t kw_mode)
{
unsigned long ret;
ret = exynos_smc(SMC_CMD_FMP_KW_MODE, 0, FMP_EMBEDDED, kw_mode);
if (ret) {
pr_err("%s: SMC_CMD_FMP_KW_MODE failed to set kw_mode: %ld\n", __func__, ret);
return ret;
}
return 0;
}
unsigned long exynos_fmp_get_kw_mode(dma_addr_t kw_mode_addr)
{
unsigned long ret;
ret = exynos_smc(SMC_CMD_FMP_GET_KW_MODE, 0, FMP_EMBEDDED, kw_mode_addr);
if (ret) {
pr_err("%s: SMC_CMD_FMP_GET_KW_MODE failed to set kw_mode: %ld\n", __func__, ret);
return ret;
}
return 0;
}
#endif /* CONFIG_KEYS_IN_PRDT */
#ifdef CONFIG_KEYS_IN_PRDT
static int fmplib_set_key_size(struct fmp_table_setting *table,
struct fmp_crypto_info *crypto, bool cmdq_enabled)
{
enum fmp_crypto_key_size key_size;
key_size = crypto->fmp_key_size;
if ((key_size != EXYNOS_FMP_KEY_SIZE_16) &&
(key_size != EXYNOS_FMP_KEY_SIZE_32)) {
pr_err("%s: Invalid keysize %d\n", __func__, key_size);
return -EINVAL;
}
switch (crypto->enc_mode) {
case EXYNOS_FMP_FILE_ENC:
if (cmdq_enabled)
SET_CMDQ_KEYLEN(table,
(key_size ==
FMP_KEY_SIZE_32) ? FKL_CMDQ : 0);
else
SET_KEYLEN(table,
(key_size == FMP_KEY_SIZE_32) ? FKL : 0);
break;
default:
pr_err("%s: Invalid fmp enc mode %d\n", __func__,
crypto->enc_mode);
return -EINVAL;
}
return 0;
}
static int fmplib_set_iv(struct fmp_table_setting *table,
struct fmp_crypto_info *crypto, u8 *iv)
{
int idx;
switch (crypto->enc_mode) {
case EXYNOS_FMP_FILE_ENC:
for (idx = 0; idx < FMP_IV_MAX_IDX; idx++)
*(&table->file_iv0 + idx) =
get_word(iv, FMP_IV_MAX_IDX - (idx + 1));
break;
default:
pr_err("%s: Invalid fmp enc mode %d\n", __func__,
crypto->enc_mode);
return -EINVAL;
}
return 0;
}
int exynos_fmp_crypt(struct exynos_fmp_crypt_info *fmp_ci, struct fmp_table_setting *table)
{
struct exynos_fmp *fmp = get_fmp();
struct fmp_crypto_info *ci;
struct fmp_sg_entry *ent = (struct fmp_sg_entry *)table;
u8 iv[FMP_IV_SIZE_16];
u64 ret = 0;
size_t j, limit;
if (!fmp) {
pr_err("%s: invalid fmp\n", __func__);
return -EINVAL;
}
if (!table) {
pr_err("%s: invalid table setting\n", __func__);
return -EINVAL;
}
if (get_fmp_fips_state())
return -EINVAL;
if (fmp_ci->fips) {
/* check fips test data */
if (!fmp->test_data) {
dev_err(fmp->dev, "%s: no test_data for test mode\n", __func__);
return -EINVAL;
}
ci = &fmp->test_data->ci;
if (!ci || !(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST)) {
dev_err(fmp->dev, "%s: Invalid fmp crypto_info for test mode\n", __func__);
return -EINVAL;
}
if (!(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK)) {
dev_err(fmp->dev, "%s: no test_data for algo mode\n", __func__);
return -EINVAL;
}
ret = fmplib_set_algo_mode(table, ci, CMDQ_DISABLED);
if (ret) {
dev_err(fmp->dev, "%s: Fail to set FMP encryption mode\n", __func__);
ret = -EINVAL;
goto out;
}
/* set key into table */
switch (ci->enc_mode) {
case EXYNOS_FMP_FILE_ENC:
ret = fmplib_set_file_key(table, ci);
if (ret) {
dev_err(fmp->dev, "%s: Fail to set FMP key\n", __func__);
ret = -EINVAL;
goto out;
}
break;
default:
dev_err(fmp->dev, "%s: Invalid fmp enc mode %d\n", __func__, ci->enc_mode);
ret = -EINVAL;
goto out;
}
/* set key size into table */
ret = fmplib_set_key_size(table, ci, CMDQ_DISABLED);
if (ret) {
dev_err(fmp->dev, "%s: Fail to set FMP key size\n", __func__);
goto out;
}
/* use test manager's iv instead of host driver's iv */
dev_dbg(fmp->dev, "%s: fips crypt: ivsize:%d, key_size: %d, %d\n",
__func__, sizeof(fmp->test_data->iv), ci->key_size, ci->fmp_key_size);
if (!is_supported_ivsize(sizeof(fmp->test_data->iv))) {
dev_err(fmp->dev, "%s: invalid (mode:%d ivsize:%d)\n",
__func__, ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK,
sizeof(fmp->test_data->iv));
ret = -EINVAL;
goto out;
}
/* set iv as intended size */
memset(iv, 0, FMP_IV_SIZE_16);
memcpy(iv, fmp->test_data->iv, sizeof(fmp->test_data->iv));
ret = fmplib_set_iv(table, ci, iv);
if (ret) {
dev_err(fmp->dev, "%s: Fail to set FMP IV\n", __func__);
ret = -EINVAL;
goto out;
}
} else {
if (!crypto_memneq(fmp_ci->enckey, fmp_ci->twkey, AES_KEYSIZE_256)) {
dev_err(fmp->dev, "Can't use weak AES-XTS key\n");
ret = -EKEYREJECTED;
goto out;
}
SET_FAS(ent, EXYNOS_FMP_ALGO_MODE_AES_XTS);
SET_KEYLEN(ent, FKL);
/* set the file/tweak key in table */
for (j = 0, limit = AES_KEYSIZE_256 / sizeof(u32); j < limit; j++) {
ent->file_enckey[j] = fmp_key_word(fmp_ci->enckey, j);
ent->file_twkey[j] = fmp_key_word(fmp_ci->twkey, j);
}
/* Set the IV. */
ent->file_iv[0] = cpu_to_be32(upper_32_bits(fmp_ci->dun_hi));
ent->file_iv[1] = cpu_to_be32(lower_32_bits(fmp_ci->dun_hi));
ent->file_iv[2] = cpu_to_be32(upper_32_bits(fmp_ci->dun_lo));
ent->file_iv[3] = cpu_to_be32(lower_32_bits(fmp_ci->dun_lo));
}
out:
if (ret) {
dump_ci(ci);
dump_table(table);
}
return ret;
}
EXPORT_SYMBOL(exynos_fmp_crypt);
#else
int exynos_fmp_setkey(struct exynos_fmp_key_info *fmp_ki, struct fmp_handle *handle)
{
int ret = 0;
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;
/* Key length check. FMP only support 256b Key for AES-XTS */
if (fmp_ki->size != AES_256_XTS_KEY_SIZE) {
pr_err("%s: Does not support %d length of AES-XTS key\n", __func__, fmp_ki->size);
return -EINVAL;
}
if (get_fmp_fips_state())
return -EINVAL;
/* In XTS mode, the blk_crypto_key's size is already doubled */
memcpy(fmp_key.bytes, fmp_ki->raw, fmp_ki->size);
enckey = fmp_ki->raw;
twkey = enckey + AES_KEYSIZE_256;
slot_offset = FMP_KW_SECUREKEY + (fmp_ki->slot * FMP_KW_SECUREKEY_OFFSET);
/* Reject weak AES-XTS keys */
if (!crypto_memneq(enckey, twkey, AES_KEYSIZE_256)) {
pr_err("%s: Can't use weak AES-XTS key\n", __func__);
return -EKEYREJECTED;
}
/* Key program in keyslot */
/* Swap File key and Tweak key */
for (i = 0, limit = fmp_ki->size / (sizeof(u32) * 2); i < limit; i++) {
ufsp_writel((struct ufs_vs_handle *)handle, le32_to_cpu(fmp_key.words[i]),
slot_offset + ((i + AES_256_XTS_TWK_OFFSET) * sizeof(u32)));
ufsp_writel((struct ufs_vs_handle *)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, fmp_ki->size);
/* Keyslot should be valid for crypto IO */
do {
kw_keyvalid = ufsp_readl((struct ufs_vs_handle *)handle, FMP_KW_KEYVALID);
if (!(kw_keyvalid & (0x1 << (fmp_ki->slot)))) {
pr_warn("%s: Key slot #%d is not valid yet\n", __func__, fmp_ki->slot);
udelay(2);
count++;
continue;
} else {
break;
}
} while (count < MAX_RETRY_COUNT);
if (!(kw_keyvalid & (0x1 << fmp_ki->slot))) {
pr_err("%s: Key slot #%d is not valid\n", __func__, fmp_ki->slot);
return -EINVAL;
}
pr_debug("%s: Key valid = 0x%x\n", __func__, kw_keyvalid);
return ret;
}
EXPORT_SYMBOL(exynos_fmp_setkey);
int exynos_fmp_crypt(struct exynos_fmp_crypt_info *fmp_ci, struct fmp_handle *handle)
{
struct exynos_fmp *fmp = get_fmp();
struct fmp_crypto_info *ci;
int ret = 0;
if (!fmp) {
pr_err("%s: invalid fmp\n", __func__);
return -EINVAL;
}
if (!fmp_ci || !handle) {
dev_err(fmp->dev, "%s: Invalid fmp_crypt_info, fmp_handle parameter\n", __func__);
return -EINVAL;
}
if (get_fmp_fips_state())
return -EINVAL;
if (fmp_ci->fips) {
/* check fips test data */
if (!fmp->test_data) {
dev_err(fmp->dev, "%s: no test_data for test mode\n", __func__);
return -EINVAL;
}
ci = &fmp->test_data->ci;
if (!ci || !(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST)) {
dev_err(fmp->dev, "%s: Invalid fmp crypto_info for test mode\n", __func__);
return -EINVAL;
}
if (!(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK)) {
dev_err(fmp->dev, "%s: no test_data for algo mode\n", __func__);
return -EINVAL;
}
switch (ci->enc_mode) {
case EXYNOS_FMP_FILE_ENC:
ret = fmplib_set_file_key(handle, ci);
if (ret) {
dev_err(fmp->dev, "%s: Fail to set FMP key\n", __func__);
ret = -EINVAL;
goto out;
}
break;
default:
dev_err(fmp->dev, "%s: Invalid fmp enc mode %d\n", __func__, ci->enc_mode);
ret = -EINVAL;
goto out;
}
if (fmp->dun_swap == 1) {
fmp_ci->data_unit_num = cpu_to_be64(fmp->test_data->DataUnitSeqNumber);
fmp_ci->crypto_key_slot = EXYNOS_FMP_FIPS_KEYSLOT;
} else {
fmp_ci->data_unit_num = cpu_to_le64(fmp->test_data->DataUnitSeqNumber);
fmp_ci->crypto_key_slot = EXYNOS_FMP_FIPS_KEYSLOT;
}
}
out:
return ret;
}
EXPORT_SYMBOL(exynos_fmp_crypt);
#endif /* CONFIG_KEYS_IN_PRDT */
#ifdef CONFIG_KEYS_IN_PRDT
static inline void fmplib_clear_file_key(struct fmp_table_setting *table)
{
memset(&table->file_iv0, 0, sizeof(__le32) * 24);
}
#else
static void fmplib_clear_file_key(struct ufs_vs_handle *handle, int slot)
{
size_t i, limit;
int count = 0;
u32 kw_keyvalid;
u32 slot_offset = FMP_KW_SECUREKEY + (slot * FMP_KW_SECUREKEY_OFFSET);
union {
u8 bytes[AES_256_XTS_KEY_SIZE];
u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
} fmp_key;
if (slot > EXYNOS_FMP_FIPS_KEYSLOT)
return;
/* Make Zeroised key */
memzero_explicit(&fmp_key, AES_256_XTS_KEY_SIZE);
for (i = 0, limit = AES_256_XTS_KEY_SIZE / (sizeof(u32) * 2); i < limit; i++) {
ufsp_writel(handle, le32_to_cpu(fmp_key.words[i]),
slot_offset + ((i + AES_256_XTS_TWK_OFFSET) * sizeof(u32)));
ufsp_writel(handle, le32_to_cpu(fmp_key.words[i + AES_256_XTS_TWK_OFFSET]),
slot_offset + (i * sizeof(u32)));
}
/* Keyslot should be valid for crypto IO */
do {
kw_keyvalid = ufsp_readl(handle, FMP_KW_KEYVALID);
if (!(kw_keyvalid & (0x1 << slot))) {
pr_warn("%s: Key slot #%d is not zeroised yet - 0x%x\n",
__func__, slot, kw_keyvalid);
udelay(2);
count++;
continue;
} else {
break;
}
} while (count < MAX_RETRY_COUNT);
if (!(kw_keyvalid & (0x1 << slot))) {
pr_err("%s: Key slot #%d is not zeroised\n", __func__, slot);
return;
}
pr_debug("%s: Key valid = 0x%x\n", __func__, kw_keyvalid);
}
#endif
#ifndef CONFIG_KEYS_IN_PRDT
int exynos_fmp_clear(struct fmp_handle *handle, int slot)
{
struct exynos_fmp *fmp = get_fmp();
if (slot > EXYNOS_FMP_FIPS_KEYSLOT) {
dev_err(fmp->dev, "%s: unable to clear %d keyslot. Keyslot num exceeded\n",
__func__, slot);
return -EINVAL;
}
if (!handle) {
dev_err(fmp->dev, "%s: vacant ufs handle\n", __func__);
return -EINVAL;
}
fmplib_clear_file_key((struct ufs_vs_handle *)handle, slot);
return 0;
}
EXPORT_SYMBOL(exynos_fmp_clear);
#endif /* CONFIG_KEYS_IN_PRDT */
static void fmplib_bypass(void *desc, bool cmdq_enabled)
{
if (cmdq_enabled) {
SET_CMDQ_FAS((struct fmp_table_setting *)desc, 0);
SET_CMDQ_DAS((struct fmp_table_setting *)desc, 0);
} else {
SET_FAS((struct fmp_table_setting *)desc, 0);
SET_DAS((struct fmp_table_setting *)desc, 0);
}
}
static bool fmp_check_fips(struct bio *bio, struct exynos_fmp *fmp)
{
struct page *page;
struct buffer_head *bh;
bool find = false;
struct fmp_crypto_info *ci;
if (bio->bi_io_vec) {
page = bio->bi_io_vec[0].bv_page;
if (page && !PageAnon(page) && page_has_buffers(page)) {
bh = page_buffers(page);
if (bh != fmp->bh) {
dev_err(fmp->dev, "%s: invalid fips bh\n", __func__);
return false;
}
if (bh && ((void *)bh->b_private == (void *)fmp))
find = true;
}
}
if (find) {
fmp->fips_fin++;
ci = &fmp->test_data->ci;
dev_dbg(fmp->dev, "%s: find fips run(%d) with algo:%d, enc:%d, key_size:%d\n",
__func__, fmp->fips_run, ci->algo_mode, ci->enc_mode, ci->key_size);
if (ci->algo_mode == (EXYNOS_FMP_ALGO_MODE_TEST | EXYNOS_FMP_BYPASS_MODE)) {
dev_dbg(fmp->dev, "%s: find fips for bypass mode\n", __func__);
return 0;
}
}
return find;
}
static bool fmp_check_fips_clean(struct bio *bio, struct exynos_fmp *fmp)
{
bool find = false;
struct page *page;
struct buffer_head *bh;
struct fmp_crypto_info *ci;
if (bio->bi_io_vec) {
page = bio->bi_io_vec[0].bv_page;
if (page && !PageAnon(page) && page_has_buffers(page)) {
bh = page_buffers(page);
if (bh != fmp->bh) {
dev_err(fmp->dev, "%s: invalid fips bh\n", __func__);
return false;
}
if (bh && ((void *)bh->b_private == (void *)fmp))
find = true;
}
}
if (find) {
fmp->fips_fin--;
ci = &fmp->test_data->ci;
dev_dbg(fmp->dev, "%s: find fips run(%d) fin(%d)with algo:%d, enc:%d, key_size:%d\n",
__func__, fmp->fips_run, fmp->fips_fin, ci->algo_mode, ci->enc_mode, ci->key_size);
if (ci->algo_mode == (EXYNOS_FMP_ALGO_MODE_TEST | EXYNOS_FMP_BYPASS_MODE)) {
dev_dbg(fmp->dev, "%s: find fips for bypass mode\n", __func__);
return false;
}
}
return find;
}
bool is_fmp_fips_op(struct bio *bio)
{
struct exynos_fmp *fmp = get_fmp();
if (!fmp->fips_run)
return false;
if (!fmp->result.overall && fmp_check_fips(bio, fmp)) {
dev_dbg(fmp->dev, "%s: find fips\n", __func__);
return true;
}
return false;
}
EXPORT_SYMBOL(is_fmp_fips_op);
bool is_fmp_fips_clean(struct bio *bio)
{
struct exynos_fmp *fmp = get_fmp();
if (fmp->fips_fin && fmp_check_fips_clean(bio, fmp)) {
dev_dbg(fmp->dev, "%s: find fips\n", __func__);
return true;
}
return false;
}
EXPORT_SYMBOL(is_fmp_fips_clean);
int get_fmp_fips_state(void)
{
if (unlikely(in_fmp_fips_err())) {
#if defined(CONFIG_NODE_FOR_SELFTEST_FAIL)
pr_err("%s: Fail to work fmp config due to fips in error.\n", __func__);
#else
panic("%s: Fail to work fmp config due to fips in error\n", __func__);
#endif
return -EINVAL;
}
return 0;
}
int exynos_fmp_bypass(struct fmp_table_setting *table, u32 prdt_cnt, unsigned long prdt_off)
{
u32 i;
void *prd = table;
for (i = 0; i < prdt_cnt; i++) {
fmplib_bypass(prd, 0);
prd = (void *)prd + prdt_off;
}
return 0;
}
EXPORT_SYMBOL(exynos_fmp_bypass);
#define CFG_DESCTYPE_3 0x3
static void *exynos_fmp_init(struct platform_device *pdev)
{
struct exynos_fmp *fmp;
struct device_node *np;
u8 dun_swap;
int ret = 0;
if (!pdev) {
pr_err("%s: Invalid platform_device.\n", __func__);
goto err;
}
fmp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_fmp), GFP_KERNEL);
if (!fmp)
goto err;
fmp->dev = &pdev->dev;
if (!fmp->dev) {
pr_err("%s: Invalid device.\n", __func__);
goto err_dev;
}
atomic_set(&fmp->fips_start, 0);
ret = exynos_fmp_fips_register(fmp);
if (ret) {
dev_err(fmp->dev, "%s: Fail to exynos_fmp_fips_register. ret(0x%x)",
__func__, ret);
goto err_dev;
}
/* Check fmp status for featuring */
np = fmp->dev->of_node;
ret = of_property_read_u8(np, "dun-swap", &dun_swap);
if (ret) {
dev_info(fmp->dev, "read dun_swap failed = 0x%x, set to 0\n", __func__, ret);
dun_swap = 0;
}
fmp->dun_swap = dun_swap;
dev_info(fmp->dev, "Exynos FMP dun swap = %d\n", fmp->dun_swap);
fmp->fips_run = 0;
dev_info(fmp->dev, "Exynos FMP driver is initialized\n");
dev_info(fmp->dev, "Exynos FMP driver Version: %s\n", FMP_DRV_VERSION);
return fmp;
err_dev:
devm_kfree(&pdev->dev, fmp);
err:
return NULL;
}
void exynos_fmp_exit(struct platform_device *pdev)
{
struct exynos_fmp *fmp = dev_get_drvdata(&pdev->dev);
exynos_fmp_fips_deregister(fmp);
devm_kfree(&pdev->dev, fmp);
}
static int exynos_fmp_probe(struct platform_device *pdev)
{
struct exynos_fmp *fmp_ctx = exynos_fmp_init(pdev);
int ret;
if (!fmp_ctx) {
dev_err(&pdev->dev,
"%s: Fail to get fmp_ctx\n", __func__);
return -EINVAL;
}
fmp_dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, fmp_ctx);
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
if (ret) {
dev_warn(&pdev->dev,
"%s: No suitable DMA available (%d)\n", __func__, ret);
}
return 0;
}
static int exynos_fmp_remove(struct platform_device *pdev)
{
exynos_fmp_exit(pdev);
dev_info(&pdev->dev, "%s: complete remove fmp driver\n", __func__);
return 0;
}
static const struct of_device_id exynos_fmp_match[] = {
{ .compatible = "samsung,exynos-fmp" },
{},
};
static struct platform_driver exynos_fmp_driver = {
.driver = {
.name = "exynos-fmp",
.owner = THIS_MODULE,
.pm = NULL,
.of_match_table = exynos_fmp_match,
},
.probe = exynos_fmp_probe,
.remove = exynos_fmp_remove,
};
module_platform_driver(exynos_fmp_driver);
MODULE_DESCRIPTION("FMP driver");
MODULE_AUTHOR("Boojin Kim <boojin.kim@samsung.com>");
MODULE_LICENSE("GPL");