658 lines
15 KiB
C
Executable File
658 lines
15 KiB
C
Executable File
/*
|
|
* Exynos FMP driver
|
|
*
|
|
* Copyright (C) 2015 Samsung Electronics Co., Ltd.
|
|
*
|
|
* 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 <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 <linux/smc.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <crypto/fmp.h>
|
|
|
|
#include "fmp_test.h"
|
|
#include "fmp_fips_main.h"
|
|
#include "fmp_fips_info.h"
|
|
#include "fmp_fips_func_test.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])
|
|
|
|
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_set_fmp_disk_key(struct exynos_fmp *fmp)
|
|
{
|
|
return (fmp->status_disk_key == KEY_SET) ? TRUE : FALSE;
|
|
}
|
|
|
|
static inline int is_stored_fmp_disk_key(struct exynos_fmp *fmp)
|
|
{
|
|
return (fmp->status_disk_key == KEY_STORED) ? TRUE : 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_algo_mode algo_mode = crypto->algo_mode;
|
|
|
|
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)\n",
|
|
__func__, ret);
|
|
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;
|
|
case EXYNOS_FMP_DISK_ENC:
|
|
if (cmdq_enabled)
|
|
SET_CMDQ_DAS(table, algo_mode);
|
|
else
|
|
SET_DAS(table, algo_mode);
|
|
break;
|
|
default:
|
|
pr_err("%s: Invalid fmp enc mode %d\n", __func__,
|
|
crypto->enc_mode);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fmplib_set_file_key(struct fmp_table_setting *table,
|
|
struct fmp_crypto_info *crypto)
|
|
{
|
|
enum fmp_crypto_algo_mode algo_mode = crypto->algo_mode;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
case EXYNOS_FMP_DISK_ENC:
|
|
if (cmdq_enabled)
|
|
SET_CMDQ_KEYLEN(table,
|
|
(key_size ==
|
|
FMP_KEY_SIZE_32) ? DKL_CMDQ : 0);
|
|
else
|
|
SET_KEYLEN(table,
|
|
(key_size == FMP_KEY_SIZE_32) ? DKL : 0);
|
|
break;
|
|
default:
|
|
pr_err("%s: Invalid fmp enc mode %d\n", __func__,
|
|
crypto->enc_mode);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fmplib_set_disk_key(struct exynos_fmp *fmp, u8 *key, u32 key_size)
|
|
{
|
|
int ret;
|
|
|
|
/* TODO: only set for host0 */
|
|
__flush_dcache_area(key, (size_t) FMP_MAX_KEY_SIZE);
|
|
ret =
|
|
exynos_smc(SMC_CMD_FMP_DISK_KEY_STORED, 0, virt_to_phys(key),
|
|
key_size);
|
|
if (ret) {
|
|
pr_err("%s: Fail to set FMP disk key. ret = %d\n", __func__,
|
|
ret);
|
|
fmp->status_disk_key = KEY_ERROR;
|
|
return -EINVAL;
|
|
}
|
|
fmp->status_disk_key = KEY_STORED;
|
|
return 0;
|
|
}
|
|
|
|
static int fmplib_clear_disk_key(struct exynos_fmp *fmp)
|
|
{
|
|
int ret;
|
|
|
|
ret = exynos_smc(SMC_CMD_FMP_DISK_KEY_CLEAR, 0, 0, 0);
|
|
if (ret) {
|
|
pr_err("%s: Fail to clear FMP disk key. ret = %d\n",
|
|
__func__, ret);
|
|
fmp->status_disk_key = KEY_ERROR;
|
|
return -EPERM;
|
|
}
|
|
|
|
fmp->status_disk_key = KEY_CLEAR;
|
|
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;
|
|
case EXYNOS_FMP_DISK_ENC:
|
|
for (idx = 0; idx < FMP_IV_MAX_IDX; idx++)
|
|
*(&table->disk_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 fmp_crypto_info *ci, void *priv)
|
|
{
|
|
struct exynos_fmp *fmp = ci->ctx;
|
|
struct fmp_request *r = priv;
|
|
int ret = 0;
|
|
u8 iv[FMP_IV_SIZE_16];
|
|
|
|
if (!r || !fmp) {
|
|
pr_err("%s: invalid req or fmp\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
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
|
|
if (in_fmp_fips_init())
|
|
pr_err("%s: Fail to work fmp config due to fips in init.\n",
|
|
__func__);
|
|
else
|
|
panic("%s: Fail to work fmp config due to fips in error\n",
|
|
__func__);
|
|
#endif
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check test mode */
|
|
if (ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST) {
|
|
ci->algo_mode &= EXYNOS_FMP_ALGO_MODE_MASK;
|
|
if (!ci->algo_mode)
|
|
return 0;
|
|
|
|
if (!fmp->test_data) {
|
|
pr_err("%s: no test_data for test mode\n", __func__);
|
|
goto out;
|
|
}
|
|
/* use test manager's iv instead of host driver's iv */
|
|
r->iv = fmp->test_data->iv;
|
|
r->ivsize = sizeof(fmp->test_data->iv);
|
|
}
|
|
|
|
/* check crypto info & input param */
|
|
if (!ci->algo_mode || !is_supported_ivsize(r->ivsize) ||
|
|
!r->table || !r->iv) {
|
|
dev_err(fmp->dev,
|
|
"%s: invalid mode:%d ivsize:%d\n",
|
|
__func__, ci->algo_mode, r->ivsize);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* set algo & enc mode into table */
|
|
ret = fmplib_set_algo_mode(r->table, ci, r->cmdq_enabled);
|
|
if (ret) {
|
|
dev_err(fmp->dev, "%s: Fail to set FMP encryption mode\n",
|
|
__func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* set key size into table */
|
|
switch (ci->enc_mode) {
|
|
case EXYNOS_FMP_FILE_ENC:
|
|
ret = fmplib_set_file_key(r->table, ci);
|
|
if (ret) {
|
|
dev_err(fmp->dev, "%s: Fail to set FMP key\n",
|
|
__func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
break;
|
|
case EXYNOS_FMP_DISK_ENC:
|
|
if (is_stored_fmp_disk_key(fmp)) {
|
|
ret = exynos_smc(SMC_CMD_FMP_DISK_KEY_SET, 0, 0, 0);
|
|
if (ret) {
|
|
dev_err(fmp->dev,
|
|
"%s: Fail to set disk key\n", __func__);
|
|
goto out;
|
|
}
|
|
fmp->status_disk_key = KEY_SET;
|
|
} else if (!is_set_fmp_disk_key(fmp)) {
|
|
dev_err(fmp->dev,
|
|
"%s: Fail because disk key is clear\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(r->table, ci, r->cmdq_enabled);
|
|
if (ret) {
|
|
dev_err(fmp->dev, "%s: Fail to set FMP key size\n", __func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* set iv */
|
|
memset(iv, 0, FMP_IV_SIZE_16);
|
|
memcpy(iv, r->iv, r->ivsize);
|
|
ret = fmplib_set_iv(r->table, ci, iv);
|
|
if (ret) {
|
|
dev_err(fmp->dev, "%s: Fail to set FMP IV\n", __func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
out:
|
|
if (ret) {
|
|
dump_ci(ci);
|
|
if (r && r->table)
|
|
dump_table(r->table);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static inline void fmplib_clear_file_key(struct fmp_table_setting *table)
|
|
{
|
|
memset(&table->file_iv0, 0, sizeof(__le32) * 24);
|
|
}
|
|
|
|
int exynos_fmp_clear(struct fmp_crypto_info *ci, void *priv)
|
|
{
|
|
struct fmp_request *r = priv;
|
|
#ifdef CONFIG_EXYNOS_FMP_FIPS
|
|
struct exynos_fmp *fmp = ci->ctx;
|
|
struct exynos_fmp_fips_test_vops *test_vops = fmp->test_vops;
|
|
int ret;
|
|
#endif
|
|
|
|
if (!r) {
|
|
pr_err("%s: Invalid input\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!r->table) {
|
|
pr_err("%s: Invalid input table\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_EXYNOS_FMP_FIPS
|
|
if (test_vops) {
|
|
ret = test_vops->zeroization(r->table, "before");
|
|
if (ret)
|
|
dev_err(fmp->dev,
|
|
"%s: Fail to check zeroization(before)\n",
|
|
__func__);
|
|
}
|
|
#endif
|
|
fmplib_clear_file_key(r->table);
|
|
|
|
#ifdef CONFIG_EXYNOS_FMP_FIPS
|
|
if (test_vops) {
|
|
ret = test_vops->zeroization(r->table, "after");
|
|
if (ret)
|
|
dev_err(fmp->dev,
|
|
"%s: Fail to check zeroization(after)\n",
|
|
__func__);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int exynos_fmp_setkey(struct fmp_crypto_info *ci, u8 *in_key, u32 keylen,
|
|
bool persistent)
|
|
{
|
|
struct exynos_fmp *fmp = ci->ctx;
|
|
int ret = 0;
|
|
int keylen_org = keylen;
|
|
|
|
if (!fmp || !in_key) {
|
|
pr_err("%s: invalid input param\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set key_size & fmp_key_size */
|
|
if (ci->algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS)
|
|
keylen = keylen >> 1;
|
|
switch (keylen) {
|
|
case FMP_KEY_SIZE_16:
|
|
ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_16;
|
|
break;
|
|
case FMP_KEY_SIZE_32:
|
|
ci->fmp_key_size = EXYNOS_FMP_KEY_SIZE_32;
|
|
break;
|
|
default:
|
|
pr_err("%s: FMP doesn't support key size %d\n", __func__,
|
|
keylen);
|
|
return -ENOKEY;
|
|
}
|
|
ci->key_size = keylen_org;
|
|
|
|
/* set key */
|
|
if (persistent) {
|
|
ci->enc_mode = EXYNOS_FMP_DISK_ENC;
|
|
ret = fmplib_set_disk_key(fmp, in_key, ci->key_size);
|
|
if (ret)
|
|
pr_err("%s: Fail to set FMP disk key\n", __func__);
|
|
} else {
|
|
ci->enc_mode = EXYNOS_FMP_FILE_ENC;
|
|
memset(ci->key, 0, sizeof(ci->key));
|
|
memcpy(ci->key, in_key, ci->key_size);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int exynos_fmp_clearkey(struct fmp_crypto_info *ci)
|
|
{
|
|
struct exynos_fmp *fmp = ci->ctx;
|
|
int ret = 0;
|
|
|
|
if (!fmp) {
|
|
pr_err("%s: invalid input param\n", __func__);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (ci->enc_mode == EXYNOS_FMP_DISK_ENC) {
|
|
ret = fmplib_clear_disk_key(fmp);
|
|
if (ret)
|
|
pr_err("%s: fail to clear FMP disk key\n", __func__);
|
|
} else if (ci->enc_mode == EXYNOS_FMP_FILE_ENC) {
|
|
memset(ci->key, 0, sizeof(ci->key));
|
|
ci->key_size = 0;
|
|
} else {
|
|
pr_err("%s: invalid algo mode:%d\n", __func__, ci->enc_mode);
|
|
ret = -EINVAL;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int exynos_fmp_test_crypt(struct fmp_crypto_info *ci,
|
|
const uint8_t *iv, uint32_t ivlen, uint8_t *src,
|
|
uint8_t *dst, uint32_t len, bool enc, void *priv)
|
|
{
|
|
struct exynos_fmp *fmp = ci->ctx;
|
|
int ret = 0;
|
|
|
|
if (!fmp || !iv || !src || !dst) {
|
|
pr_err("%s: invalid input(fmp, iv, src, dst)\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* init fmp test to get test block */
|
|
fmp->test_data = fmp_test_init(fmp);
|
|
if (!fmp->test_data) {
|
|
dev_err(fmp->dev, "%s: fail to initialize fmp test.",
|
|
__func__);
|
|
goto err;
|
|
}
|
|
|
|
/* setiv */
|
|
if (iv && (ivlen <= FMP_IV_SIZE_16)) {
|
|
memset(fmp->test_data->iv, 0, FMP_IV_SIZE_16);
|
|
memcpy(fmp->test_data->iv, iv, ivlen);
|
|
} else {
|
|
dev_err(fmp->dev, "%s: fail to set fmp iv. ret(%d)",
|
|
__func__, ret);
|
|
goto err;
|
|
}
|
|
|
|
/* do crypt: priv: struct crypto_diskcipher */
|
|
ret = fmp_test_crypt(fmp, fmp->test_data,
|
|
src, dst, len, enc ? ENCRYPT : DECRYPT, priv, ci);
|
|
if (ret)
|
|
dev_err(fmp->dev, "%s: fail to run fmp test. ret(%d)",
|
|
__func__, ret);
|
|
|
|
err:
|
|
if (fmp->test_data)
|
|
fmp_test_exit(fmp->test_data);
|
|
return ret;
|
|
}
|
|
|
|
int exynos_fmp_smu_abort(int id)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (id == SMU_ID_MAX)
|
|
return 0;
|
|
|
|
ret = exynos_smc(SMC_CMD_SMU, SMU_ABORT, id, 0);
|
|
if (ret)
|
|
pr_err("%s: Fail smc call. ret(%d)\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define CFG_DESCTYPE_3 0x3
|
|
int exynos_fmp_sec_cfg(int fmp_id, int smu_id, bool init)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (fmp_id != SMU_ID_MAX) {
|
|
ret = exynos_smc(SMC_CMD_FMP_SECURITY, 0,
|
|
fmp_id, CFG_DESCTYPE_3);
|
|
if (ret)
|
|
pr_err("%s: Fail smc call for FMP_SECURITY. ret(%d)\n",
|
|
__func__, ret);
|
|
}
|
|
|
|
if (smu_id != SMU_ID_MAX) {
|
|
if (init)
|
|
ret = exynos_smc(SMC_CMD_SMU, SMU_INIT, smu_id, 0);
|
|
else
|
|
ret = exynos_smc(SMC_CMD_FMP_SMU_RESUME, 0, smu_id, 0);
|
|
if (ret)
|
|
pr_err("%s: Fail smc call cmd:%d. ret(%d)\n",
|
|
__func__, init, ret);
|
|
}
|
|
pr_info("%s: fmp:%d, smu:%d, init:%d\n", __func__, fmp_id, smu_id, init);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void *exynos_fmp_init(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct exynos_fmp *fmp;
|
|
|
|
if (!pdev) {
|
|
pr_err("%s: Invalid platform_device.\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
fmp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_fmp), GFP_KERNEL);
|
|
if (!fmp)
|
|
return NULL;
|
|
|
|
fmp->dev = &pdev->dev;
|
|
if (!fmp->dev) {
|
|
pr_err("%s: Invalid device.\n", __func__);
|
|
goto err_dev;
|
|
}
|
|
|
|
/* init disk key status */
|
|
fmp->status_disk_key = KEY_CLEAR;
|
|
|
|
dev_info(fmp->dev, "Exynos FMP Version: %s\n", FMP_DRV_VERSION);
|
|
|
|
#ifdef CONFIG_EXYNOS_FMP_FIPS
|
|
ret = exynos_fmp_func_test_KAT_case(pdev, fmp);
|
|
if (ret) {
|
|
dev_err(fmp->dev, "%s: Fail to test KAT case. ret(%d)",
|
|
__func__, ret);
|
|
goto err_dev;
|
|
}
|
|
#endif
|
|
|
|
/* init fips */
|
|
ret = exynos_fmp_fips_init(fmp);
|
|
if (ret) {
|
|
dev_err(fmp->dev, "%s: Fail to initialize fmp fips. ret(%d)",
|
|
__func__, ret);
|
|
exynos_fmp_fips_exit(fmp);
|
|
goto err_dev;
|
|
}
|
|
|
|
dev_info(fmp->dev, "Exynos FMP driver is initialized\n");
|
|
return fmp;
|
|
|
|
err_dev:
|
|
devm_kfree(&pdev->dev, fmp);
|
|
return NULL;
|
|
}
|
|
|
|
void exynos_fmp_exit(struct platform_device *pdev)
|
|
{
|
|
struct exynos_fmp *fmp = dev_get_drvdata(&pdev->dev);
|
|
|
|
exynos_fmp_fips_exit(fmp);
|
|
devm_kfree(&pdev->dev, fmp);
|
|
}
|