blob: b1f6337ec2364dc03d718da6ef282ef0473590c2 [file] [log] [blame]
/*
* Exynos FMP cipher driver
*
* Copyright (C) 2016 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/slab.h>
#include <linux/buffer_head.h>
#include <crypto/fmp.h>
#include "fmp_fips_info.h"
int fmp_cipher_init(struct exynos_fmp *fmp)
{
struct device *dev;
struct fmp_crypto_setting *crypto;
if (!fmp || !fmp->fips_data)
return -EINVAL;
dev = fmp->dev;
crypto = kzalloc(sizeof(struct fmp_crypto_setting), GFP_KERNEL);
if (!crypto) {
dev_err(dev, "%s: Fail to alloc for fmp crypto.\n", __func__);
return -ENOMEM;
}
fmp->fips_data->crypto = crypto;
return 0;
}
int fmp_cipher_set_key(struct exynos_fmp *fmp, uint32_t mode,
uint8_t *key, uint32_t key_len)
{
int ret = 0;
struct fmp_crypto_setting *crypto;
struct device *dev = fmp->dev;
if (!fmp->fips_data) {
dev_err(dev, "%s: fmp test data context is not allocated\n",
__func__);
ret = -ENOMEM;
goto err;
}
crypto = fmp->fips_data->crypto;
memset(crypto->key, 0, FMP_MAX_KEY_SIZE);
if (mode == CBC_MODE) {
crypto->algo_mode = EXYNOS_FMP_ALGO_MODE_AES_CBC;
switch (key_len) {
case FMP_KEY_SIZE_16:
crypto->key_size = EXYNOS_FMP_KEY_SIZE_16;
break;
case FMP_KEY_SIZE_32:
crypto->key_size = EXYNOS_FMP_KEY_SIZE_32;
break;
default:
dev_err(dev, "%s: Invalid FMP CBC key size(%d)\n",
__func__, key_len);
ret = -EINVAL;
goto err;
}
memcpy(crypto->key, key, key_len);
} else if (mode == XTS_MODE) {
crypto->algo_mode = EXYNOS_FMP_ALGO_MODE_AES_XTS;
switch (key_len >> 1) {
case FMP_KEY_SIZE_16:
crypto->key_size = EXYNOS_FMP_KEY_SIZE_16;
break;
case FMP_KEY_SIZE_32:
crypto->key_size = EXYNOS_FMP_KEY_SIZE_32;
break;
default:
dev_err(dev, "%s: Invalid FMP XTS key size(%d)\n",
__func__, key_len);
ret = -EINVAL;
goto err;
}
memcpy(crypto->key, key, key_len);
} else if (mode == BYPASS_MODE) {
crypto->algo_mode = EXYNOS_FMP_BYPASS_MODE;
goto err;
} else {
dev_err(dev, "%s: Invalid FMP encryption mode(%d)\n",
__func__, mode);
ret = -EINVAL;
goto err;
}
err:
return ret;
}
int fmp_cipher_set_iv(struct exynos_fmp *fmp, uint32_t mode,
uint8_t *iv, uint32_t iv_len)
{
int ret = 0;
struct fmp_crypto_setting *crypto;
struct device *dev = fmp->dev;
if (!fmp->fips_data) {
dev_err(dev, "%s: fmp test data context is not allocated\n",
__func__);
ret = -ENOMEM;
goto err;
}
crypto = fmp->fips_data->crypto;
if (iv_len != FMP_IV_SIZE_16) {
dev_err(dev, "%s: Invalid FMP iv size(%d)\n", __func__, iv_len);
ret = -EINVAL;
goto err;
}
if (mode == CBC_MODE || mode == XTS_MODE)
memcpy(crypto->iv, iv, iv_len);
else if (mode == BYPASS_MODE)
memset(crypto->iv, 0, FMP_IV_SIZE_16);
else {
dev_err(dev, "%s: Invalid mode(%d)\n", __func__, mode);
ret = -EINVAL;
goto err;
}
err:
return ret;
}
int fmp_cipher_run(struct exynos_fmp *fmp, uint32_t mode, uint8_t *data,
uint32_t len, uint32_t write)
{
int ret = 0;
struct device *dev = fmp->dev;
static struct buffer_head *bh;
struct fmp_fips_data *fips_data = fmp->fips_data;
bh = __getblk(fips_data->bdev, fips_data->sector, FMP_BLK_SIZE);
if (!bh) {
dev_err(dev, "%s: Fail to get block from bdev\n", __func__);
return -ENODEV;
}
if (mode == CBC_MODE)
fips_data->crypto->algo_mode = EXYNOS_FMP_ALGO_MODE_AES_CBC;
else if (mode == XTS_MODE)
fips_data->crypto->algo_mode = EXYNOS_FMP_ALGO_MODE_AES_XTS;
else if (mode == BYPASS_MODE)
fips_data->crypto->algo_mode = EXYNOS_FMP_BYPASS_MODE;
else {
dev_err(dev, "%s: Invalid algo mode(%d)\n", __func__, mode);
return -EINVAL;
}
fmp->test_bh = bh;
get_bh(bh);
if (write == WRITE_MODE) {
memcpy(bh->b_data, data, len);
set_buffer_dirty(bh);
sync_dirty_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
dev_err(dev, "%s: IO error syncing for write mode\n",
__func__);
ret = -EIO;
goto out;
}
memset(bh->b_data, 0, FMP_BLK_SIZE);
} else {
lock_buffer(bh);
bh->b_end_io = end_buffer_read_sync;
submit_bh(READ_SYNC, bh);
wait_on_buffer(bh);
if (unlikely(!buffer_uptodate(bh))) {
ret = -EIO;
goto out;
}
memcpy(data, bh->b_data, len);
}
out:
fmp->test_bh = NULL;
put_bh(bh);
return ret;
}
void fmp_cipher_exit(struct exynos_fmp *fmp)
{
kfree(fmp->fips_data->crypto);
}