blob: 47fb163b28c2eea5f4c4e0678656716fa914ccbc [file] [log] [blame]
/*
* 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 <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/scatterlist.h>
#include <linux/smc.h>
#include <crypto/fmp.h>
#include "fmp_fips_main.h"
#ifdef CONFIG_EXYNOS_FMP_FIPS
#include "fmp_test.h"
#include "fmp_fips_info.h"
#include "fmp_fips_func_test.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])
static struct device *fmp_dev;
static inline struct exynos_fmp *get_fmp(void)
{
return dev_get_drvdata(fmp_dev);
}
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 & 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;
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 & 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;
}
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)
{
u64 ret;
/* TODO: only set for host0 */
// hack: mark on 5.4 __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)
{
u64 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;
}
static void fmp_dumpsfr(void)
{
u64 fmp_sec = exynos_smc(SMC_CMD_FMP_SMU_DUMP, 0, 0, 0x10);
u64 fmp_ver = exynos_smc(SMC_CMD_FMP_SMU_DUMP, 0, 0, 0x1c);
pr_info("%s: sec:%x, ver:%x\n", __func__, fmp_sec, fmp_ver);
}
int gnum3;
int exynos_fmp_crypt(struct fmp_crypto_info *fmp_ci, void *priv)
{
struct exynos_fmp *fmp = get_fmp();
struct fmp_request *r = priv;
struct fmp_crypto_info *ci = fmp_ci;
u64 ret = 0;
u8 iv[FMP_IV_SIZE_16];
bool fips = 0;
enum fmp_crypto_algo_mode algo_mode;
struct exynos_fmp_fips_test_vops *test_vops = NULL;
if (!r || !fmp) {
pr_err("%s: invalid req or fmp\n", __func__);
return -EINVAL;
}
#ifdef CONFIG_EXYNOS_FMP_FIPS
test_vops = fmp->test_vops;
#endif
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__);
return -EINVAL;
#else
if (test_vops)
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__);
return -EINVAL;
#endif
}
/* check fips test mode */
if (r->fips) {
if (!fmp->test_data) {
pr_err("%s: no test_data for test mode\n", __func__);
goto out;
}
ci = &fmp->test_data->ci;
if (!(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST)) {
pr_err("%s: no test_data for algo mode\n", __func__);
goto out;
}
}
algo_mode = ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK;
/* check test mode */
if (ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST) {
if (!algo_mode)
return 0;
/* use test manager's iv instead of host driver's iv */
r->iv = fmp->test_data->iv;
r->ivsize = sizeof(fmp->test_data->iv);
dev_dbg(fmp->dev, "%s: fips crypt: ivsize:%d, key_size: %d, %d\n",
__func__, r->ivsize, ci->key_size, ci->fmp_key_size);
fips = true;
}
#ifdef CONFIG_EXYNOS_FMP_FIPS
if (fips == false && fmp->result.overall == 0) {
#if defined(CONFIG_NODE_FOR_SELFTEST_FAIL)
pr_err("%s: Fail to work fmp config due to all fips test is not done.\n",
__func__);
#else
panic("%s: Fail to work fmp config due to all fips test is not done\n",
__func__);
#endif
return -EINVAL;
}
#endif
/* check crypto info & input param */
if (!algo_mode || !is_supported_ivsize(r->ivsize) ||
!r->table || !r->iv) {
dev_err(fmp->dev, "%s: invalid mode:%d ivsize:%d\n",
__func__, 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;
}
EXPORT_SYMBOL(exynos_fmp_crypt);
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 = get_fmp();
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;
}
EXPORT_SYMBOL(exynos_fmp_clear);
int exynos_fmp_setkey(struct fmp_crypto_info *ci, u8 *in_key, u32 keylen,
bool persistent)
{
struct exynos_fmp *fmp = get_fmp();
int ret = 0;
int keylen_org = keylen;
enum fmp_crypto_algo_mode algo_mode = ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK;
if (!fmp || !in_key) {
pr_err("%s: invalid input param\n", __func__);
return -EINVAL;
}
/* set key_size & fmp_key_size */
if (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;
}
EXPORT_SYMBOL(exynos_fmp_setkey);
int exynos_fmp_clearkey(struct fmp_crypto_info *ci)
{
struct exynos_fmp *fmp = get_fmp();
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_smu_abort(int id)
{
u64 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;
}
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);
}
}
#ifdef CONFIG_EXYNOS_FMP_FIPS
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 = (void *)page->private;
if (bh && ((void *)bh->b_private == (void *)fmp)) {
find = true;
if ((void *)page->private != (void *)fmp->bh) {
dev_err(fmp->dev, "%s: invalid fips bh\n", __func__);
return false;
}
}
}
}
if (find) {
fmp->fips_run--;
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 0;
}
}
return find;
}
#endif
int exynos_fmp_fips(struct bio *bio)
{
bool find = false;
#ifdef CONFIG_EXYNOS_FMP_FIPS
struct exynos_fmp *fmp = get_fmp();
struct page *page;
struct buffer_head *bh;
struct fmp_crypto_info *ci;
if (fmp->fips_fin) {
if (bio->bi_io_vec) {
page = bio->bi_io_vec[0].bv_page;
if (page && !PageAnon(page) && page_has_buffers(page)) {
bh = (void *)page->private;
if (bh && ((void *)bh->b_private == (void *)fmp)) {
find = true;
if ((void *)page->private != (void *)fmp->bh) {
dev_err(fmp->dev, "%s: invalid fips bh\n", __func__);
return false;
}
}
}
}
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 0;
}
}
}
#endif
return find;
}
EXPORT_SYMBOL(exynos_fmp_fips);
int exynos_fmp_bypass(struct fmp_request *req, struct bio *bio)
{
int i;
void *prd;
#ifdef CONFIG_EXYNOS_FMP_FIPS
struct exynos_fmp *fmp = get_fmp();
if (fmp->fips_run) {
if (fmp_check_fips(bio, fmp)) {
dev_dbg(fmp->dev, "%s: find fips\n", __func__);
return -EINVAL;
}
}
#endif
prd = req->table;
for (i = 0; i < req->prdt_cnt; i++) {
fmplib_bypass(prd, 0);
prd = (void *)prd + req->prdt_off;
}
return 0;
}
EXPORT_SYMBOL(exynos_fmp_bypass);
#define CFG_DESCTYPE_3 0x3
int exynos_fmp_sec_cfg(int fmp_id, int smu_id, bool init)
{
u64 ret = 0;
/* configure fmp */
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);
}
/* configure smu */
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 for SMU_INIT/RESUME. ret(%d)\n",
__func__, ret);
}
if (ret)
fmp_dumpsfr();
return ret;
}
EXPORT_SYMBOL(exynos_fmp_sec_cfg);
void *exynos_fmp_init(struct platform_device *pdev)
{
struct exynos_fmp *fmp;
#ifdef CONFIG_EXYNOS_FMP_FIPS
int ret = 0;
#endif
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;
}
/* 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
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;
}
ret = exynos_fmp_fips_integrity(fmp);
if (ret) {
dev_err(fmp->dev, "%s: Fail to exynos_fmp_fips_integrity. ret(0x%x)",
__func__, ret);
goto err_dev;
}
#endif
fmp_dev = &pdev->dev;
dev_info(fmp->dev, "Exynos FMP driver is initialized\n");
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);
#ifdef CONFIG_EXYNOS_FMP_FIPS
exynos_fmp_fips_deregister(fmp);
#endif
devm_kfree(&pdev->dev, fmp);
}