To compile this driver as a module, choose M here.
+config CRYPTO_DEV_FMP
+ bool "FMP diskcipher engine driver"
+ depends on CRYPTO_DISKCIPHER && EXYNOS_FMP
+ default y
+
+source "drivers/crypto/fmp/Kconfig"
+
endif # CRYPTO_HW
obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/
obj-$(CONFIG_CRYPTO_DEV_SAFEXCEL) += inside-secure/
obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/
+obj-$(CONFIG_CRYPTO_DEV_FMP) += exynos-diskcipher.o
+ifneq (,$(filter $(CONFIG_EXYNOS_SMU) $(CONFIG_EXYNOS_FMP),y))
+obj-y += fmp/
+endif
--- /dev/null
+/*
+ * Exynos FMP driver
+ *
+ * Copyright (C) 2018 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/slab.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <crypto/fmp.h>
+#include <crypto/diskcipher.h>
+
+static int fmp_crypt(struct crypto_tfm *tfm, void *priv)
+{
+ struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm);
+
+ return exynos_fmp_crypt(ci, priv);
+}
+
+static int fmp_clear(struct crypto_tfm *tfm, void *priv)
+{
+ struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm);
+
+ return exynos_fmp_clear(ci, priv);
+}
+
+static int fmp_setkey(struct crypto_tfm *tfm, const char *in_key, u32 keylen,
+ bool persistent)
+{
+ struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm);
+
+ return exynos_fmp_setkey(ci, (char *)in_key, keylen, persistent);
+}
+
+static int fmp_clearkey(struct crypto_tfm *tfm)
+{
+ struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm);
+
+ return exynos_fmp_clearkey(ci);
+}
+
+#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
+static int fmp_do_test_crypt(struct crypto_tfm *tfm,
+ struct diskcipher_test_request *req)
+{
+ struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm);
+ struct crypto_diskcipher *diskc = __crypto_diskcipher_cast(tfm);
+
+ if (!req) {
+ pr_err("%s: invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ return exynos_fmp_test_crypt(ci, req->iv, diskc->ivsize,
+ sg_virt(req->src), sg_virt(req->dst),
+ req->cryptlen, req->enc ? 1 : 0, diskc);
+}
+#endif
+
+static void *fmp_ctx;
+
+static inline void fmp_algo_init(struct crypto_tfm *tfm,
+ enum fmp_crypto_algo_mode algo)
+{
+ struct fmp_crypto_info *ci = crypto_tfm_ctx(tfm);
+ struct crypto_diskcipher *diskc = __crypto_diskcipher_cast(tfm);
+
+ /* This field's stongly aligned 'fmp_crypto_info->use_diskc' */
+ diskc->algo = (u32)algo;
+ diskc->ivsize = FMP_IV_SIZE_16;
+ ci->ctx = fmp_ctx;
+ ci->algo_mode = algo;
+}
+
+static int fmp_aes_xts_init(struct crypto_tfm *tfm)
+{
+ fmp_algo_init(tfm, EXYNOS_FMP_ALGO_MODE_AES_XTS);
+ return 0;
+}
+
+static int fmp_cbc_aes_init(struct crypto_tfm *tfm)
+{
+ fmp_algo_init(tfm, EXYNOS_FMP_ALGO_MODE_AES_CBC);
+ return 0;
+}
+
+static struct diskcipher_alg fmp_algs[] = {{
+ .base = {
+ .cra_name = "xts(aes)-disk",
+ .cra_driver_name = "xts(aes)-disk(fmp)",
+ .cra_priority = 200,
+ .cra_module = THIS_MODULE,
+ .cra_ctxsize = sizeof(struct fmp_crypto_info),
+ .cra_init = fmp_aes_xts_init,
+ }
+}, {
+ .base = {
+ .cra_name = "cbc(aes)-disk",
+ .cra_driver_name = "cbc(aes)-disk(fmp)",
+ .cra_priority = 200,
+ .cra_module = THIS_MODULE,
+ .cra_ctxsize = sizeof(struct fmp_crypto_info),
+ .cra_init = fmp_cbc_aes_init,
+ }
+} };
+
+static int exynos_fmp_probe(struct platform_device *pdev)
+{
+ struct diskcipher_alg *alg;
+ int ret;
+ int i;
+
+ fmp_ctx = exynos_fmp_init(pdev);
+ if (!fmp_ctx) {
+ dev_err(&pdev->dev,
+ "%s: Fail to register diskciphero\n", __func__);
+ return -EINVAL;
+ }
+ dev_set_drvdata(&pdev->dev, fmp_ctx);
+
+ for (i = 0; i < ARRAY_SIZE(fmp_algs); i++) {
+ alg = &fmp_algs[i];
+ alg->setkey = fmp_setkey;
+ alg->clearkey = fmp_clearkey;
+ alg->crypt = fmp_crypt;
+ alg->clear = fmp_clear;
+#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
+ alg->do_crypt = fmp_do_test_crypt;
+#endif
+ }
+ ret = crypto_register_diskciphers(fmp_algs, ARRAY_SIZE(fmp_algs));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Fail to register diskciphero. ret = %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+ dev_info(&pdev->dev, "Exynos FMP driver is registered to diskcipher\n");
+ return 0;
+}
+
+static int exynos_fmp_remove(struct platform_device *pdev)
+{
+ void *drv_data = dev_get_drvdata(&pdev->dev);
+
+ if (!drv_data) {
+ pr_err("%s: Fail to get drvdata\n", __func__);
+ return 0;
+ }
+ crypto_unregister_diskciphers(fmp_algs, ARRAY_SIZE(fmp_algs));
+ exynos_fmp_exit(drv_data);
+ 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,
+};
+
+static int __init fmp_init(void)
+{
+ return platform_driver_register(&exynos_fmp_driver);
+}
+late_initcall(fmp_init);
+
+static void __exit fmp_exit(void)
+{
+ platform_driver_unregister(&exynos_fmp_driver);
+}
+module_exit(fmp_exit);
+MODULE_DESCRIPTION("Exynos Spedific crypto algo driver");
--- /dev/null
+#
+# SMU/FMP controller drivers
+#
+
+config EXYNOS_SMU
+ tristate "Samsung EXYNOS SMU driver"
+ help
+ Say yes here to build support for SMU (Secure Manangement Unit)
+ to control access permission to storage device.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here
+
+config EXYNOS_FMP
+ tristate "Samsung EXYNOS FMP driver"
+ depends on CRYPTO_DISKCIPHER
+ help
+ Say yes here to build support for FMP (Flash Memory Protector)
+ to encrypt and decrypt userdata using inline H/W crypto module.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here
+
+config EXYNOS_FMP_FIPS
+ tristate "Samsung EXYNOS FMP Validation Driver for FIPS"
+ depends on EXYNOS_FMP
+ depends on !FUNCTION_TRACER
+ help
+ Say yes here to build support for FMP (Flash Memory Protector)
+ FIPS validation driver for integrity checking, self and functional test.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here
+
+choice
+ prompt "Option for self-test failure"
+ depends on EXYNOS_FMP_FIPS
+ default NODE_FOR_SELFTEST_FAIL
+
+config NODE_FOR_SELFTEST_FAIL
+ bool "Set fips fmp node when self-test fails"
+ depends on EXYNOS_FMP_FIPS
+ help
+ Choose here to select that fips fmp node was set to zero
+ when FMP self-test fails.
+
+config PANIC_FOR_SELFTEST_FAIL
+ bool "Panic when self-test fails"
+ depends on EXYNOS_FMP_FIPS
+ help
+ Choose here to select that kernel panic occurs when FMP self-test fails.
+
+endchoice
--- /dev/null
+# Exynos FMP/SMU makefile
+#ccflags-y := -DCONFIG_EXYNOS_FMP_FIPS_FUNC_TEST
+obj-$(CONFIG_EXYNOS_SMU) += smu.o
+obj-$(CONFIG_EXYNOS_FMP_FIPS) += first_file.o
+obj-$(CONFIG_EXYNOS_FMP) += fmp.o fmp_test.o
+CFLAGS_fmp_fips_selftest.o = -fno-merge-constants
+obj-$(CONFIG_EXYNOS_FMP_FIPS) += fmp_fips_main.o fmp_fips_fops.o fmp_fips_selftest.o \
+ fmp_fips_integrity.o hmac-sha256.o \
+ fmp_fips_cipher.o
+ifneq ($(filter y, $(CONFIG_EXYNOS_FMP) $(CONFIG_EXYNOS_FMP_FIPS)),)
+obj-y += sha256.o
+endif
+#obj-y += fmp_fips_func_test.o
+obj-$(CONFIG_EXYNOS_FMP_FIPS) += last_file.o
+obj-$(CONFIG_EXYNOS_FMP_FIPS) += fips_out.o
--- /dev/null
+#include <linux/kernel.h>
+#include <crypto/fmp.h>
+#include "fmp_fips_integrity.h"
+
+__attribute__ ((section(".rodata")))
+const __s8 builtime_fmp_hmac[FIPS_HMAC_SIZE] = {0};
+
+__attribute__ ((section(".rodata")))
+const struct first_last integrity_fmp_addrs[FIPS_FMP_ADDRS_SIZE] = {{0}, };
+
+__attribute__ ((section(".rodata")))
+const __u64 fmp_buildtime_address = 10;
--- /dev/null
+/*
+ * First file for Exynos FMP FIPS integrity check
+ *
+ * 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.
+ */
+
+__attribute__ ((section(".rodata"), unused))
+const unsigned char first_fmp_rodata = 0x10;
+
+__attribute__ ((section(".text"), unused))
+void first_fmp_text(void){}
+
+__attribute__ ((section(".init.text"), optimize("-O0"), unused))
+static void first_fmp_init(void){};
--- /dev/null
+/*
+ * 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 <crypto/fmp.h>
+#include <linux/scatterlist.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: crypto:%p algo:%d enc:%d key_size:%d key:%p\n",
+ __func__, c, c->algo_mode, c->enc_mode,
+ c->key_size, c->key);
+ 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_table:", 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);
+ ret = -EINVAL;
+ break;
+ }
+ 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 crypto:%p key:%p key_size:%d enc_mode:%d\n",
+ __func__, crypto, key, 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;
+
+ __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];
+ bool test_mode = 0;
+
+ if (!r || !fmp) {
+ pr_err("%s: invalid req:%p, fmp:%p\n", __func__, r, fmp);
+ return -EINVAL;
+ }
+
+ if (unlikely(in_fmp_fips_err())) {
+ pr_err("%s: Fail to work fmp config due to fips in error.\n",
+ __func__);
+ 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;
+ }
+ test_mode = 1;
+ /* 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 iv:%p ivsize:%d table:%p\n",
+ __func__, ci->algo_mode, r->iv, r->ivsize, r->table);
+ 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)) {
+ exynos_smc(SMC_CMD_FMP_DISK_KEY_SET, 0, 0, 0);
+ fmp->status_disk_key = KEY_SET;
+ } else if (!is_set_fmp_disk_key(fmp)) {
+ dev_err(fmp->dev,
+ "%s: Cannot configure FMP 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, char *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;
+}
+
+#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
+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:%p, iv:%p, s:%p, d:%p\n",
+ __func__, fmp, iv, src, dst);
+ return -EINVAL;
+ }
+
+ /* init fips 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;
+}
+#endif
+
+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:
+ kzfree(fmp);
+ return NULL;
+}
+
+void exynos_fmp_exit(struct exynos_fmp *fmp)
+{
+ exynos_fmp_fips_exit(fmp);
+ kzfree(fmp);
+}
--- /dev/null
+/*
+ * 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_set_key(struct fmp_test_data *fdata,
+ uint8_t *key, uint32_t key_len)
+{
+ int ret = 0;
+ struct fmp_crypto_info *ci;
+
+ if (!fdata)
+ return -EINVAL;
+
+ ci = &fdata->ci;
+ memset(ci->key, 0, FMP_MAX_KEY_SIZE);
+ ci->key_size = key_len;
+
+ if (ci->algo_mode == EXYNOS_FMP_ALGO_MODE_AES_CBC) {
+ switch (key_len) {
+ 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: Invalid FMP CBC key size(%d)\n",
+ __func__, key_len);
+ ret = -EINVAL;
+ goto err;
+ }
+ memcpy(ci->key, key, key_len);
+ } else if (ci->algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) {
+ switch (key_len >> 1) {
+ 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: Invalid FMP XTS key size(%d)\n",
+ __func__, key_len);
+ ret = -EINVAL;
+ goto err;
+ }
+ memcpy(ci->key, key, key_len);
+ } else {
+ pr_err("%s: Invalid FMP encryption mode(%d)\n",
+ __func__, ci->algo_mode);
+ ret = -EINVAL;
+ goto err;
+ }
+err:
+ return ret;
+}
+
+int fmp_cipher_set_iv(struct fmp_test_data *fdata,
+ uint8_t *iv, uint32_t iv_len)
+{
+ int ret = 0;
+ struct fmp_crypto_info *ci;
+
+ if (!fdata)
+ return -EINVAL;
+
+ ci = &fdata->ci;
+ if (iv_len != FMP_IV_SIZE_16) {
+ pr_err("%s: Invalid FMP iv size(%d)\n", __func__, iv_len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ memset(fdata->iv, 0, FMP_IV_SIZE_16);
+ memcpy(fdata->iv, iv, iv_len);
+
+err:
+ return ret;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _FMP_FIPS_CIPHER_H_
+#define _FMP_FIPS_CIPHER_H_
+int fmp_cipher_set_key(struct fmp_test_data *fdata,
+ uint8_t *key, uint32_t key_len);
+int fmp_cipher_set_iv(struct fmp_test_data *fdata,
+ uint8_t *iv, uint32_t iv_len);
+#endif
--- /dev/null
+/*
+ * Exynos FMP func test 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/completion.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+
+#include <crypto/fmp.h>
+#include <crypto/authenc.h>
+
+#include <asm/cacheflush.h>
+#include <linux/uaccess.h>
+#include <asm/memory.h>
+
+#include "fmp_fips_main.h"
+#include "fmp_fips_info.h"
+#include "fmp_fips_cipher.h"
+#include "sha256.h"
+#include "hmac-sha256.h"
+#include "fmp_test.h"
+
+#define INIT 0
+#define UPDATE 1
+#define FINAL 2
+
+/* ====== Compile-time config ====== */
+
+/*
+ * Default (pre-allocated) and maximum size of the job queue.
+ * These are free, pending and done items all together.
+ */
+#define DEF_COP_RINGSIZE 16
+#define MAX_COP_RINGSIZE 64
+
+static int fmp_fips_set_key(struct exynos_fmp *fmp, struct fmp_fips_info *info,
+ uint8_t *enckey, uint8_t *twkey, uint32_t key_len)
+{
+ int ret = 0;
+ uint8_t *key;
+
+ if (twkey) {
+ key = kzalloc(key_len * 2, GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+ memcpy(key, enckey, key_len);
+ memcpy(key + key_len, twkey, key_len);
+ key_len *= 2;
+ } else {
+ key = kzalloc(key_len, GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+ memcpy(key, enckey, key_len);
+ }
+
+ ret = fmp_cipher_set_key(info->data, key, key_len);
+ if (ret)
+ dev_err(fmp->dev, "%s: Fail to set key. ret(%d)\n",
+ __func__, ret);
+ kzfree(key);
+ return ret;
+}
+
+static int fmp_fips_cipher_init(struct fmp_fips_info *info,
+ struct cipher_data *out, const char *alg_name,
+ uint8_t *enckey, uint8_t *twkey, size_t key_len)
+{
+ int ret = 0;
+ struct exynos_fmp *fmp;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ memset(out, 0, sizeof(*out));
+
+ if (!strcmp(alg_name, "cbc(aes-fmp)"))
+ info->data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_CBC;
+ else if (!strcmp(alg_name, "xts(aes-fmp)"))
+ info->data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_XTS;
+ else {
+ dev_err(fmp->dev, "%s: Invalid mode\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to init fmp cipher\n", __func__);
+ goto err;
+ }
+
+ out->blocksize = 16;
+ out->ivsize = 16;
+ ret = fmp_fips_set_key(fmp, info, enckey, twkey, key_len);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to set fmp key\n", __func__);
+ goto err;
+ }
+
+ out->init = 1;
+err:
+ return ret;
+}
+
+static void fmp_fips_cipher_deinit(struct cipher_data *cdata)
+{
+ if (cdata->init)
+ cdata->init = 0;
+}
+
+static int fmp_fips_hash_init(struct fmp_fips_info *info, struct hash_data *hdata,
+ const char *alg_name,
+ int hmac_mode, void *mackey, size_t mackeylen)
+{
+ int ret = -ENOMSG;
+ struct exynos_fmp *fmp;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ hdata->init = 0;
+
+ switch (hmac_mode) {
+ case 0:
+ hdata->sha = kzalloc(sizeof(*hdata->sha), GFP_KERNEL);
+ if (!hdata->sha)
+ return -ENOMEM;
+
+ ret = sha256_init(hdata->sha);
+ break;
+ case 1:
+ hdata->hmac = kzalloc(sizeof(*hdata->hmac), GFP_KERNEL);
+ if (!hdata->hmac)
+ return -ENOMEM;
+
+ ret = hmac_sha256_init(hdata->hmac, mackey, mackeylen);
+ break;
+ default:
+ dev_err(fmp->dev, "Wrong mode\n");
+ return ret;
+ }
+
+ if (ret == 0)
+ hdata->init = 1;
+
+ return ret;
+}
+
+static void fmp_fips_hash_deinit(struct hash_data *hdata)
+{
+ if (hdata->hmac != NULL) {
+ hmac_sha256_ctx_cleanup(hdata->hmac);
+ kzfree(hdata->hmac);
+ return;
+ }
+
+ if (hdata->sha != NULL) {
+ memset(hdata->sha, 0x00, sizeof(*hdata->sha));
+ kzfree(hdata->sha);
+ return;
+ }
+}
+
+static ssize_t fmp_fips_hash_update(struct exynos_fmp *fmp,
+ struct hash_data *hdata,
+ struct scatterlist *sg, size_t len)
+{
+ int ret = -ENOMSG;
+ int8_t *buf;
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (len != sg_copy_to_buffer(sg, 1, buf, len)) {
+ dev_err(fmp->dev, "%s: sg_copy_buffer error copying\n", __func__);
+ goto exit;
+ }
+
+ if (hdata->sha != NULL)
+ ret = sha256_update(hdata->sha, buf, len);
+ else
+ ret = hmac_sha256_update(hdata->hmac, buf, len);
+
+exit:
+ kzfree(buf);
+ return ret;
+}
+
+static int fmp_fips_hash_final(struct exynos_fmp *fmp,
+ struct hash_data *hdata, void *output)
+{
+ int ret_crypto = 0; /* OK if zero */
+
+ if (hdata->sha != NULL)
+ ret_crypto = sha256_final(hdata->sha, output);
+ else
+ ret_crypto = hmac_sha256_final(hdata->hmac, output);
+
+ if (ret_crypto != 0)
+ return -ENOMSG;
+
+ hdata->digestsize = 32;
+ return 0;
+}
+
+static int fmp_n_crypt(struct fmp_fips_info *info, struct csession *ses_ptr,
+ struct crypt_op *cop,
+ struct scatterlist *src_sg, struct scatterlist *dst_sg,
+ uint32_t len)
+{
+ int ret;
+ struct exynos_fmp *fmp;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ if (cop->op == COP_ENCRYPT) {
+ if (ses_ptr->hdata.init != 0) {
+ ret = fmp_fips_hash_update(fmp, &ses_ptr->hdata,
+ src_sg, len);
+ if (unlikely(ret))
+ goto out_err;
+ }
+
+ if (ses_ptr->cdata.init != 0) {
+ if (info->data) {
+ ret = fmp_test_crypt(fmp, info->data,
+ sg_virt(src_sg), sg_virt(dst_sg), len,
+ ENCRYPT, NULL, &info->data->ci);
+ if (unlikely(ret))
+ goto out_err;
+ } else {
+ dev_err(fmp->dev,
+ "%s: no crypt for enc\n", __func__);
+ ret = -EINVAL;
+ }
+ }
+ } else {
+ if (ses_ptr->cdata.init != 0) {
+ if (info->data) {
+ ret = fmp_test_crypt(fmp, info->data,
+ sg_virt(src_sg), sg_virt(dst_sg), len,
+ DECRYPT, NULL, &info->data->ci);
+ if (unlikely(ret))
+ goto out_err;
+ } else {
+ dev_err(fmp->dev,
+ "%s: no crypt for dec\n", __func__);
+ ret = -EINVAL;
+ }
+ }
+
+ if (ses_ptr->hdata.init != 0) {
+ ret = fmp_fips_hash_update(fmp, &ses_ptr->hdata,
+ dst_sg, len);
+ if (unlikely(ret))
+ goto out_err;
+ }
+ }
+
+ return 0;
+out_err:
+ dev_err(fmp->dev, "%s: FMP crypt failure: %d\n", __func__, ret);
+ return ret;
+}
+
+static int __fmp_run_std(struct fmp_fips_info *info,
+ struct csession *ses_ptr, struct crypt_op *cop)
+{
+ char *data;
+ struct exynos_fmp *fmp;
+ char __user *src, *dst;
+ size_t nbytes, bufsize;
+ struct scatterlist sg;
+ int ret = 0;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ nbytes = cop->len;
+ data = (char *)__get_free_page(GFP_KERNEL);
+ if (unlikely(!data)) {
+ dev_err(fmp->dev, "Error getting free page.\n");
+ return -ENOMEM;
+ }
+
+ bufsize = (nbytes > PAGE_SIZE) ? PAGE_SIZE : nbytes;
+
+ src = cop->src;
+ dst = cop->dst;
+
+ while (nbytes > 0) {
+ size_t current_len = nbytes > bufsize ? bufsize : nbytes;
+
+ if (unlikely(copy_from_user(data, src, current_len))) {
+ dev_err(fmp->dev, "Error copying %d bytes from user address %p\n",
+ (int)current_len, src);
+ ret = -EFAULT;
+ break;
+ }
+
+ sg_init_one(&sg, data, current_len);
+ ret = fmp_n_crypt(info, ses_ptr, cop, &sg, &sg, current_len);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "fmp_n_crypt failed\n");
+ break;
+ }
+
+ if (ses_ptr->cdata.init != 0) {
+ if (unlikely(copy_to_user(dst, data, current_len))) {
+ dev_err(fmp->dev, "could not copy to user\n");
+ ret = -EFAULT;
+ break;
+ }
+ }
+
+ dst += current_len;
+ nbytes -= current_len;
+ src += current_len;
+ }
+ free_page((unsigned long)data);
+
+ return ret;
+}
+
+static int fmp_run(struct fmp_fips_info *info, struct fcrypt *fcr,
+ struct kernel_crypt_op *kcop)
+{
+ struct exynos_fmp *fmp;
+ struct csession *ses_ptr;
+ struct crypt_op *cop = &kcop->cop;
+ int ret = -EINVAL;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) {
+ dev_err(fmp->dev, "invalid operation op=%u\n", cop->op);
+ return -EINVAL;
+ }
+
+ /* this also enters ses_ptr->sem */
+ ses_ptr = fmp_get_session_by_sid(fcr, cop->ses);
+ if (unlikely(!ses_ptr)) {
+ dev_err(fmp->dev, "invalid session ID=0x%08X\n", cop->ses);
+ return -EINVAL;
+ }
+
+ if ((ses_ptr->cdata.init != 0) && (cop->len > PAGE_SIZE)) {
+ dev_err(fmp->dev, "Invalid input length. len = %d\n", cop->len);
+ return -EINVAL;
+ }
+
+ if (ses_ptr->cdata.init != 0) {
+ int blocksize = ses_ptr->cdata.blocksize;
+
+ if (unlikely(cop->len % blocksize)) {
+ dev_err(fmp->dev,
+ "data size (%u) isn't a multiple of block size(%u)\n",
+ cop->len, blocksize);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (cop->flags == COP_FLAG_AES_CBC)
+ ret = fmp_cipher_set_iv(info->data, kcop->iv, 16);
+ else if (cop->flags == COP_FLAG_AES_XTS)
+ ret = fmp_cipher_set_iv(info->data,
+ (uint8_t *)&cop->data_unit_seqnumber, 16);
+ else
+ ret = -EINVAL;
+
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: set_iv failed\n", __func__);
+ goto out_unlock;
+ }
+ }
+
+ if (likely(cop->len)) {
+ ret = __fmp_run_std(info, ses_ptr, &kcop->cop);
+ if (unlikely(ret))
+ goto out_unlock;
+ }
+
+ if (ses_ptr->hdata.init != 0 &&
+ ((cop->flags & COP_FLAG_FINAL) ||
+ (!(cop->flags & COP_FLAG_UPDATE) || cop->len == 0))) {
+ ret = fmp_fips_hash_final(fmp, &ses_ptr->hdata, kcop->hash_output);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "CryptoAPI failure: %d\n", ret);
+ goto out_unlock;
+ }
+ kcop->digestsize = ses_ptr->hdata.digestsize;
+ }
+
+out_unlock:
+ fmp_put_session(ses_ptr);
+ return ret;
+}
+
+static int fmp_run_AES_CBC_MCT(struct fmp_fips_info *info, struct fcrypt *fcr,
+ struct kernel_crypt_op *kcop)
+{
+ struct csession *ses_ptr;
+ struct crypt_op *cop = &kcop->cop;
+ char **Ct = 0;
+ char **Pt = 0;
+ int ret = 0, k = 0;
+ struct exynos_fmp *fmp;
+ char *data = NULL;
+ char __user *src, *dst, *secondLast;
+ struct scatterlist sg;
+ size_t nbytes, bufsize;
+ int y = 0;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) {
+ dev_err(fmp->dev, "invalid operation op=%u\n", cop->op);
+ return -EINVAL;
+ }
+
+ /* this also enters ses_ptr->sem */
+ ses_ptr = fmp_get_session_by_sid(fcr, cop->ses);
+ if (unlikely(!ses_ptr)) {
+ dev_err(fmp->dev, "invalid session ID=0x%08X\n", cop->ses);
+ return -EINVAL;
+ }
+
+ if (cop->len > PAGE_SIZE) {
+ dev_err(fmp->dev, "Invalid input length. len = %d\n", cop->len);
+ return -EINVAL;
+ }
+
+ if (ses_ptr->cdata.init != 0) {
+ int blocksize = ses_ptr->cdata.blocksize;
+
+ if (unlikely(cop->len % blocksize)) {
+ dev_err(fmp->dev,
+ "data size (%u) isn't a multiple of block size(%u)\n",
+ cop->len, blocksize);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ret = fmp_cipher_set_iv(info->data, kcop->iv, 16);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: set_iv failed\n", __func__);
+ goto out_unlock;
+ }
+ }
+
+ if (!cop->len || !(cop->flags & COP_FLAG_AES_CBC_MCT)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ nbytes = cop->len;
+ data = (char *)__get_free_page(GFP_KERNEL);
+ if (unlikely(!data)) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ Pt = kzalloc(1000 * sizeof(char *), GFP_KERNEL);
+ if (!Pt) {
+ ret = -ENOMEM;
+ goto out_err_mem_pt;
+ }
+
+ for (k = 0; k < 1000; k++) {
+ Pt[k] = kzalloc(nbytes, GFP_KERNEL);
+ if (!Pt[k]) {
+ ret = -ENOMEM;
+ goto out_err_mem_pt_k;
+ }
+ }
+
+ Ct = kzalloc(1000 * sizeof(char *), GFP_KERNEL);
+ if (!Ct) {
+ ret = -ENOMEM;
+ goto out_err_mem_ct;
+ }
+
+ for (k = 0; k < 1000; k++) {
+ Ct[k] = kzalloc(nbytes, GFP_KERNEL);
+ if (!Ct[k]) {
+ ret = -ENOMEM;
+ goto out_err_mem_ct_k;
+ }
+ }
+
+ bufsize = (nbytes > PAGE_SIZE) ? PAGE_SIZE : nbytes;
+
+ src = cop->src;
+ dst = cop->dst;
+ secondLast = cop->secondLastEncodedData;
+
+ if (unlikely(copy_from_user(data, src, nbytes))) {
+ dev_err(fmp->dev,
+ "Error copying %d bytes from user address %p.\n",
+ (int)nbytes, src);
+ ret = -EFAULT;
+ goto out_err_fail;
+ }
+
+ sg_init_one(&sg, data, nbytes);
+ for (y = 0; y < 1000; y++) {
+ memcpy(Pt[y], data, nbytes);
+ ret = fmp_n_crypt(info, ses_ptr, cop, &sg, &sg, nbytes);
+ memcpy(Ct[y], data, nbytes);
+
+ if (!memcmp(Pt[y], Ct[y], nbytes))
+ dev_warn(fmp->dev,
+ "plaintext and ciphertext is the same\n");
+
+ if (y == 998) {
+ if (unlikely(copy_to_user(secondLast, data, nbytes)))
+ dev_err(fmp->dev,
+ "unable to copy second last data for AES_CBC_MCT\n");
+ }
+
+ if (y == 0)
+ memcpy(data, kcop->iv, kcop->ivlen);
+ else {
+ if (y != 999)
+ memcpy(data, Ct[y-1], nbytes);
+ }
+
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "fmp_n_crypt failed.\n");
+ ret = -EFAULT;
+ goto out_err_fail;
+ }
+
+ if (cop->op == COP_ENCRYPT)
+ ret = fmp_cipher_set_iv(info->data, Ct[y], 16);
+ else if (cop->op == COP_DECRYPT)
+ ret = fmp_cipher_set_iv(info->data, Pt[y], 16);
+ else
+ ret = -EINVAL;
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: set_iv failed\n", __func__);
+ goto out_unlock;
+ }
+ }
+
+ if (ses_ptr->cdata.init != 0) {
+ if (unlikely(copy_to_user(dst, data, nbytes))) {
+ dev_err(fmp->dev, "could not copy to user.\n");
+ ret = -EFAULT;
+ goto out_err_fail;
+ }
+ }
+
+out_err_fail:
+ for (k = 0; k < 1000; k++)
+ kzfree(Ct[k]);
+out_err_mem_ct_k:
+ kzfree(Ct);
+
+out_err_mem_ct:
+ for (k = 0; k < 1000; k++)
+ kzfree(Pt[k]);
+out_err_mem_pt_k:
+ kzfree(Pt);
+out_err_mem_pt:
+ free_page((unsigned long)data);
+out_unlock:
+ fmp_put_session(ses_ptr);
+
+ return ret;
+}
+
+static int fmp_create_session(struct fmp_fips_info *info,
+ struct fcrypt *fcr, struct session_op *sop)
+{
+ int ret = 0;
+ struct exynos_fmp *fmp;
+ struct csession *ses_new = NULL, *ses_ptr;
+ const char *alg_name = NULL;
+ const char *hash_name = NULL;
+ int hmac_mode = 1;
+
+ /*
+ * With composite aead ciphers, only ckey is used and it can cover all the
+ * structure space; otherwise both keys may be used simultaneously but they
+ * are confined to their spaces
+ */
+ struct {
+ uint8_t mkey[FMP_HMAC_MAX_KEY_LEN];
+ } keys;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ if (unlikely(!sop->cipher && !sop->mac)) {
+ dev_err(fmp->dev, "Both 'cipher' and 'mac' unset.\n");
+ return -EINVAL;
+ }
+
+ switch (sop->cipher) {
+ case 0:
+ break;
+ case FMP_AES_CBC:
+ alg_name = "cbc(aes-fmp)";
+ break;
+ case FMP_AES_XTS:
+ alg_name = "xts(aes-fmp)";
+ break;
+ default:
+ dev_err(fmp->dev, "Invalid cipher : %d\n", sop->cipher);
+ return -EINVAL;
+ }
+
+ switch (sop->mac) {
+ case 0:
+ break;
+ case FMP_SHA2_256_HMAC:
+ hash_name = "hmac-fmp(sha256-fmp)";
+ break;
+ case FMP_SHA2_256:
+ hash_name = "sha256-fmp";
+ hmac_mode = 0;
+ break;
+ default:
+ dev_err(fmp->dev, "Invalid mac : %d\n", sop->mac);
+ return -EINVAL;
+ }
+
+ /* Create a session and put it to the list. */
+ ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL);
+ if (!ses_new)
+ return -ENOMEM;
+
+ if (!fmp->test_data) {
+ dev_err(fmp->dev, "Invalid fips data\n");
+ return -EINVAL;
+ }
+
+ /* Set-up crypto transform. */
+ if (alg_name) {
+ uint8_t keyp[FMP_CIPHER_MAX_KEY_LEN];
+
+ if (unlikely(sop->keylen > FMP_CIPHER_MAX_KEY_LEN)) {
+ dev_err(fmp->dev, "Setting key failed for %s-%zu.\n",
+ alg_name, (size_t)sop->keylen*8);
+ ret = -EINVAL;
+ goto error_cipher;
+ }
+ if (unlikely(copy_from_user(keyp, sop->key, sop->keylen))) {
+ ret = -EFAULT;
+ goto error_cipher;
+ }
+
+ if (!strcmp(alg_name, "xts(aes-fmp)"))
+ ret = fmp_fips_cipher_init(info, &ses_new->cdata, alg_name,
+ keyp, keyp + (sop->keylen >> 1),
+ sop->keylen >> 1);
+ else
+ ret = fmp_fips_cipher_init(info, &ses_new->cdata, alg_name,
+ keyp, NULL, sop->keylen);
+ if (ret < 0) {
+ dev_err(fmp->dev, "%s: Fail to load cipher for %s\n",
+ __func__, alg_name);
+ ret = -EINVAL;
+ goto error_cipher;
+ }
+ }
+
+ if (hash_name) {
+ if (unlikely(sop->mackeylen > FMP_HMAC_MAX_KEY_LEN)) {
+ dev_err(fmp->dev, "Setting key failed for %s-%zu.",
+ hash_name, (size_t)sop->mackeylen*8);
+ ret = -EINVAL;
+ goto error_hash;
+ }
+
+ if (sop->mackey && unlikely(copy_from_user(keys.mkey, sop->mackey,
+ sop->mackeylen))) {
+ ret = -EFAULT;
+ goto error_hash;
+ }
+
+ ret = fmp_fips_hash_init(info, &ses_new->hdata, hash_name,
+ hmac_mode, keys.mkey,
+ sop->mackeylen);
+ if (ret != 0) {
+ dev_err(fmp->dev, "Failed to load hash for %s", hash_name);
+ ret = -EINVAL;
+ goto error_hash;
+ }
+ }
+
+ ses_new->alignmask = max(ses_new->cdata.alignmask,
+ ses_new->hdata.alignmask);
+ ses_new->array_size = DEFAULT_PREALLOC_PAGES;
+ ses_new->pages = kzalloc(ses_new->array_size *
+ sizeof(struct page *), GFP_KERNEL);
+ ses_new->sg = kzalloc(ses_new->array_size *
+ sizeof(struct scatterlist), GFP_KERNEL);
+ if (ses_new->sg == NULL || ses_new->pages == NULL) {
+ dev_err(fmp->dev, "Memory error\n");
+ ret = -ENOMEM;
+ goto error_hash;
+ }
+
+ /* put the new session to the list */
+ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
+ mutex_init(&ses_new->sem);
+
+ mutex_lock(&fcr->sem);
+restart:
+ list_for_each_entry(ses_ptr, &fcr->list, entry) {
+ /* Check for duplicate SID */
+ if (unlikely(ses_new->sid == ses_ptr->sid)) {
+ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
+ /*
+ * Unless we have a broken RNG this
+ * shouldn't loop forever... ;-)
+ */
+ goto restart;
+ }
+ }
+
+ list_add(&ses_new->entry, &fcr->list);
+ mutex_unlock(&fcr->sem);
+
+ /* Fill in some values for the user. */
+ sop->ses = ses_new->sid;
+
+ return 0;
+
+error_hash:
+ fmp_fips_cipher_deinit(&ses_new->cdata);
+ kzfree(ses_new->sg);
+ kzfree(ses_new->pages);
+error_cipher:
+ kzfree(ses_new);
+ return ret;
+}
+
+static inline void fmp_destroy_session(struct fmp_fips_info *info, struct csession *ses_ptr)
+{
+ struct exynos_fmp *fmp;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return;
+ }
+ fmp = info->fmp;
+
+ if (!mutex_trylock(&ses_ptr->sem)) {
+ dev_err(fmp->dev, "Waiting for semaphore of sid=0x%08X\n", ses_ptr->sid);
+ mutex_lock(&ses_ptr->sem);
+ }
+ fmp_fips_cipher_deinit(&ses_ptr->cdata);
+ fmp_fips_hash_deinit(&ses_ptr->hdata);
+ kzfree(ses_ptr->pages);
+ kzfree(ses_ptr->sg);
+ mutex_unlock(&ses_ptr->sem);
+ mutex_destroy(&ses_ptr->sem);
+ kzfree(ses_ptr);
+}
+
+/* Look up a session by ID and remove. */
+static int fmp_finish_session(struct fmp_fips_info *info, struct fcrypt *fcr, uint32_t sid)
+{
+ struct exynos_fmp *fmp;
+ struct csession *tmp, *ses_ptr;
+ struct list_head *head;
+ int ret = 0;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&fcr->sem);
+ head = &fcr->list;
+ fmp = info->fmp;
+
+ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
+ if (ses_ptr->sid == sid) {
+ list_del(&ses_ptr->entry);
+ fmp_destroy_session(info, ses_ptr);
+ break;
+ }
+ }
+
+ if (unlikely(!ses_ptr)) {
+ dev_err(fmp->dev, "%s: Session with sid=0x%08X not found!\n",
+ __func__, sid);
+ ret = -ENOENT;
+ }
+ mutex_unlock(&fcr->sem);
+
+ return ret;
+}
+
+/* Remove all sessions when closing the file */
+static int fmp_finish_all_sessions(struct fmp_fips_info *info, struct fcrypt *fcr)
+{
+ struct csession *tmp, *ses_ptr;
+ struct list_head *head;
+
+ mutex_lock(&fcr->sem);
+
+ head = &fcr->list;
+ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
+ list_del(&ses_ptr->entry);
+ fmp_destroy_session(info, ses_ptr);
+ }
+ mutex_unlock(&fcr->sem);
+
+ return 0;
+}
+
+/* Look up session by session ID. The returned session is locked. */
+struct csession *fmp_get_session_by_sid(struct fcrypt *fcr, uint32_t sid)
+{
+ struct csession *ses_ptr, *retval = NULL;
+
+ if (unlikely(fcr == NULL))
+ return NULL;
+
+ mutex_lock(&fcr->sem);
+ list_for_each_entry(ses_ptr, &fcr->list, entry) {
+ if (ses_ptr->sid == sid) {
+ mutex_lock(&ses_ptr->sem);
+ retval = ses_ptr;
+ break;
+ }
+ }
+ mutex_unlock(&fcr->sem);
+
+ return retval;
+}
+
+static void fmptask_routine(struct work_struct *work)
+{
+ struct fmp_fips_info *info = container_of(work, struct fmp_fips_info, fmptask);
+ struct exynos_fmp *fmp;
+ struct todo_list_item *item;
+ LIST_HEAD(tmp);
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return;
+ }
+ fmp = info->fmp;
+
+ /* fetch all pending jobs into the temporary list */
+ mutex_lock(&info->todo.lock);
+ list_cut_position(&tmp, &info->todo.list, info->todo.list.prev);
+ mutex_unlock(&info->todo.lock);
+
+ /* handle each job locklessly */
+ list_for_each_entry(item, &tmp, __hook) {
+ item->result = fmp_run(info, &info->fcrypt, &item->kcop);
+ if (unlikely(item->result))
+ dev_err(fmp->dev, "%s: crypto_run() failed: %d\n",
+ __func__, item->result);
+ }
+
+ /* push all handled jobs to the done list at once */
+ mutex_lock(&info->done.lock);
+ list_splice_tail(&tmp, &info->done.list);
+ mutex_unlock(&info->done.lock);
+
+ /* wake for POLLIN */
+ wake_up_interruptible(&info->user_waiter);
+}
+
+int fmp_fips_open(struct inode *inode, struct file *file)
+{
+ int i, ret = 0;
+ struct fmp_fips_info *info = NULL;
+ struct todo_list_item *tmp;
+ struct exynos_fmp *fmp = container_of(file->private_data,
+ struct exynos_fmp, miscdev);
+
+ if (!fmp || !fmp->dev) {
+ pr_err("%s: Invalid fmp driver\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev_info(fmp->dev, "fmp fips driver name : %s\n", dev_name(fmp->dev));
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ memset(info, 0, sizeof(*info));
+
+ info->data = fmp_test_init(fmp);
+ if (!info->data) {
+ dev_err(fmp->dev, "%s: Fail to initialize fips test.\n",
+ __func__);
+ goto err;
+ }
+
+ mutex_init(&info->fcrypt.sem);
+ INIT_LIST_HEAD(&info->fcrypt.list);
+ INIT_LIST_HEAD(&info->free.list);
+ INIT_LIST_HEAD(&info->todo.list);
+ INIT_LIST_HEAD(&info->done.list);
+ INIT_WORK(&info->fmptask, fmptask_routine);
+ mutex_init(&info->free.lock);
+ mutex_init(&info->todo.lock);
+ mutex_init(&info->done.lock);
+ init_waitqueue_head(&info->user_waiter);
+
+ for (i = 0; i < DEF_COP_RINGSIZE; i++) {
+ tmp = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL);
+ info->itemcount++;
+ dev_info(fmp->dev, "%s: allocated new item at %lx\n",
+ __func__, (unsigned long)tmp);
+ list_add(&tmp->__hook, &info->free.list);
+ }
+
+ info->fmp = fmp;
+ file->private_data = info;
+
+ dev_info(fmp->dev, "%s opened.\n", dev_name(fmp->dev));
+ return ret;
+err:
+ if (info)
+ fmp_test_exit(info->data);
+ return ret;
+}
+
+/* this function has to be called from process context */
+static int fill_kcop_from_cop(struct fmp_fips_info *info,
+ struct kernel_crypt_op *kcop, struct fcrypt *fcr)
+{
+ struct exynos_fmp *fmp;
+ struct crypt_op *cop = &kcop->cop;
+ struct csession *ses_ptr;
+ int rc;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ /* this also enters ses_ptr->sem */
+ ses_ptr = fmp_get_session_by_sid(fcr, cop->ses);
+ if (unlikely(!ses_ptr)) {
+ dev_err(fmp->dev, "invalid session ID=0x%08X\n", cop->ses);
+ return -EINVAL;
+ }
+ kcop->digestsize = 0; /* will be updated during operation */
+
+ fmp_put_session(ses_ptr);
+
+ kcop->task = current;
+ kcop->mm = current->mm;
+
+ if (cop->iv) {
+ kcop->ivlen = ses_ptr->cdata.ivsize;
+ rc = copy_from_user(kcop->iv, cop->iv, kcop->ivlen);
+ if (unlikely(rc)) {
+ dev_err(fmp->dev,
+ "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n",
+ kcop->ivlen, rc, (unsigned long)cop->iv);
+ return -EFAULT;
+ }
+ } else {
+ kcop->ivlen = 0;
+ }
+
+ return 0;
+}
+
+/* this function has to be called from process context */
+static int fill_cop_from_kcop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
+{
+ int ret;
+
+ if (kcop->digestsize) {
+ ret = copy_to_user(kcop->cop.mac,
+ kcop->hash_output, kcop->digestsize);
+ if (unlikely(ret))
+ return -EFAULT;
+ }
+ if (kcop->ivlen && kcop->cop.flags & COP_FLAG_WRITE_IV) {
+ ret = copy_to_user(kcop->cop.iv,
+ kcop->iv, kcop->ivlen);
+ if (unlikely(ret))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+
+static int kcop_from_user(struct fmp_fips_info *info, struct kernel_crypt_op *kcop,
+ struct fcrypt *fcr, void __user *arg)
+{
+ if (unlikely(copy_from_user(&kcop->cop, arg, sizeof(kcop->cop))))
+ return -EFAULT;
+
+ return fill_kcop_from_cop(info, kcop, fcr);
+}
+
+static int kcop_to_user(struct fmp_fips_info *info, struct kernel_crypt_op *kcop,
+ struct fcrypt *fcr, void __user *arg)
+{
+ int ret;
+ struct exynos_fmp *fmp;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ ret = fill_cop_from_kcop(kcop, fcr);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Error in fill_cop_from_kcop\n");
+ return ret;
+ }
+
+ if (unlikely(copy_to_user(arg, &kcop->cop, sizeof(kcop->cop)))) {
+ dev_err(fmp->dev, "Cannot copy to userspace\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int get_session_info(struct fmp_fips_info *info,
+ struct fcrypt *fcr, struct session_info_op *siop)
+{
+ struct csession *ses_ptr;
+ struct exynos_fmp *fmp;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+
+ /* this also enters ses_ptr->sem */
+ ses_ptr = fmp_get_session_by_sid(fcr, siop->ses);
+ if (unlikely(!ses_ptr)) {
+ dev_err(fmp->dev, "invalid session ID=0x%08X\n", siop->ses);
+ return -EINVAL;
+ }
+
+ siop->flags = 0;
+ siop->alignmask = ses_ptr->alignmask;
+ fmp_put_session(ses_ptr);
+
+ return 0;
+}
+
+long fmp_fips_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
+{
+ int ret;
+ struct session_op sop;
+ struct kernel_crypt_op kcop;
+ struct fmp_fips_info *info = file->private_data;
+ struct exynos_fmp *fmp;
+ void __user *arg = (void __user *)arg_;
+ struct fcrypt *fcr;
+ struct session_info_op siop;
+ uint32_t ses;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: Invalid fmp info\n", __func__);
+ return -ENODEV;
+ }
+ fmp = info->fmp;
+ fcr = &info->fcrypt;
+
+ switch (cmd) {
+ case FMPGSESSION:
+ if (unlikely(copy_from_user(&sop, arg, sizeof(sop))))
+ return -EFAULT;
+
+ ret = fmp_create_session(info, fcr, &sop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: Fail to create fmp session. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = copy_to_user(arg, &sop, sizeof(sop));
+ if (unlikely(ret)) {
+ fmp_finish_session(info, fcr, sop.ses);
+ return -EFAULT;
+ }
+ return ret;
+ case FMPFSESSION:
+ ret = get_user(ses, (uint32_t __user *)arg);
+ if (unlikely(ret))
+ return ret;
+ ret = fmp_finish_session(info, fcr, ses);
+ if (unlikely(ret))
+ dev_err(fmp->dev, "%s: Fail to finish fmp session. ret = %d\n",
+ __func__, ret);
+ return ret;
+ case FMPGSESSIONINFO:
+ if (unlikely(copy_from_user(&siop, arg, sizeof(siop))))
+ return -EFAULT;
+ ret = get_session_info(info, fcr, &siop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: Fail to get fmp session. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return copy_to_user(arg, &siop, sizeof(siop));
+ case FMPCRYPT:
+ ret = kcop_from_user(info, &kcop, fcr, arg);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: Error copying from user", __func__);
+ return ret;
+ }
+
+ if (unlikely(in_fmp_fips_err())) {
+ dev_err(fmp->dev, "%s: Fail to run fmp due to fips in error.",
+ __func__);
+ return -EPERM;
+ }
+
+ ret = fmp_run(info, fcr, &kcop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: Fail to run fmp crypt. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return kcop_to_user(info, &kcop, fcr, arg);
+ case FMP_AES_CBC_MCT:
+ ret = kcop_from_user(info, &kcop, fcr, arg);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: Error copying from user", __func__);
+ return ret;
+ }
+
+ if (unlikely(in_fmp_fips_err())) {
+ dev_err(fmp->dev, "%s: Fail to run fmp due to fips in error.",
+ __func__);
+ return -EPERM;
+ }
+
+ ret = fmp_run_AES_CBC_MCT(info, fcr, &kcop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "%s: Error in fmp_run_AES_CBC_MCT", __func__);
+ return ret;
+ }
+ return kcop_to_user(info, &kcop, fcr, arg);
+ default:
+ dev_err(fmp->dev, "%s: Invalid command : 0x%x\n", __func__, cmd);
+ return -EINVAL;
+ }
+}
+
+/* compatibility code for 32bit userlands */
+#ifdef CONFIG_COMPAT
+static inline void compat_to_session_op(struct compat_session_op *compat,
+ struct session_op *sop)
+{
+ sop->cipher = compat->cipher;
+ sop->mac = compat->mac;
+ sop->keylen = compat->keylen;
+
+ sop->key = compat_ptr(compat->key);
+ sop->mackeylen = compat->mackeylen;
+ sop->mackey = compat_ptr(compat->mackey);
+ sop->ses = compat->ses;
+}
+
+static inline void session_op_to_compat(struct session_op *sop,
+ struct compat_session_op *compat)
+{
+ compat->cipher = sop->cipher;
+ compat->mac = sop->mac;
+ compat->keylen = sop->keylen;
+
+ compat->key = ptr_to_compat(sop->key);
+ compat->mackeylen = sop->mackeylen;
+ compat->mackey = ptr_to_compat(sop->mackey);
+ compat->ses = sop->ses;
+}
+
+static inline void compat_to_crypt_op(struct compat_crypt_op *compat,
+ struct crypt_op *cop)
+{
+ cop->ses = compat->ses;
+ cop->op = compat->op;
+ cop->flags = compat->flags;
+ cop->len = compat->len;
+
+ cop->src = compat_ptr(compat->src);
+ cop->dst = compat_ptr(compat->dst);
+ cop->mac = compat_ptr(compat->mac);
+ cop->iv = compat_ptr(compat->iv);
+
+ cop->data_unit_len = compat->data_unit_len;
+ cop->data_unit_seqnumber = compat->data_unit_seqnumber;
+
+ cop->secondLastEncodedData = compat_ptr(compat->secondLastEncodedData);
+ cop->thirdLastEncodedData = compat_ptr(compat->thirdLastEncodedData);
+}
+
+static inline void crypt_op_to_compat(struct crypt_op *cop,
+ struct compat_crypt_op *compat)
+{
+ compat->ses = cop->ses;
+ compat->op = cop->op;
+ compat->flags = cop->flags;
+ compat->len = cop->len;
+
+ compat->src = ptr_to_compat(cop->src);
+ compat->dst = ptr_to_compat(cop->dst);
+ compat->mac = ptr_to_compat(cop->mac);
+ compat->iv = ptr_to_compat(cop->iv);
+
+ compat->data_unit_len = cop->data_unit_len;
+ compat->data_unit_seqnumber = cop->data_unit_seqnumber;
+
+ compat->secondLastEncodedData = ptr_to_compat(cop->secondLastEncodedData);
+ compat->thirdLastEncodedData = ptr_to_compat(cop->thirdLastEncodedData);
+}
+
+static int compat_kcop_from_user(struct fmp_fips_info *info,
+ struct kernel_crypt_op *kcop,
+ struct fcrypt *fcr, void __user *arg)
+{
+ struct compat_crypt_op compat_cop;
+
+ if (unlikely(copy_from_user(&compat_cop, arg, sizeof(compat_cop))))
+ return -EFAULT;
+ compat_to_crypt_op(&compat_cop, &kcop->cop);
+
+ return fill_kcop_from_cop(info, kcop, fcr);
+}
+
+static int compat_kcop_to_user(struct fmp_fips_info *info,
+ struct kernel_crypt_op *kcop,
+ struct fcrypt *fcr, void __user *arg)
+{
+ int ret;
+ struct exynos_fmp *fmp;
+ struct compat_crypt_op compat_cop;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: fmp info is already free.\n", __func__);
+ return 0;
+ }
+ fmp = info->fmp;
+
+ ret = fill_cop_from_kcop(kcop, fcr);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Error in fill_cop_from_kcop");
+ return ret;
+ }
+ crypt_op_to_compat(&kcop->cop, &compat_cop);
+
+ if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) {
+ dev_err(fmp->dev, "Error copying to user");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+long fmp_fips_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg_)
+{
+ int ret;
+ struct session_op sop;
+ struct compat_session_op compat_sop;
+ struct kernel_crypt_op kcop;
+ struct fmp_fips_info *info = file->private_data;
+ struct exynos_fmp *fmp;
+ void __user *arg = (void __user *)arg_;
+ struct fcrypt *fcr;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: fmp info is already free.\n", __func__);
+ return 0;
+ }
+ fmp = info->fmp;
+
+ fcr = &info->fcrypt;
+
+ switch (cmd) {
+ case FMPFSESSION:
+ case FMPGSESSIONINFO:
+ return fmp_fips_ioctl(file, cmd, arg_);
+
+ case COMPAT_FMPGSESSION:
+ if (unlikely(copy_from_user(&compat_sop, arg, sizeof(compat_sop))))
+ return -EFAULT;
+ compat_to_session_op(&compat_sop, &sop);
+
+ ret = fmp_create_session(info, fcr, &sop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Fail to create fmp session. ret = %d\n", ret);
+ return ret;
+ }
+
+ session_op_to_compat(&sop, &compat_sop);
+ ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop));
+ if (unlikely(ret)) {
+ fmp_finish_session(info, fcr, sop.ses);
+ return -EFAULT;
+ }
+ return ret;
+ case COMPAT_FMPCRYPT:
+ ret = compat_kcop_from_user(info, &kcop, fcr, arg);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Error copying from user");
+ return ret;
+ }
+
+ if (unlikely(in_fmp_fips_err())) {
+ dev_err(fmp->dev, "Fail to run fmp due to fips in error.");
+ return -EPERM;
+ }
+
+ ret = fmp_run(info, fcr, &kcop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Fail to run fm)p crypt. ret = %d\n", ret);
+ return ret;
+ }
+ return compat_kcop_to_user(info, &kcop, fcr, arg);
+ case COMPAT_FMP_AES_CBC_MCT:
+ ret = compat_kcop_from_user(info, &kcop, fcr, arg);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Error copying from user");
+ return ret;
+ }
+
+ if (unlikely(in_fmp_fips_err())) {
+ dev_err(fmp->dev, "Fail to run fmp due to fips in error.");
+ return -EPERM;
+ }
+
+ ret = fmp_run_AES_CBC_MCT(info, fcr, &kcop);
+ if (unlikely(ret)) {
+ dev_err(fmp->dev, "Error in fmp_run_AES_CBC_MCT");
+ return ret;
+ }
+ return compat_kcop_to_user(info, &kcop, fcr, arg);
+ default:
+ dev_err(fmp->dev, "Invalid command : 0x%x\n", cmd);
+ return -EINVAL;
+ }
+}
+#endif /* CONFIG_COMPAT */
+
+int fmp_fips_release(struct inode *inode, struct file *file)
+{
+ struct fmp_fips_info *info = file->private_data;
+ struct exynos_fmp *fmp;
+ struct todo_list_item *item, *item_safe;
+ int items_freed = 0;
+
+ if (!info || !info->fmp) {
+ pr_err("%s: fmp info is already free.\n", __func__);
+ return 0;
+ }
+ fmp = info->fmp;
+
+ cancel_work_sync(&info->fmptask);
+
+ mutex_destroy(&info->todo.lock);
+ mutex_destroy(&info->done.lock);
+ mutex_destroy(&info->free.lock);
+
+ list_splice_tail(&info->todo.list, &info->free.list);
+ list_splice_tail(&info->done.list, &info->free.list);
+
+ list_for_each_entry_safe(item, item_safe, &info->free.list, __hook) {
+ dev_err(fmp->dev, "%s: freeing item at %lx\n",
+ __func__, (unsigned long)item);
+ list_del(&item->__hook);
+ kzfree(item);
+ items_freed++;
+ }
+ if (items_freed != info->itemcount)
+ dev_err(fmp->dev, "%s: freed %d items, but %d should exist!\n",
+ __func__, items_freed, info->itemcount);
+
+ fmp_finish_all_sessions(info, &info->fcrypt);
+ fmp_test_exit(info->data);
+ kzfree(info);
+ file->private_data = NULL;
+ dev_info(fmp->dev, "%s released.\n", dev_name(fmp->dev));
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _FMP_FIPS_FOPS_H_
+#define _FMP_FIPS_FOPS_H_
+int fmp_fips_open(struct inode *inode, struct file *file);
+int fmp_fips_release(struct inode *inode, struct file *file);
+long fmp_fips_ioctl(struct file *file, unsigned int cmd, unsigned long arg_);
+#ifdef CONFIG_COMPAT
+long fmp_fips_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg_);
+#endif /* CONFIG_COMPAT */
+#endif /* _FMP_FIPS_FOPS_H_ */
--- /dev/null
+/*
+ * Exynos FMP device information for FIPS
+ *
+ * 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.
+ */
+
+#ifndef _FMP_FIPS_FOPS_INFO_
+#define _FMP_FIPS_FOPS_INFO_
+
+/* API extensions for linux */
+#define FMP_HMAC_MAX_KEY_LEN 512
+#define FMP_CIPHER_MAX_KEY_LEN 64
+
+enum fmpdev_crypto_op_t {
+ FMP_AES_CBC = 1,
+ FMP_AES_XTS = 2,
+ FMP_SHA2_256_HMAC = 3,
+ FMP_SHA2_256 = 4,
+ FMPFW_SHA2_256_HMAC = 5,
+ FMPFW_SHA2_256 = 6,
+ FMP_ALGORITHM_ALL, /* Keep updated - see below */
+};
+#define FMP_ALGORITHM_MAX (FMP_ALGORITHM_ALL - 1)
+
+/* the maximum of the above */
+#define EALG_MAX_BLOCK_LEN 16
+
+/* Values for hashes/MAC */
+#define AALG_MAX_RESULT_LEN 64
+
+/* maximum length of verbose alg names (depends on CRYPTO_MAX_ALG_NAME) */
+#define FMPDEV_MAX_ALG_NAME 64
+
+#define DEFAULT_PREALLOC_PAGES 32
+
+/* input of FMPGSESSION */
+struct session_op {
+ __u32 cipher; /* cryptodev_crypto_op_t */
+ __u32 mac; /* cryptodev_crypto_op_t */
+
+ __u32 keylen;
+ __u8 __user *key;
+ __u32 mackeylen;
+ __u8 __user *mackey;
+
+ __u32 ses; /* session identifier */
+};
+
+struct session_info_op {
+ __u32 ses; /* session identifier */
+
+ /* verbose names for the requested ciphers */
+ struct alg_info {
+ char cra_name[FMPDEV_MAX_ALG_NAME];
+ char cra_driver_name[FMPDEV_MAX_ALG_NAME];
+ } cipher_info, hash_info;
+
+ __u16 alignmask; /* alignment constraints */
+ __u32 flags; /* SIOP_FLAGS_* */
+};
+
+/* If this flag is set then this algorithm uses
+ * a driver only available in kernel (software drivers,
+ * or drivers based on instruction sets do not set this flag).
+ *
+ * If multiple algorithms are involved (as in AEAD case), then
+ * if one of them is kernel-driver-only this flag will be set.
+ */
+#define SIOP_FLAG_KERNEL_DRIVER_ONLY 1
+
+#define COP_ENCRYPT 0
+#define COP_DECRYPT 1
+
+/* input of FMPCRYPT */
+struct crypt_op {
+ __u32 ses; /* session identifier */
+ __u16 op; /* COP_ENCRYPT or COP_DECRYPT */
+ __u16 flags; /* see COP_FLAG_* */
+ __u32 len; /* length of source data */
+ __u8 __user *src; /* source data */
+ __u8 __user *dst; /* pointer to output data */
+ /* pointer to output data for hash/MAC operations */
+ __u8 __user *mac;
+ /* initialization vector for encryption operations */
+ __u8 __user *iv;
+
+ __u32 data_unit_len;
+ __u32 data_unit_seqnumber;
+
+ __u8 __user *secondLastEncodedData;
+ __u8 __user *thirdLastEncodedData;
+};
+
+#define COP_FLAG_NONE (0 << 0) /* totally no flag */
+#define COP_FLAG_UPDATE (1 << 0) /* multi-update hash mode */
+#define COP_FLAG_FINAL (1 << 1) /* multi-update final hash mode */
+#define COP_FLAG_WRITE_IV (1 << 2) /* update the IV during operation */
+#define COP_FLAG_NO_ZC (1 << 3) /* do not zero-copy */
+#define COP_FLAG_AEAD_TLS_TYPE (1 << 4) /* authenticate and encrypt using the
+ * TLS protocol rules */
+#define COP_FLAG_AEAD_SRTP_TYPE (1 << 5) /* authenticate and encrypt using the
+ * SRTP protocol rules */
+#define COP_FLAG_RESET (1 << 6) /* multi-update reset the state.
+ * should be used in combination
+ * with COP_FLAG_UPDATE */
+#define COP_FLAG_AES_CBC (1 << 7)
+#define COP_FLAG_AES_XTS (1 << 8)
+#define COP_FLAG_AES_CBC_MCT (1 << 9)
+
+#define FMPGSESSION _IOWR('c', 200, struct session_op)
+#define FMPFSESSION _IOWR('c', 201, __u32)
+#define FMPGSESSIONINFO _IOWR('c', 202, struct session_info_op)
+#define FMPCRYPT _IOWR('c', 203, struct crypt_op)
+#define FMP_AES_CBC_MCT _IOWR('c', 204, struct crypt_op)
+
+#endif
--- /dev/null
+/*
+ * Exynos FMP FIPS functional test
+ *
+ * Copyright (C) 2017 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/string.h>
+#include <linux/printk.h>
+#include <linux/device.h>
+
+#include <crypto/fmp.h>
+
+#include "fmp_fips_info.h"
+#include "fmp_fips_main.h"
+#include "fmp_fips_func_test.h"
+
+static char *fips_functest_mode;
+
+static char *fips_functest_KAT_list[] = {
+ "aes-xts",
+ "aes-cbc",
+ "sha256",
+ "hmac-sha256",
+ "integrity",
+ "zeroization"
+};
+
+void set_fmp_fips_functest_KAT_mode(const int num)
+{
+ if (num >= 0 && num < FMP_FUNCTEST_KAT_CASE_NUM)
+ fips_functest_mode = fips_functest_KAT_list[num];
+ else
+ fips_functest_mode = FMP_FUNCTEST_NO_TEST;
+}
+
+char *get_fmp_fips_functest_mode(void)
+{
+ if (fips_functest_mode)
+ return fips_functest_mode;
+ else
+ return FMP_FUNCTEST_NO_TEST;
+}
+
+static int fmp_func_test_integrity(HMAC_SHA256_CTX *desc,
+ unsigned long *start_addr)
+{
+ int ret = 0;
+
+ if (!strcmp("integrity", get_fmp_fips_functest_mode())) {
+ pr_info("FIPS(%s): Failing Integrity Test\n", __func__);
+ ret = hmac_sha256_update(desc,
+ (unsigned char *)start_addr, 1);
+ }
+
+ return ret;
+}
+
+static int fmp_func_test_zeroization(struct fmp_table_setting *table,
+ char *str)
+{
+ if (!table || !str) {
+ pr_err("%s: invalid fmp table address or string\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp("zeroization", get_fmp_fips_functest_mode())) {
+ pr_info("FIPS FMP descriptor zeroize (%s) ", str);
+ print_hex_dump(KERN_ERR, "FIPS FMP descriptor zeroize: ",
+ DUMP_PREFIX_NONE, 16, 1, &table->file_iv0,
+ sizeof(__le32) * 20, false);
+ }
+
+ return 0;
+}
+
+static int fmp_func_test_hmac_sha256(char *digest, char *algorithm)
+{
+ char *str = get_fmp_fips_functest_mode();
+
+ if (!digest || !algorithm) {
+ pr_err("%s: invalid digest address or algorithm\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(algorithm, str))
+ digest[0] += 1;
+
+ return 0;
+}
+
+static int fmp_func_test_aes(const int mode, char *key, unsigned char klen)
+{
+ char *str = get_fmp_fips_functest_mode();
+
+ if (!key || (klen == 0)) {
+ pr_err("%s: invalid fmp key or key length\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((!strcmp("aes-xts", str) && XTS_MODE == mode)
+ || (!strcmp("aes-cbc", str) && CBC_MODE == mode))
+ key[0] += 1;
+
+ return 0;
+}
+
+const struct exynos_fmp_fips_test_vops exynos_fmp_fips_test_ops = {
+ .integrity = fmp_func_test_integrity,
+ .zeroization = fmp_func_test_zeroization,
+ .hmac_sha256 = fmp_func_test_hmac_sha256,
+ .aes = fmp_func_test_aes,
+};
+
+int exynos_fmp_func_test_KAT_case(struct platform_device *pdev,
+ struct exynos_fmp *fmp)
+{
+ int i, ret;
+ struct fmp_crypto_info data;
+ struct fmp_request req;
+ struct device *dev;
+
+ if (!fmp || !fmp->dev) {
+ pr_err("%s: invalid fmp context or device\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev = fmp->dev;
+ fmp->test_vops = (struct exynos_fmp_fips_test_vops *)&exynos_fmp_fips_test_ops;
+
+ for (i = 0; i < FMP_FUNCTEST_KAT_CASE_NUM; i++) {
+ set_fmp_fips_functest_KAT_mode(i);
+ dev_info(dev, "FIPS FUNC : -----------------------------------\n");
+ dev_info(dev, "FIPS FUNC : Test case %d - [%s]\n",
+ i + 1, get_fmp_fips_functest_mode());
+ dev_info(dev, "FIPS FUNC : -----------------------------------\n");
+
+ ret = exynos_fmp_fips_init(fmp);
+ if (ret) {
+ dev_err(dev, "FIPS FUNC : Fail to initialize fmp fips. ret(%d)",
+ ret);
+ exynos_fmp_fips_exit(fmp);
+ goto out;
+ }
+ dev_info(dev, "FIPS FUNC : (%d-1) Selftest done. FMP FIPS status : %s\n",
+ i + 1, in_fmp_fips_err() ? "FAILED" : "PASSED");
+
+ memset(&data, 0, sizeof(struct fmp_crypto_info));
+ memset(&req, 0, sizeof(struct fmp_request));
+ dev_info(dev, "FIPS FUNC : (%d-2) Try to set config\n", i + 1);
+
+ ret = exynos_fmp_crypt(&data, &req);
+ if (ret)
+ dev_info(dev,
+ "FIPS FUNC : (%d-3) Fail FMP config as expected. ret(%d)\n",
+ i + 1, ret);
+
+ exynos_fmp_fips_exit(fmp);
+ }
+
+ set_fmp_fips_functest_KAT_mode(-1);
+
+ dev_info(dev, "FIPS FUNC : Functional test end\n");
+ dev_info(dev, "FIPS FUNC : -----------------------------------\n");
+ dev_info(dev, "FIPS FUNC : Normal init start\n");
+ dev_info(dev, "FIPS FUNC : -----------------------------------\n");
+
+ ret = 0;
+out:
+ fmp->test_vops = NULL;
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _FMP_FIPS_FUNC_TEST_H_
+#define _FMP_FIPS_FUNC_TEST_H_
+
+#if defined(CONFIG_EXYNOS_FMP_FIPS)
+#if defined(CONFIG_EXYNOS_FMP_FIPS_FUNC_TEST)
+#define FMP_FUNCTEST_KAT_CASE_NUM 6
+#define FMP_FUNCTEST_NO_TEST "NO_TEST"
+
+int exynos_fmp_func_test_KAT_case(struct platform_device *pdev,
+ struct exynos_fmp *fmp);
+#else
+inline int exynos_fmp_func_test_KAT_case(struct platform_device *pdev,
+ struct exynos_fmp *fmp)
+{
+ if (fmp)
+ fmp->test_vops = NULL;
+ return 0;
+}
+#endif /* CONFIG_EXYNOS_FMP_FIPS_FUNC_TEST */
+#endif /* CONFIG_EXYNOS_FMP_FIPS */
+#endif /* _FMP_FIPS_FUNC_TEST_H_ */
--- /dev/null
+/*
+ * Exynos FMP device header for FIPS
+ *
+ * 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.
+ */
+
+#ifndef __FMP_FIPS_INFO_H__
+#define __FMP_FIPS_INFO_H__
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/scatterlist.h>
+
+#include "fmp_fips_fops_info.h"
+#include "sha256.h"
+#include "hmac-sha256.h"
+
+#define BYPASS_MODE 0
+#define CBC_MODE 1
+#define XTS_MODE 2
+
+struct fcrypt {
+ struct list_head list;
+ struct mutex sem;
+};
+
+/* kernel-internal extension to struct crypt_op */
+struct kernel_crypt_op {
+ struct crypt_op cop;
+
+ int ivlen;
+ __u8 iv[EALG_MAX_BLOCK_LEN];
+
+ int digestsize;
+ uint8_t hash_output[AALG_MAX_RESULT_LEN];
+
+ struct task_struct *task;
+ struct mm_struct *mm;
+};
+
+struct todo_list_item {
+ struct list_head __hook;
+ struct kernel_crypt_op kcop;
+ int result;
+};
+
+struct locked_list {
+ struct list_head list;
+ struct mutex lock;
+};
+
+struct fmp_fips_info {
+ struct fcrypt fcrypt;
+ struct locked_list free, todo, done;
+ int itemcount;
+ struct work_struct fmptask;
+ wait_queue_head_t user_waiter;
+ struct exynos_fmp *fmp;
+ struct fmp_test_data *data;
+};
+
+/* compatibility stuff */
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+/* input of FMPGSESSION */
+struct compat_session_op {
+ /* Specify either cipher or mac
+ */
+ uint32_t cipher; /* cryptodev_crypto_op_t */
+ uint32_t mac; /* cryptodev_crypto_op_t */
+
+ uint32_t keylen;
+ compat_uptr_t key; /* pointer to key data */
+ uint32_t mackeylen;
+ compat_uptr_t mackey; /* pointer to mac key data */
+
+ uint32_t ses; /* session identifier */
+};
+
+/* input of FMPCRYPT */
+struct compat_crypt_op {
+ uint32_t ses; /* session identifier */
+ uint16_t op; /* COP_ENCRYPT or COP_DECRYPT */
+ uint16_t flags; /* see COP_FLAG_* */
+ uint32_t len; /* length of source data */
+ compat_uptr_t src; /* source data */
+ compat_uptr_t dst; /* pointer to output data */
+ compat_uptr_t mac;/* pointer to output data for hash/MAC operations */
+ compat_uptr_t iv;/* initialization vector for encryption operations */
+
+ __u32 data_unit_len;
+ __u32 data_unit_seqnumber;
+
+ compat_uptr_t secondLastEncodedData;
+ compat_uptr_t thirdLastEncodedData;
+};
+
+#define COMPAT_FMPGSESSION _IOWR('c', 200, struct compat_session_op)
+#define COMPAT_FMPCRYPT _IOWR('c', 203, struct compat_crypt_op)
+#define COMPAT_FMP_AES_CBC_MCT _IOWR('c', 204, struct compat_crypt_op)
+#endif
+
+/* the maximum of the above */
+#define EALG_MAX_BLOCK_LEN 16
+
+struct cipher_data {
+ int init; /* 0 uninitialized */
+ int blocksize;
+ int aead;
+ int stream;
+ int ivsize;
+ int alignmask;
+ struct {
+ /* block ciphers */
+ struct crypto_ablkcipher *s;
+ struct ablkcipher_request *request;
+
+ /* AEAD ciphers */
+ struct crypto_aead *as;
+ struct aead_request *arequest;
+
+ struct fmp_fips_result *result;
+ uint8_t iv[EALG_MAX_BLOCK_LEN];
+ } async;
+};
+
+struct hash_data {
+ int init; /* 0 uninitialized */
+ int digestsize;
+ int alignmask;
+ SHA256_CTX *sha;
+ HMAC_SHA256_CTX *hmac;
+ struct {
+ struct crypto_ahash *s;
+ struct fmp_fips_result *result;
+ struct ahash_request *request;
+ } async;
+};
+
+struct fmp_fips_result {
+ struct completion completion;
+ int err;
+};
+
+/* other internal structs */
+struct csession {
+ struct list_head entry;
+ struct mutex sem;
+ struct cipher_data cdata;
+ struct hash_data hdata;
+ uint32_t sid;
+ uint32_t alignmask;
+
+ unsigned int array_size;
+ unsigned int used_pages; /* the number of pages that are used */
+ /* the number of pages marked as writable (first are the readable) */
+ unsigned int readable_pages;
+ struct page **pages;
+ struct scatterlist *sg;
+};
+
+struct csession *fmp_get_session_by_sid(struct fcrypt *fcr, uint32_t sid);
+
+static inline void fmp_put_session(struct csession *ses_ptr)
+{
+ mutex_unlock(&ses_ptr->sem);
+}
+int adjust_sg_array(struct csession *ses, int pagecount);
+
+#define MAX_TAP 8
+#define XBUFSIZE 8
+
+struct cipher_testvec {
+ char *key;
+ char *iv;
+ char *input;
+ char *result;
+ unsigned short tap[MAX_TAP];
+ int np;
+ unsigned char also_non_np;
+ unsigned char klen;
+ unsigned short ilen;
+ unsigned short rlen;
+};
+
+struct hash_testvec {
+ /* only used with keyed hash algorithms */
+ char *key;
+ char *plaintext;
+ char *digest;
+ unsigned char tap[MAX_TAP];
+ unsigned short psize;
+ unsigned char np;
+ unsigned char ksize;
+};
+
+struct cipher_test_suite {
+ struct {
+ struct cipher_testvec *vecs;
+ unsigned int count;
+ } enc;
+};
+
+struct hash_test_suite {
+ struct hash_testvec *vecs;
+ unsigned int count;
+};
+
+struct exynos_fmp_fips_test_vops {
+ int (*integrity)(HMAC_SHA256_CTX *desc, unsigned long *start_addr);
+ int (*zeroization)(struct fmp_table_setting *table, char *str);
+ int (*hmac_sha256)(char *digest, char *algorithm);
+ int (*aes)(const int mode, char *key, unsigned char klen);
+};
+
+#endif
--- /dev/null
+/*
+ * Perform FIPS Integrity test on FMP driver
+ *
+ * At build time, hmac(sha256) of crypto code, avaiable in different ELF sections
+ * of vmlinux file, is generated. vmlinux file is updated with built-time hmac
+ * in a read-only data variable, so that it is available at run-time
+ *
+ * At run time, hmac(sha256) is again calculated using crypto bytes of a running
+ * At run time, hmac-fmp(sha256-fmp) is again calculated using crypto bytes of a running
+ * kernel.
+ * Run time hmac is compared to built time hmac to verify the integrity.
+ *
+ *
+ * Author : Rohit Kothari (r.kothari@samsung.com)
+ * Date : 11 Feb 2014
+ *
+ * Copyright (c) 2014 Samsung Electronics
+ *
+ */
+
+#include <linux/kallsyms.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#include <linux/smc.h>
+
+#include <crypto/fmp.h>
+
+#include "hmac-sha256.h"
+#include "fmp_fips_info.h"
+#include "fmp_fips_integrity.h"
+
+#undef FIPS_DEBUG_INTEGRITY
+
+/* Same as build time */
+static const unsigned char *integrity_check_key = "The quick brown fox jumps over the lazy dog";
+
+/*
+ * This function return kernel offset.
+ * If CONFIG_RELOCATABLE is set
+ * Then the kernel will be placed into the random offset in memory
+ * At the build time, we write self address of the "buildtime_address"
+ * to the "buildtime_address" variable
+ */
+static __u64 get_kernel_offset(void)
+{
+ static __u64 runtime_address = (__u64) &fmp_buildtime_address;
+ __u64 kernel_offset = abs(fmp_buildtime_address - runtime_address);
+ return kernel_offset;
+}
+
+int do_fmp_integrity_check(struct exynos_fmp *fmp)
+{
+ int i, rows, err;
+ unsigned long start_addr = 0;
+ unsigned long end_addr = 0;
+ unsigned char runtime_hmac[32];
+ struct hmac_sha256_ctx ctx;
+ const char *builtime_hmac = 0;
+ unsigned int size = 0;
+#ifdef FIPS_DEBUG_INTEGRITY
+ unsigned int covered = 0;
+ unsigned int num_addresses = 0;
+#endif
+ struct exynos_fmp_fips_test_vops *test_vops;
+
+ if (!fmp || !fmp->dev) {
+ pr_err("%s: invalid fmp device\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(runtime_hmac, 0x00, 32);
+
+ err = hmac_sha256_init(&ctx, integrity_check_key, strlen(integrity_check_key));
+ if (err) {
+ pr_err("FIPS(%s): init_hash failed\n", __func__);
+ return -1;
+ }
+
+ rows = (__u32) ARRAY_SIZE(integrity_fmp_addrs);
+
+ for (i = 0; integrity_fmp_addrs[i].first && i < rows; i++) {
+
+ start_addr = integrity_fmp_addrs[i].first + get_kernel_offset();
+ end_addr = integrity_fmp_addrs[i].last + get_kernel_offset();
+
+ /* Print addresses for HMAC calculation */
+#ifdef FIPS_DEBUG_INTEGRITY
+ pr_info("FIPS_DEBUG_INTEGRITY : first last %08llux %08llux\n",
+ integrity_fmp_addrs[i].first,
+ integrity_fmp_addrs[i].last);
+ covered += (end_addr - start_addr);
+#endif
+
+ size = (uint32_t)(end_addr - start_addr);
+
+ err = hmac_sha256_update(&ctx, (unsigned char *)start_addr, size);
+ if (err) {
+ pr_err("FIPS(%s): Error to update hash", __func__);
+ return -1;
+ }
+ }
+
+ test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops;
+
+ if (test_vops) {
+ err = test_vops->integrity(&ctx, &start_addr);
+ if (err) {
+ pr_err("FIPS(%s): Error to update hash for func test\n",
+ __func__);
+ return -1;
+ }
+ }
+
+/* Dump bytes for HMAC */
+#ifdef FIPS_DEBUG_INTEGRITY
+ num_addresses = i;
+ for (i = 0; integrity_fmp_addrs[i].first && i < rows; i++) {
+ start_addr = integrity_fmp_addrs[i].first + get_kernel_offset();
+ end_addr = integrity_fmp_addrs[i].last + get_kernel_offset();
+ size = (uint32_t)(end_addr - start_addr);
+ print_hex_dump(KERN_INFO, "FIPS_DEBUG_INTEGRITY : bytes for HMAC = ",
+ DUMP_PREFIX_NONE, 16, 1,
+ (char *)start_addr, size, false);
+ }
+#endif
+
+ err = hmac_sha256_final(&ctx, runtime_hmac);
+ if (err) {
+ pr_err("FIPS(%s): Error in finalize", __func__);
+ hmac_sha256_ctx_cleanup(&ctx);
+ return -1;
+ }
+
+ hmac_sha256_ctx_cleanup(&ctx);
+ builtime_hmac = builtime_fmp_hmac;
+
+#ifdef FIPS_DEBUG_INTEGRITY
+ pr_info("FIPS_DEBUG_INTEGRITY : %d bytes are covered, Address fragments (%d)", covered, num_addresses);
+ print_hex_dump(KERN_INFO, "FIPS FMP RUNTIME : runtime hmac = ",
+ DUMP_PREFIX_NONE, 16, 1, runtime_hmac, sizeof(runtime_hmac), false);
+ print_hex_dump(KERN_INFO, "FIPS FMP RUNTIME : builtime_hmac = ",
+ DUMP_PREFIX_NONE, 16, 1, builtime_hmac, sizeof(runtime_hmac), false);
+#endif
+
+ if (!memcmp(builtime_hmac, runtime_hmac, sizeof(runtime_hmac))) {
+ pr_info("FIPS(%s): Integrity Check Passed", __func__);
+ return 0;
+ }
+
+ pr_err("FIPS(%s): Integrity Check Failed", __func__);
+ return -1;
+}
+EXPORT_SYMBOL_GPL(do_fmp_integrity_check);
--- /dev/null
+/*
+ * Exynos FMP integrity header
+ *
+ * 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.
+ */
+
+#ifndef __FMP_INTEGRITY_H__
+#define __FMP_INTEGRITY_H__
+
+#include <linux/kernel.h>
+
+#define FIPS_HMAC_SIZE (32)
+#define FIPS_FMP_ADDRS_SIZE (100)
+
+struct first_last {
+ aligned_u64 first;
+ aligned_u64 last;
+};
+
+extern const __u64 fmp_buildtime_address;
+extern const struct first_last integrity_fmp_addrs[FIPS_FMP_ADDRS_SIZE];
+extern const __s8 builtime_fmp_hmac[FIPS_HMAC_SIZE];
+
+int do_fmp_integrity_check(struct exynos_fmp *fmp);
+
+#endif
--- /dev/null
+/*
+ * Exynos FMP test 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/of.h>
+#include <linux/of_platform.h>
+#include <linux/miscdevice.h>
+#include <linux/crypto.h>
+#include <linux/buffer_head.h>
+#include <linux/genhd.h>
+#include <linux/delay.h>
+
+#include <crypto/authenc.h>
+#include <crypto/fmp.h>
+
+#include "fmp_fips_main.h"
+#include "fmp_fips_fops.h"
+#include "fmp_fips_selftest.h"
+#include "fmp_fips_integrity.h"
+#include "fmp_test.h"
+
+static const char pass[] = "passed";
+static const char fail[] = "failed";
+
+enum fips_state {
+ FMP_FIPS_INIT_STATE,
+ FMP_FIPS_PROGRESS_STATE,
+ FMP_FIPS_ERR_STATE,
+ FMP_FIPS_SUCCESS_STATE
+};
+
+static enum fips_state fmp_fips_state = FMP_FIPS_INIT_STATE;
+
+bool in_fmp_fips_err(void)
+{
+ if (fmp_fips_state == FMP_FIPS_INIT_STATE ||
+ fmp_fips_state == FMP_FIPS_ERR_STATE)
+ return true;
+ return false;
+}
+
+static void set_fmp_fips_state(uint32_t val)
+{
+ fmp_fips_state = val;
+}
+
+static ssize_t fmp_fips_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_fmp *fmp = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(pass), "%s\n", fmp->result.overall ? pass : fail);
+}
+
+static ssize_t fmp_fips_aes_xts_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_fmp *fmp = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(pass), "%s\n", fmp->result.aes_xts ? pass : fail);
+}
+
+static ssize_t fmp_fips_aes_cbc_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_fmp *fmp = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(pass), "%s\n", fmp->result.aes_cbc ? pass : fail);
+}
+
+static ssize_t fmp_fips_sha256_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_fmp *fmp = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(pass), "%s\n", fmp->result.sha256 ? pass : fail);
+}
+
+static ssize_t fmp_fips_hmac_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_fmp *fmp = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(pass), "%s\n", fmp->result.hmac ? pass : fail);
+}
+
+static ssize_t fmp_fips_integrity_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_fmp *fmp = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(pass), "%s\n", fmp->result.integrity ? pass : fail);
+}
+
+static DEVICE_ATTR(fmp_fips_status, 0444, fmp_fips_result_show, NULL);
+static DEVICE_ATTR(aes_xts_status, 0444, fmp_fips_aes_xts_result_show, NULL);
+static DEVICE_ATTR(aes_cbc_status, 0444, fmp_fips_aes_cbc_result_show, NULL);
+static DEVICE_ATTR(sha256_status, 0444, fmp_fips_sha256_result_show, NULL);
+static DEVICE_ATTR(hmac_status, 0444, fmp_fips_hmac_result_show, NULL);
+static DEVICE_ATTR(integrity_status, 0444, fmp_fips_integrity_result_show, NULL);
+
+static struct attribute *fmp_fips_attr[] = {
+ &dev_attr_fmp_fips_status.attr,
+ &dev_attr_aes_xts_status.attr,
+ &dev_attr_aes_cbc_status.attr,
+ &dev_attr_sha256_status.attr,
+ &dev_attr_hmac_status.attr,
+ &dev_attr_integrity_status.attr,
+ NULL,
+};
+
+static struct attribute_group fmp_fips_attr_group = {
+ .name = "fmp-fips",
+ .attrs = fmp_fips_attr,
+};
+
+static const struct file_operations fmp_fips_fops = {
+ .owner = THIS_MODULE,
+ .open = fmp_fips_open,
+ .release = fmp_fips_release,
+ .unlocked_ioctl = fmp_fips_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = fmp_fips_compat_ioctl,
+#endif
+};
+
+int exynos_fmp_fips_init(struct exynos_fmp *fmp)
+{
+ int ret;
+
+ if (!fmp || !fmp->dev) {
+ pr_err("%s: Invalid exynos fmp dev\n", __func__);
+ return -EINVAL;
+ }
+
+ set_fmp_fips_state(FMP_FIPS_INIT_STATE);
+
+ fmp->miscdev.minor = MISC_DYNAMIC_MINOR;
+ fmp->miscdev.name = "fmp";
+ fmp->miscdev.fops = &fmp_fips_fops;
+ ret = misc_register(&fmp->miscdev);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to register misc device. ret(%d)\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = sysfs_create_group(&fmp->dev->kobj, &fmp_fips_attr_group);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to create sysfs. ret(%d)\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ set_fmp_fips_state(FMP_FIPS_SUCCESS_STATE);
+
+ fmp->test_data = fmp_test_init(fmp);
+ if (!fmp->test_data) {
+ dev_err(fmp->dev,
+ "%s: fails to initialize fips test.\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = do_fmp_selftest(fmp);
+ if (ret) {
+ dev_err(fmp->dev, "%s: self-tests for FMP failed\n", __func__);
+ goto err;
+ } else {
+ dev_info(fmp->dev, "%s: self-tests for FMP passed\n", __func__);
+ }
+
+ /* TODO: Add integrity check */
+#ifdef CHECK_INTEGRITY
+ ret = do_fmp_integrity_check(fmp);
+ if (ret) {
+ dev_err(fmp->dev, "%s: integrity check for FMP failed\n", __func__);
+ fmp->result.integrity = 0;
+ goto err;
+ } else {
+ dev_info(fmp->dev, "%s: integrity check for FMP passed\n", __func__);
+ fmp->result.integrity = 1;
+ }
+#else
+ fmp->result.integrity = 1;
+#endif
+ set_fmp_fips_state(FMP_FIPS_SUCCESS_STATE);
+ fmp->result.overall = 1;
+ fmp_test_exit(fmp->test_data);
+ return 0;
+err:
+#if defined(CONFIG_NODE_FOR_SELFTEST_FAIL)
+ set_fmp_fips_state(FMP_FIPS_ERR_STATE);
+ fmp->result.overall = 0;
+ fmp_test_exit(fmp->test_data);
+ return 0;
+#else
+ panic("%s: Panic due to FMP self test for FIPS KAT", __func__);
+#endif
+ return -EINVAL;
+}
+
+void exynos_fmp_fips_exit(struct exynos_fmp *fmp)
+{
+ sysfs_remove_group(&fmp->dev->kobj, &fmp_fips_attr_group);
+ misc_deregister(&fmp->miscdev);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _FMP_FIPS_MAIN_H_
+#define _FMP_FIPS_MAIN_H_
+
+#if defined(CONFIG_EXYNOS_FMP_FIPS)
+int exynos_fmp_fips_init(struct exynos_fmp *fmp);
+void exynos_fmp_fips_exit(struct exynos_fmp *fmp);
+bool in_fmp_fips_err(void);
+#else
+inline int exynos_fmp_fips_init(struct exynos_fmp *fmp)
+{
+ return 0;
+}
+
+inline void exynos_fmp_fips_exit(struct exynos_fmp *fmp)
+{
+}
+
+inline bool in_fmp_fips_err(void)
+{
+ return 0;
+}
+#endif /* CONFIG_EXYNOS_FMP_FIPS */
+#endif /* _FMP_FIPS_MAIN_H_ */
--- /dev/null
+/*
+ * Exynos FMP selftest 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/crypto.h>
+
+#include <crypto/fmp.h>
+
+#include "fmp_fips_info.h"
+#include "fmp_test.h"
+#include "fmp_fips_cipher.h"
+#include "sha256.h"
+#include "hmac-sha256.h"
+
+static const char *ALG_SHA256_FMP = "sha256";
+static const char *ALG_HMAC_SHA256_FMP = "hmac(sha256)";
+
+#if 0
+/*
+ * Indexes into the xbuf to simulate cross-page access.
+ */
+#define IDX1 32
+#define IDX2 32400
+#define IDX3 1
+#define IDX4 8193
+#define IDX5 22222
+#define IDX6 17101
+#define IDX7 27333
+#define IDX8 3000
+
+static unsigned int IDX[8] = { IDX1, IDX2, IDX3, IDX4, IDX5, IDX6, IDX7, IDX8 };
+#endif
+
+#define AES_XTS_ENC_TEST_VECTORS 5
+#define AES_CBC_ENC_TEST_VECTORS 4
+
+static void hexdump(uint8_t *buf, uint32_t len)
+{
+ print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
+ 16, 1,
+ buf, len, false);
+}
+
+static int alloc_buf(char *buf[XBUFSIZE])
+{
+ int i;
+
+ for (i = 0; i < XBUFSIZE; i++) {
+ buf[i] = (void *)__get_free_page(GFP_KERNEL);
+ if (!buf[i])
+ goto err_free_buf;
+ }
+
+ return 0;
+
+err_free_buf:
+ while (i-- > 0)
+ free_page((unsigned long)buf[i]);
+
+ return -ENOMEM;
+}
+
+static void free_buf(char *buf[XBUFSIZE])
+{
+ int i;
+
+ for (i = 0; i < XBUFSIZE; i++)
+ free_page((unsigned long)buf[i]);
+}
+
+static struct cipher_testvec aes_xts_enc_tv_template[] = {
+ /* http://grouper.ieee.org/groups/1619/email/pdf00086.pdf */
+ { /* XTS-AES 1 */
+ .key = "\xa1\xb9\x0c\xba\x3f\x06\xac\x35"
+ "\x3b\x2c\x34\x38\x76\x08\x17\x62"
+ "\x09\x09\x23\x02\x6e\x91\x77\x18"
+ "\x15\xf2\x9d\xab\x01\x93\x2f\x2f",
+ .klen = 32,
+ .iv = "\x4f\xae\xf7\x11\x7c\xda\x59\xc6"
+ "\x6e\x4b\x92\x01\x3e\x76\x8a\xd5",
+ .input = "\xeb\xab\xce\x95\xb1\x4d\x3c\x8d"
+ "\x6f\xb3\x50\x39\x07\x90\x31\x1c",
+ .ilen = 16,
+ .result = "\x77\x8a\xe8\xb4\x3c\xb9\x8d\x5a"
+ "\x82\x50\x81\xd5\xbe\x47\x1c\x63",
+ .rlen = 16,
+ }, { /* XTS-AES 2 */
+ .key = "\x11\x11\x11\x11\x11\x11\x11\x11"
+ "\x11\x11\x11\x11\x11\x11\x11\x11"
+ "\x22\x22\x22\x22\x22\x22\x22\x22"
+ "\x22\x22\x22\x22\x22\x22\x22\x22",
+ .klen = 32,
+ .iv = "\x33\x33\x33\x33\x33\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x44\x44\x44\x44\x44\x44\x44\x44"
+ "\x44\x44\x44\x44\x44\x44\x44\x44"
+ "\x44\x44\x44\x44\x44\x44\x44\x44"
+ "\x44\x44\x44\x44\x44\x44\x44\x44",
+ .ilen = 32,
+ .result = "\xc4\x54\x18\x5e\x6a\x16\x93\x6e"
+ "\x39\x33\x40\x38\xac\xef\x83\x8b"
+ "\xfb\x18\x6f\xff\x74\x80\xad\xc4"
+ "\x28\x93\x82\xec\xd6\xd3\x94\xf0",
+ .rlen = 32,
+ }, { /* XTS-AES 3 */
+ .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8"
+ "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0"
+ "\x22\x22\x22\x22\x22\x22\x22\x22"
+ "\x22\x22\x22\x22\x22\x22\x22\x22",
+ .klen = 32,
+ .iv = "\x33\x33\x33\x33\x33\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x44\x44\x44\x44\x44\x44\x44\x44"
+ "\x44\x44\x44\x44\x44\x44\x44\x44"
+ "\x44\x44\x44\x44\x44\x44\x44\x44"
+ "\x44\x44\x44\x44\x44\x44\x44\x44",
+ .ilen = 32,
+ .result = "\xaf\x85\x33\x6b\x59\x7a\xfc\x1a"
+ "\x90\x0b\x2e\xb2\x1e\xc9\x49\xd2"
+ "\x92\xdf\x4c\x04\x7e\x0b\x21\x53"
+ "\x21\x86\xa5\x97\x1a\x22\x7a\x89",
+ .rlen = 32,
+ }, { /* XTS-AES 4 */
+ .key = "\x27\x18\x28\x18\x28\x45\x90\x45"
+ "\x23\x53\x60\x28\x74\x71\x35\x26"
+ "\x31\x41\x59\x26\x53\x58\x97\x93"
+ "\x23\x84\x62\x64\x33\x83\x27\x95",
+ .klen = 32,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x20\x21\x22\x23\x24\x25\x26\x27"
+ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+ "\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
+ "\x40\x41\x42\x43\x44\x45\x46\x47"
+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
+ "\x50\x51\x52\x53\x54\x55\x56\x57"
+ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
+ "\x60\x61\x62\x63\x64\x65\x66\x67"
+ "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+ "\x70\x71\x72\x73\x74\x75\x76\x77"
+ "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+ "\x80\x81\x82\x83\x84\x85\x86\x87"
+ "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+ "\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7"
+ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
+ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7"
+ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+ "\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x20\x21\x22\x23\x24\x25\x26\x27"
+ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+ "\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
+ "\x40\x41\x42\x43\x44\x45\x46\x47"
+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
+ "\x50\x51\x52\x53\x54\x55\x56\x57"
+ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
+ "\x60\x61\x62\x63\x64\x65\x66\x67"
+ "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+ "\x70\x71\x72\x73\x74\x75\x76\x77"
+ "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+ "\x80\x81\x82\x83\x84\x85\x86\x87"
+ "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+ "\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7"
+ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
+ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7"
+ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+ "\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ilen = 512,
+ .result = "\x27\xa7\x47\x9b\xef\xa1\xd4\x76"
+ "\x48\x9f\x30\x8c\xd4\xcf\xa6\xe2"
+ "\xa9\x6e\x4b\xbe\x32\x08\xff\x25"
+ "\x28\x7d\xd3\x81\x96\x16\xe8\x9c"
+ "\xc7\x8c\xf7\xf5\xe5\x43\x44\x5f"
+ "\x83\x33\xd8\xfa\x7f\x56\x00\x00"
+ "\x05\x27\x9f\xa5\xd8\xb5\xe4\xad"
+ "\x40\xe7\x36\xdd\xb4\xd3\x54\x12"
+ "\x32\x80\x63\xfd\x2a\xab\x53\xe5"
+ "\xea\x1e\x0a\x9f\x33\x25\x00\xa5"
+ "\xdf\x94\x87\xd0\x7a\x5c\x92\xcc"
+ "\x51\x2c\x88\x66\xc7\xe8\x60\xce"
+ "\x93\xfd\xf1\x66\xa2\x49\x12\xb4"
+ "\x22\x97\x61\x46\xae\x20\xce\x84"
+ "\x6b\xb7\xdc\x9b\xa9\x4a\x76\x7a"
+ "\xae\xf2\x0c\x0d\x61\xad\x02\x65"
+ "\x5e\xa9\x2d\xc4\xc4\xe4\x1a\x89"
+ "\x52\xc6\x51\xd3\x31\x74\xbe\x51"
+ "\xa1\x0c\x42\x11\x10\xe6\xd8\x15"
+ "\x88\xed\xe8\x21\x03\xa2\x52\xd8"
+ "\xa7\x50\xe8\x76\x8d\xef\xff\xed"
+ "\x91\x22\x81\x0a\xae\xb9\x9f\x91"
+ "\x72\xaf\x82\xb6\x04\xdc\x4b\x8e"
+ "\x51\xbc\xb0\x82\x35\xa6\xf4\x34"
+ "\x13\x32\xe4\xca\x60\x48\x2a\x4b"
+ "\xa1\xa0\x3b\x3e\x65\x00\x8f\xc5"
+ "\xda\x76\xb7\x0b\xf1\x69\x0d\xb4"
+ "\xea\xe2\x9c\x5f\x1b\xad\xd0\x3c"
+ "\x5c\xcf\x2a\x55\xd7\x05\xdd\xcd"
+ "\x86\xd4\x49\x51\x1c\xeb\x7e\xc3"
+ "\x0b\xf1\x2b\x1f\xa3\x5b\x91\x3f"
+ "\x9f\x74\x7a\x8a\xfd\x1b\x13\x0e"
+ "\x94\xbf\xf9\x4e\xff\xd0\x1a\x91"
+ "\x73\x5c\xa1\x72\x6a\xcd\x0b\x19"
+ "\x7c\x4e\x5b\x03\x39\x36\x97\xe1"
+ "\x26\x82\x6f\xb6\xbb\xde\x8e\xcc"
+ "\x1e\x08\x29\x85\x16\xe2\xc9\xed"
+ "\x03\xff\x3c\x1b\x78\x60\xf6\xde"
+ "\x76\xd4\xce\xcd\x94\xc8\x11\x98"
+ "\x55\xef\x52\x97\xca\x67\xe9\xf3"
+ "\xe7\xff\x72\xb1\xe9\x97\x85\xca"
+ "\x0a\x7e\x77\x20\xc5\xb3\x6d\xc6"
+ "\xd7\x2c\xac\x95\x74\xc8\xcb\xbc"
+ "\x2f\x80\x1e\x23\xe5\x6f\xd3\x44"
+ "\xb0\x7f\x22\x15\x4b\xeb\xa0\xf0"
+ "\x8c\xe8\x89\x1e\x64\x3e\xd9\x95"
+ "\xc9\x4d\x9a\x69\xc9\xf1\xb5\xf4"
+ "\x99\x02\x7a\x78\x57\x2a\xee\xbd"
+ "\x74\xd2\x0c\xc3\x98\x81\xc2\x13"
+ "\xee\x77\x0b\x10\x10\xe4\xbe\xa7"
+ "\x18\x84\x69\x77\xae\x11\x9f\x7a"
+ "\x02\x3a\xb5\x8c\xca\x0a\xd7\x52"
+ "\xaf\xe6\x56\xbb\x3c\x17\x25\x6a"
+ "\x9f\x6e\x9b\xf1\x9f\xdd\x5a\x38"
+ "\xfc\x82\xbb\xe8\x72\xc5\x53\x9e"
+ "\xdb\x60\x9e\xf4\xf7\x9c\x20\x3e"
+ "\xbb\x14\x0f\x2e\x58\x3c\xb2\xad"
+ "\x15\xb4\xaa\x5b\x65\x50\x16\xa8"
+ "\x44\x92\x77\xdb\xd4\x77\xef\x2c"
+ "\x8d\x6c\x01\x7d\xb7\x38\xb1\x8d"
+ "\xeb\x4a\x42\x7d\x19\x23\xce\x3f"
+ "\xf2\x62\x73\x57\x79\xa4\x18\xf2"
+ "\x0a\x28\x2d\xf9\x20\x14\x7b\xea"
+ "\xbe\x42\x1e\xe5\x31\x9d\x05\x68",
+ .rlen = 512,
+ }, { /* XTS-AES 10, XTS-AES-256, data unit 512 bytes */
+ .key = "\x27\x18\x28\x18\x28\x45\x90\x45"
+ "\x23\x53\x60\x28\x74\x71\x35\x26"
+ "\x62\x49\x77\x57\x24\x70\x93\x69"
+ "\x99\x59\x57\x49\x66\x96\x76\x27"
+ "\x31\x41\x59\x26\x53\x58\x97\x93"
+ "\x23\x84\x62\x64\x33\x83\x27\x95"
+ "\x02\x88\x41\x97\x16\x93\x99\x37"
+ "\x51\x05\x82\x09\x74\x94\x45\x92",
+ .klen = 64,
+ .iv = "\xff\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .input = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x20\x21\x22\x23\x24\x25\x26\x27"
+ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+ "\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
+ "\x40\x41\x42\x43\x44\x45\x46\x47"
+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
+ "\x50\x51\x52\x53\x54\x55\x56\x57"
+ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
+ "\x60\x61\x62\x63\x64\x65\x66\x67"
+ "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+ "\x70\x71\x72\x73\x74\x75\x76\x77"
+ "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+ "\x80\x81\x82\x83\x84\x85\x86\x87"
+ "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+ "\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7"
+ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
+ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7"
+ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+ "\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x20\x21\x22\x23\x24\x25\x26\x27"
+ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+ "\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
+ "\x40\x41\x42\x43\x44\x45\x46\x47"
+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
+ "\x50\x51\x52\x53\x54\x55\x56\x57"
+ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
+ "\x60\x61\x62\x63\x64\x65\x66\x67"
+ "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+ "\x70\x71\x72\x73\x74\x75\x76\x77"
+ "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+ "\x80\x81\x82\x83\x84\x85\x86\x87"
+ "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+ "\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7"
+ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
+ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7"
+ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+ "\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ilen = 512,
+ .result = "\x1c\x3b\x3a\x10\x2f\x77\x03\x86"
+ "\xe4\x83\x6c\x99\xe3\x70\xcf\x9b"
+ "\xea\x00\x80\x3f\x5e\x48\x23\x57"
+ "\xa4\xae\x12\xd4\x14\xa3\xe6\x3b"
+ "\x5d\x31\xe2\x76\xf8\xfe\x4a\x8d"
+ "\x66\xb3\x17\xf9\xac\x68\x3f\x44"
+ "\x68\x0a\x86\xac\x35\xad\xfc\x33"
+ "\x45\xbe\xfe\xcb\x4b\xb1\x88\xfd"
+ "\x57\x76\x92\x6c\x49\xa3\x09\x5e"
+ "\xb1\x08\xfd\x10\x98\xba\xec\x70"
+ "\xaa\xa6\x69\x99\xa7\x2a\x82\xf2"
+ "\x7d\x84\x8b\x21\xd4\xa7\x41\xb0"
+ "\xc5\xcd\x4d\x5f\xff\x9d\xac\x89"
+ "\xae\xba\x12\x29\x61\xd0\x3a\x75"
+ "\x71\x23\xe9\x87\x0f\x8a\xcf\x10"
+ "\x00\x02\x08\x87\x89\x14\x29\xca"
+ "\x2a\x3e\x7a\x7d\x7d\xf7\xb1\x03"
+ "\x55\x16\x5c\x8b\x9a\x6d\x0a\x7d"
+ "\xe8\xb0\x62\xc4\x50\x0d\xc4\xcd"
+ "\x12\x0c\x0f\x74\x18\xda\xe3\xd0"
+ "\xb5\x78\x1c\x34\x80\x3f\xa7\x54"
+ "\x21\xc7\x90\xdf\xe1\xde\x18\x34"
+ "\xf2\x80\xd7\x66\x7b\x32\x7f\x6c"
+ "\x8c\xd7\x55\x7e\x12\xac\x3a\x0f"
+ "\x93\xec\x05\xc5\x2e\x04\x93\xef"
+ "\x31\xa1\x2d\x3d\x92\x60\xf7\x9a"
+ "\x28\x9d\x6a\x37\x9b\xc7\x0c\x50"
+ "\x84\x14\x73\xd1\xa8\xcc\x81\xec"
+ "\x58\x3e\x96\x45\xe0\x7b\x8d\x96"
+ "\x70\x65\x5b\xa5\xbb\xcf\xec\xc6"
+ "\xdc\x39\x66\x38\x0a\xd8\xfe\xcb"
+ "\x17\xb6\xba\x02\x46\x9a\x02\x0a"
+ "\x84\xe1\x8e\x8f\x84\x25\x20\x70"
+ "\xc1\x3e\x9f\x1f\x28\x9b\xe5\x4f"
+ "\xbc\x48\x14\x57\x77\x8f\x61\x60"
+ "\x15\xe1\x32\x7a\x02\xb1\x40\xf1"
+ "\x50\x5e\xb3\x09\x32\x6d\x68\x37"
+ "\x8f\x83\x74\x59\x5c\x84\x9d\x84"
+ "\xf4\xc3\x33\xec\x44\x23\x88\x51"
+ "\x43\xcb\x47\xbd\x71\xc5\xed\xae"
+ "\x9b\xe6\x9a\x2f\xfe\xce\xb1\xbe"
+ "\xc9\xde\x24\x4f\xbe\x15\x99\x2b"
+ "\x11\xb7\x7c\x04\x0f\x12\xbd\x8f"
+ "\x6a\x97\x5a\x44\xa0\xf9\x0c\x29"
+ "\xa9\xab\xc3\xd4\xd8\x93\x92\x72"
+ "\x84\xc5\x87\x54\xcc\xe2\x94\x52"
+ "\x9f\x86\x14\xdc\xd2\xab\xa9\x91"
+ "\x92\x5f\xed\xc4\xae\x74\xff\xac"
+ "\x6e\x33\x3b\x93\xeb\x4a\xff\x04"
+ "\x79\xda\x9a\x41\x0e\x44\x50\xe0"
+ "\xdd\x7a\xe4\xc6\xe2\x91\x09\x00"
+ "\x57\x5d\xa4\x01\xfc\x07\x05\x9f"
+ "\x64\x5e\x8b\x7e\x9b\xfd\xef\x33"
+ "\x94\x30\x54\xff\x84\x01\x14\x93"
+ "\xc2\x7b\x34\x29\xea\xed\xb4\xed"
+ "\x53\x76\x44\x1a\x77\xed\x43\x85"
+ "\x1a\xd7\x7f\x16\xf5\x41\xdf\xd2"
+ "\x69\xd5\x0d\x6a\x5f\x14\xfb\x0a"
+ "\xab\x1c\xbb\x4c\x15\x50\xbe\x97"
+ "\xf7\xab\x40\x66\x19\x3c\x4c\xaa"
+ "\x77\x3d\xad\x38\x01\x4b\xd2\x09"
+ "\x2f\xa7\x55\xc8\x24\xbb\x5e\x54"
+ "\xc4\xf3\x6f\xfd\xa9\xfc\xea\x70"
+ "\xb9\xc6\xe6\x93\xe1\x48\xc1\x51",
+ .rlen = 512,
+ }
+};
+
+static struct cipher_testvec aes_cbc_enc_tv_template[] = {
+ { /* From RFC 3602 */
+ .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b"
+ "\x51\x2e\x03\xd5\x34\x12\x00\x06",
+ .klen = 16,
+ .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30"
+ "\xb4\x22\xda\x80\x2c\x9f\xac\x41",
+ .input = "Single block msg",
+ .ilen = 16,
+ .result = "\xe3\x53\x77\x9c\x10\x79\xae\xb8"
+ "\x27\x08\x94\x2d\xbe\x77\x18\x1a",
+ .rlen = 16,
+ }, {
+ .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0"
+ "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a",
+ .klen = 16,
+ .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28"
+ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58",
+ .input = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+ .ilen = 32,
+ .result = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a"
+ "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a"
+ "\x75\x86\x60\x2d\x25\x3c\xff\xf9"
+ "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1",
+ .rlen = 32,
+ }, {
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .ilen = 64,
+ .result = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba"
+ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6"
+ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d"
+ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d"
+ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf"
+ "\xa5\x30\xe2\x63\x04\x23\x14\x61"
+ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc"
+ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b",
+ .rlen = 64,
+ }, { /* Generated with Crypto++ */
+ .key = "\xC9\x83\xA6\xC9\xEC\x0F\x32\x55"
+ "\x0F\x32\x55\x78\x9B\xBE\x78\x9B"
+ "\xBE\xE1\x04\x27\xE1\x04\x27\x4A"
+ "\x6D\x90\x4A\x6D\x90\xB3\xD6\xF9",
+ .klen = 32,
+ .iv = "\xE7\x82\x1D\xB8\x53\x11\xAC\x47"
+ "\xE2\x7D\x18\xD6\x71\x0C\xA7\x42",
+ .input = "\x50\xB9\x22\xAE\x17\x80\x0C\x75"
+ "\xDE\x47\xD3\x3C\xA5\x0E\x9A\x03"
+ "\x6C\xF8\x61\xCA\x33\xBF\x28\x91"
+ "\x1D\x86\xEF\x58\xE4\x4D\xB6\x1F"
+ "\xAB\x14\x7D\x09\x72\xDB\x44\xD0"
+ "\x39\xA2\x0B\x97\x00\x69\xF5\x5E"
+ "\xC7\x30\xBC\x25\x8E\x1A\x83\xEC"
+ "\x55\xE1\x4A\xB3\x1C\xA8\x11\x7A"
+ "\x06\x6F\xD8\x41\xCD\x36\x9F\x08"
+ "\x94\xFD\x66\xF2\x5B\xC4\x2D\xB9"
+ "\x22\x8B\x17\x80\xE9\x52\xDE\x47"
+ "\xB0\x19\xA5\x0E\x77\x03\x6C\xD5"
+ "\x3E\xCA\x33\x9C\x05\x91\xFA\x63"
+ "\xEF\x58\xC1\x2A\xB6\x1F\x88\x14"
+ "\x7D\xE6\x4F\xDB\x44\xAD\x16\xA2"
+ "\x0B\x74\x00\x69\xD2\x3B\xC7\x30"
+ "\x99\x02\x8E\xF7\x60\xEC\x55\xBE"
+ "\x27\xB3\x1C\x85\x11\x7A\xE3\x4C"
+ "\xD8\x41\xAA\x13\x9F\x08\x71\xFD"
+ "\x66\xCF\x38\xC4\x2D\x96\x22\x8B"
+ "\xF4\x5D\xE9\x52\xBB\x24\xB0\x19"
+ "\x82\x0E\x77\xE0\x49\xD5\x3E\xA7"
+ "\x10\x9C\x05\x6E\xFA\x63\xCC\x35"
+ "\xC1\x2A\x93\x1F\x88\xF1\x5A\xE6"
+ "\x4F\xB8\x21\xAD\x16\x7F\x0B\x74"
+ "\xDD\x46\xD2\x3B\xA4\x0D\x99\x02"
+ "\x6B\xF7\x60\xC9\x32\xBE\x27\x90"
+ "\x1C\x85\xEE\x57\xE3\x4C\xB5\x1E"
+ "\xAA\x13\x7C\x08\x71\xDA\x43\xCF"
+ "\x38\xA1\x0A\x96\xFF\x68\xF4\x5D"
+ "\xC6\x2F\xBB\x24\x8D\x19\x82\xEB"
+ "\x54\xE0\x49\xB2\x1B\xA7\x10\x79"
+ "\x05\x6E\xD7\x40\xCC\x35\x9E\x07"
+ "\x93\xFC\x65\xF1\x5A\xC3\x2C\xB8"
+ "\x21\x8A\x16\x7F\xE8\x51\xDD\x46"
+ "\xAF\x18\xA4\x0D\x76\x02\x6B\xD4"
+ "\x3D\xC9\x32\x9B\x04\x90\xF9\x62"
+ "\xEE\x57\xC0\x29\xB5\x1E\x87\x13"
+ "\x7C\xE5\x4E\xDA\x43\xAC\x15\xA1"
+ "\x0A\x73\xFF\x68\xD1\x3A\xC6\x2F"
+ "\x98\x01\x8D\xF6\x5F\xEB\x54\xBD"
+ "\x26\xB2\x1B\x84\x10\x79\xE2\x4B"
+ "\xD7\x40\xA9\x12\x9E\x07\x70\xFC"
+ "\x65\xCE\x37\xC3\x2C\x95\x21\x8A"
+ "\xF3\x5C\xE8\x51\xBA\x23\xAF\x18"
+ "\x81\x0D\x76\xDF\x48\xD4\x3D\xA6"
+ "\x0F\x9B\x04\x6D\xF9\x62\xCB\x34"
+ "\xC0\x29\x92\x1E\x87\xF0\x59\xE5"
+ "\x4E\xB7\x20\xAC\x15\x7E\x0A\x73"
+ "\xDC\x45\xD1\x3A\xA3\x0C\x98\x01"
+ "\x6A\xF6\x5F\xC8\x31\xBD\x26\x8F"
+ "\x1B\x84\xED\x56\xE2\x4B\xB4\x1D"
+ "\xA9\x12\x7B\x07\x70\xD9\x42\xCE"
+ "\x37\xA0\x09\x95\xFE\x67\xF3\x5C"
+ "\xC5\x2E\xBA\x23\x8C\x18\x81\xEA"
+ "\x53\xDF\x48\xB1\x1A\xA6\x0F\x78"
+ "\x04\x6D\xD6\x3F\xCB\x34\x9D\x06"
+ "\x92\xFB\x64\xF0\x59\xC2\x2B\xB7"
+ "\x20\x89\x15\x7E\xE7\x50\xDC\x45"
+ "\xAE\x17\xA3\x0C\x75\x01\x6A\xD3"
+ "\x3C\xC8\x31\x9A\x03\x8F\xF8\x61"
+ "\xED\x56\xBF\x28\xB4\x1D\x86\x12",
+ .ilen = 496,
+ .result = "\xEA\x65\x8A\x19\xB0\x66\xC1\x3F"
+ "\xCE\xF1\x97\x75\xC1\xFD\xB5\xAF"
+ "\x52\x65\xF7\xFF\xBC\xD8\x2D\x9F"
+ "\x2F\xB9\x26\x9B\x6F\x10\xB7\xB8"
+ "\x26\xA1\x02\x46\xA2\xAD\xC6\xC0"
+ "\x11\x15\xFF\x6D\x1E\x82\x04\xA6"
+ "\xB1\x74\xD1\x08\x13\xFD\x90\x7C"
+ "\xF5\xED\xD3\xDB\x5A\x0A\x0C\x2F"
+ "\x0A\x70\xF1\x88\x07\xCF\x21\x26"
+ "\x40\x40\x8A\xF5\x53\xF7\x24\x4F"
+ "\x83\x38\x43\x5F\x08\x99\xEB\xE3"
+ "\xDC\x02\x64\x67\x50\x6E\x15\xC3"
+ "\x01\x1A\xA0\x81\x13\x65\xA6\x73"
+ "\x71\xA6\x3B\x91\x83\x77\xBE\xFA"
+ "\xDB\x71\x73\xA6\xC1\xAE\x43\xC3"
+ "\x36\xCE\xD6\xEB\xF9\x30\x1C\x4F"
+ "\x80\x38\x5E\x9C\x6E\xAB\x98\x2F"
+ "\x53\xAF\xCF\xC8\x9A\xB8\x86\x43"
+ "\x3E\x86\xE7\xA1\xF4\x2F\x30\x40"
+ "\x03\xA8\x6C\x50\x42\x9F\x77\x59"
+ "\x89\xA0\xC5\xEC\x9A\xB8\xDD\x99"
+ "\x16\x24\x02\x07\x48\xAE\xF2\x31"
+ "\x34\x0E\xC3\x85\xFE\x1C\x95\x99"
+ "\x87\x58\x98\x8B\xE7\xC6\xC5\x70"
+ "\x73\x81\x07\x7C\x56\x2F\xD8\x1B"
+ "\xB7\xB9\x2B\xAB\xE3\x01\x87\x0F"
+ "\xD8\xBB\xC0\x0D\xAC\x2C\x2F\x98"
+ "\x3C\x0B\xA2\x99\x4A\x8C\xF7\x04"
+ "\xE0\xE0\xCF\xD1\x81\x5B\xFE\xF5"
+ "\x24\x04\xFD\xB8\xDF\x13\xD8\xCD"
+ "\xF1\xE3\x3D\x98\x50\x02\x77\x9E"
+ "\xBC\x22\xAB\xFA\xC2\x43\x1F\x66"
+ "\x20\x02\x23\xDA\xDF\xA0\x89\xF6"
+ "\xD8\xF3\x45\x24\x53\x6F\x16\x77"
+ "\x02\x3E\x7B\x36\x5F\xA0\x3B\x78"
+ "\x63\xA2\xBD\xB5\xA4\xCA\x1E\xD3"
+ "\x57\xBC\x0B\x9F\x43\x51\x28\x4F"
+ "\x07\x50\x6C\x68\x12\x07\xCF\xFA"
+ "\x6B\x72\x0B\xEB\xF8\x88\x90\x2C"
+ "\x7E\xF5\x91\xD1\x03\xD8\xD5\xBD"
+ "\x22\x39\x7B\x16\x03\x01\x69\xAF"
+ "\x3D\x38\x66\x28\x0C\xBE\x5B\xC5"
+ "\x03\xB4\x2F\x51\x8A\x56\x17\x2B"
+ "\x88\x42\x6D\x40\x68\x8F\xD0\x11"
+ "\x19\xF9\x1F\x43\x79\x95\x31\xFA"
+ "\x28\x7A\x3D\xF7\x66\xEB\xEF\xAC"
+ "\x06\xB2\x01\xAD\xDB\x68\xDB\xEC"
+ "\x8D\x53\x6E\x72\x68\xA3\xC7\x63"
+ "\x43\x2B\x78\xE0\x04\x29\x8F\x72"
+ "\xB2\x2C\xE6\x84\x03\x30\x6D\xCD"
+ "\x26\x92\x37\xE1\x2F\xBB\x8B\x9D"
+ "\xE4\x4C\xF6\x93\xBC\xD9\xAD\x44"
+ "\x52\x65\xC7\xB0\x0E\x3F\x0E\x61"
+ "\x56\x5D\x1C\x6D\xA7\x05\x2E\xBC"
+ "\x58\x08\x15\xAB\x12\xAB\x17\x4A"
+ "\x5E\x1C\xF2\xCD\xB8\xA2\xAE\xFB"
+ "\x9B\x2E\x0E\x85\x34\x80\x0E\x3F"
+ "\x4C\xB8\xDB\xCE\x1C\x90\xA1\x61"
+ "\x6C\x69\x09\x35\x9E\xD4\xF4\xAD"
+ "\xBC\x06\x41\xE3\x01\xB4\x4E\x0A"
+ "\xE0\x1F\x91\xF8\x82\x96\x2D\x65"
+ "\xA3\xAA\x13\xCC\x50\xFF\x7B\x02",
+ .rlen = 496,
+ },
+};
+
+/*
+ * SHA256 test vectors from from NIST
+ */
+#define SHA256_TEST_VECTORS 2
+
+static struct hash_testvec sha256_tv_template[] = {
+ {
+ .plaintext = "abc",
+ .psize = 3,
+ .digest = "\xba\x78\x16\xbf\x8f\x01\xcf\xea"
+ "\x41\x41\x40\xde\x5d\xae\x22\x23"
+ "\xb0\x03\x61\xa3\x96\x17\x7a\x9c"
+ "\xb4\x10\xff\x61\xf2\x00\x15\xad",
+ }, {
+ .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .psize = 56,
+ .digest = "\x24\x8d\x6a\x61\xd2\x06\x38\xb8"
+ "\xe5\xc0\x26\x93\x0c\x3e\x60\x39"
+ "\xa3\x3c\xe4\x59\x64\xff\x21\x67"
+ "\xf6\xec\xed\xd4\x19\xdb\x06\xc1",
+ .np = 2,
+ .tap = { 28, 28 }
+ },
+};
+
+/*
+ * HMAC-SHA256 test vectors from
+ * draft-ietf-ipsec-ciph-sha-256-01.txt
+ */
+#define HMAC_SHA256_TEST_VECTORS 10
+
+static struct hash_testvec hmac_sha256_tv_template[] = {
+ {
+ .key = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20",
+ .ksize = 32,
+ .plaintext = "abc",
+ .psize = 3,
+ .digest = "\xa2\x1b\x1f\x5d\x4c\xf4\xf7\x3a"
+ "\x4d\xd9\x39\x75\x0f\x7a\x06\x6a"
+ "\x7f\x98\xcc\x13\x1c\xb1\x6a\x66"
+ "\x92\x75\x90\x21\xcf\xab\x81\x81",
+ }, {
+ .key = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20",
+ .ksize = 32,
+ .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .psize = 56,
+ .digest = "\x10\x4f\xdc\x12\x57\x32\x8f\x08"
+ "\x18\x4b\xa7\x31\x31\xc5\x3c\xae"
+ "\xe6\x98\xe3\x61\x19\x42\x11\x49"
+ "\xea\x8c\x71\x24\x56\x69\x7d\x30",
+ }, {
+ .key = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20",
+ .ksize = 32,
+ .plaintext = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .psize = 112,
+ .digest = "\x47\x03\x05\xfc\x7e\x40\xfe\x34"
+ "\xd3\xee\xb3\xe7\x73\xd9\x5a\xab"
+ "\x73\xac\xf0\xfd\x06\x04\x47\xa5"
+ "\xeb\x45\x95\xbf\x33\xa9\xd1\xa3",
+ }, {
+ .key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b",
+ .ksize = 32,
+ .plaintext = "Hi There",
+ .psize = 8,
+ .digest = "\x19\x8a\x60\x7e\xb4\x4b\xfb\xc6"
+ "\x99\x03\xa0\xf1\xcf\x2b\xbd\xc5"
+ "\xba\x0a\xa3\xf3\xd9\xae\x3c\x1c"
+ "\x7a\x3b\x16\x96\xa0\xb6\x8c\xf7",
+ }, {
+ .key = "Jefe",
+ .ksize = 4,
+ .plaintext = "what do ya want for nothing?",
+ .psize = 28,
+ .digest = "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e"
+ "\x6a\x04\x24\x26\x08\x95\x75\xc7"
+ "\x5a\x00\x3f\x08\x9d\x27\x39\x83"
+ "\x9d\xec\x58\xb9\x64\xec\x38\x43",
+ .np = 2,
+ .tap = { 14, 14 }
+ }, {
+ .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+ .ksize = 32,
+ .plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+ .psize = 50,
+ .digest = "\xcd\xcb\x12\x20\xd1\xec\xcc\xea"
+ "\x91\xe5\x3a\xba\x30\x92\xf9\x62"
+ "\xe5\x49\xfe\x6c\xe9\xed\x7f\xdc"
+ "\x43\x19\x1f\xbd\xe4\x5c\x30\xb0",
+ }, {
+ .key = "\x01\x02\x03\x04\x05\x06\x07\x08"
+ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+ "\x11\x12\x13\x14\x15\x16\x17\x18"
+ "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+ "\x21\x22\x23\x24\x25",
+ .ksize = 37,
+ .plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+ .psize = 50,
+ .digest = "\xd4\x63\x3c\x17\xf6\xfb\x8d\x74"
+ "\x4c\x66\xde\xe0\xf8\xf0\x74\x55"
+ "\x6e\xc4\xaf\x55\xef\x07\x99\x85"
+ "\x41\x46\x8e\xb4\x9b\xd2\xe9\x17",
+ }, {
+ .key = "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+ "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+ "\x0c\x0c\x0c\x0c\x0c\x0c",
+ .ksize = 32,
+ .plaintext = "Test With Truncation",
+ .psize = 20,
+ .digest = "\x75\x46\xaf\x01\x84\x1f\xc0\x9b"
+ "\x1a\xb9\xc3\x74\x9a\x5f\x1c\x17"
+ "\xd4\xf5\x89\x66\x8a\x58\x7b\x27"
+ "\x00\xa9\xc9\x7c\x11\x93\xcf\x42",
+ }, {
+ .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa",
+ .ksize = 80,
+ .plaintext = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .psize = 54,
+ .digest = "\x69\x53\x02\x5e\xd9\x6f\x0c\x09"
+ "\xf8\x0a\x96\xf7\x8e\x65\x38\xdb"
+ "\xe2\xe7\xb8\x20\xe3\xdd\x97\x0e"
+ "\x7d\xdd\x39\x09\x1b\x32\x35\x2f",
+ }, {
+ .key = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa",
+ .ksize = 80,
+ .plaintext = "Test Using Larger Than Block-Size Key and Larger Than "
+ "One Block-Size Data",
+ .psize = 73,
+ .digest = "\x63\x55\xac\x22\xe8\x90\xd0\xa3"
+ "\xc8\x48\x1a\x5c\xa4\x82\x5b\xc8"
+ "\x84\xd3\xe7\xa1\xff\x98\xa2\xfc"
+ "\x2a\xc7\xd8\xe0\x64\xc3\xb2\xe6",
+ },
+};
+
+struct fmp_test_result {
+ struct completion completion;
+ int err;
+};
+
+static int selftest_sha256(struct exynos_fmp *fmp)
+{
+ int i;
+ int ret;
+ unsigned char buf[SHA256_DIGEST_LENGTH];
+ unsigned char temp_digest[SHA256_DIGEST_LENGTH];
+ struct exynos_fmp_fips_test_vops *test_vops;
+
+ test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops;
+
+ for (i = 0; i < SHA256_TEST_VECTORS; i++) {
+ if (0 != sha256(sha256_tv_template[i].plaintext, sha256_tv_template[i].psize, buf))
+ return -EINVAL;
+
+ memcpy(temp_digest, sha256_tv_template[i].digest, SHA256_DIGEST_LENGTH);
+ if (test_vops) {
+ ret = test_vops->hmac_sha256(temp_digest, "sha256");
+ if (ret) {
+ pr_err("%s: Fail to set digest for func test. ret(%d)\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+ }
+
+ ret = memcmp(buf, temp_digest, SHA256_DIGEST_LENGTH);
+ if (ret) {
+ print_hex_dump_bytes("FIPS SHA256 REQ: ", DUMP_PREFIX_NONE,
+ sha256_tv_template[i].digest,
+ SHA256_DIGEST_LENGTH);
+ print_hex_dump_bytes("FIPS SHA256 RES: ", DUMP_PREFIX_NONE,
+ buf,
+ SHA256_DIGEST_LENGTH);
+ return -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static int selftest_hmac_sha256(struct exynos_fmp *fmp)
+{
+ int i;
+ int ret;
+ unsigned char buf[SHA256_DIGEST_LENGTH];
+ unsigned char temp_digest[SHA256_DIGEST_LENGTH];
+ struct exynos_fmp_fips_test_vops *test_vops;
+
+ test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops;
+
+ for (i = 0; i < HMAC_SHA256_TEST_VECTORS; i++) {
+ if (0 != hmac_sha256(hmac_sha256_tv_template[i].key,
+ hmac_sha256_tv_template[i].ksize,
+ hmac_sha256_tv_template[i].plaintext,
+ hmac_sha256_tv_template[i].psize,
+ buf))
+ return -EINVAL;
+
+ memcpy(temp_digest, hmac_sha256_tv_template[i].digest,
+ SHA256_DIGEST_LENGTH);
+ if (test_vops) {
+ ret =
+ test_vops->hmac_sha256(temp_digest, "hmac-sha256");
+ if (ret) {
+ pr_err("%s: Fail to set digest for func test. ret(%d)\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+ }
+
+ ret = memcmp(buf, temp_digest, SHA256_DIGEST_LENGTH);
+ if (ret) {
+ print_hex_dump_bytes("FIPS HMAC-SHA256 REQ: ",
+ DUMP_PREFIX_NONE,
+ hmac_sha256_tv_template[i].digest,
+ SHA256_DIGEST_LENGTH);
+ print_hex_dump_bytes("FIPS HMAC-SHA256 RES: ",
+ DUMP_PREFIX_NONE,
+ buf, SHA256_DIGEST_LENGTH);
+ return -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static int fmp_test_run(struct exynos_fmp *fmp, struct cipher_testvec *template,
+ const int idx, uint8_t *data, uint32_t len,
+ const int mode, uint32_t write)
+{
+ int ret = 0;
+ char *temp_key;
+ struct exynos_fmp_fips_test_vops *test_vops;
+
+ temp_key = kzalloc(template->klen, GFP_KERNEL);
+ if (!temp_key) {
+ dev_err(fmp->dev, "%s: Fail to alloc key buffer\n", __func__);
+ return -ENOMEM;
+ }
+ memcpy(temp_key, template->key, template->klen);
+
+ test_vops = (struct exynos_fmp_fips_test_vops *)fmp->test_vops;
+ if (test_vops) {
+ ret = test_vops->aes(mode, temp_key, template->klen);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to set key for func test. ret(%d)\n",
+ __func__, ret);
+ goto err;
+ }
+ }
+
+ ret = fmp_cipher_set_key(fmp->test_data, temp_key, template->klen);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to set key. ret(%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = fmp_cipher_set_iv(fmp->test_data, template->iv, 16);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to set iv. ret(%d)\n", __func__, ret);
+ goto err;
+ }
+
+ ret = fmp_cipher_run(fmp, fmp->test_data, data, len,
+ (mode == BYPASS_MODE) ? 1 : 0, write, NULL,
+ &fmp->test_data->ci);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to run. ret(%d)\n", __func__, ret);
+ goto err;
+ }
+err:
+ kzfree(temp_key);
+ return ret;
+}
+
+static int test_cipher(struct exynos_fmp *fmp, int mode,
+ struct cipher_testvec *template, uint32_t tcount)
+{
+ int ret;
+ uint32_t idx;
+ char *inbuf[XBUFSIZE];
+ char *outbuf[XBUFSIZE];
+ void *indata, *outdata;
+
+ if (alloc_buf(inbuf)) {
+ dev_err(fmp->dev, "%s: Fail to alloc input buf.\n", __func__);
+ goto err_alloc_inbuf;
+ }
+
+ if (alloc_buf(outbuf)) {
+ dev_err(fmp->dev, "%s: Fail to alloc output buf.\n", __func__);
+ goto err_alloc_outbuf;
+ }
+
+ for (idx = 0; idx < tcount; idx++) {
+ if (template[idx].np)
+ continue;
+
+ if (WARN_ON(template[idx].ilen > PAGE_SIZE)) {
+ dev_err(fmp->dev, "%s: Invalid input length. ilen (%d)\n",
+ __func__, template[idx].ilen);
+ goto err_ilen;
+ }
+
+ indata = inbuf[0];
+ outdata = outbuf[0];
+ memset(indata, 0, FMP_BLK_SIZE);
+ memcpy(indata, template[idx].input, template[idx].ilen);
+
+ ret = fmp_test_run(fmp, &template[idx], idx, indata,
+ template[idx].ilen, mode, WRITE_MODE);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to run fmp encrypt write. ret(%d)\n",
+ __func__, ret);
+ goto err_aes_run;
+ }
+
+ memset(outdata, 0, FMP_BLK_SIZE);
+ ret = fmp_test_run(fmp, &template[idx], idx, outdata,
+ template[idx].ilen, BYPASS_MODE, READ_MODE);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to run fmp bypass read. ret(%d)\n",
+ __func__, ret);
+ goto err_aes_run;
+ }
+
+ if (memcmp(outdata, template[idx].result, template[idx].rlen)) {
+ dev_err(fmp->dev, "%s: Fail to compare encrypted result.\n",
+ __func__);
+ hexdump(indata, template[idx].rlen);
+ hexdump(outdata, template[idx].rlen);
+ goto err_cmp;
+ }
+
+ ret = fmp_test_run(fmp, &template[idx], idx, outdata,
+ template[idx].ilen, BYPASS_MODE, WRITE_MODE);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to run fmp bypass write. ret(%d)\n",
+ __func__, ret);
+ goto err_aes_run;
+ }
+
+ memset(indata, 0, FMP_BLK_SIZE);
+ ret = fmp_test_run(fmp, &template[idx], idx, indata,
+ template[idx].ilen, mode, READ_MODE);
+ if (ret) {
+ dev_err(fmp->dev, "%s: Fail to run fmp decrypt read. ret(%d)\n",
+ __func__, ret);
+ goto err_aes_run;
+ }
+
+ if (memcmp(indata, template[idx].input, template[idx].rlen)) {
+ dev_err(fmp->dev, "%s: Fail to compare decrypted result.\n",
+ __func__);
+ hexdump(outdata, template[idx].rlen);
+ goto err_cmp;
+ }
+ }
+
+ free_buf(inbuf);
+ free_buf(outbuf);
+
+ return 0;
+
+err_ilen:
+err_aes_run:
+err_cmp:
+ free_buf(outbuf);
+err_alloc_outbuf:
+ free_buf(inbuf);
+err_alloc_inbuf:
+
+ return -1;
+}
+
+int do_fmp_selftest(struct exynos_fmp *fmp)
+{
+ int ret;
+ struct cipher_test_suite xts_cipher;
+ struct cipher_test_suite cbc_cipher;
+
+ if (!fmp || !fmp->dev) {
+ pr_err("%s: Invalid exynos fmp dev\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Self test for AES XTS mode */
+ fmp->test_data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_XTS;
+ xts_cipher.enc.vecs = aes_xts_enc_tv_template;
+ xts_cipher.enc.count = AES_XTS_ENC_TEST_VECTORS;
+ ret = test_cipher(fmp, XTS_MODE, xts_cipher.enc.vecs, xts_cipher.enc.count);
+ if (ret) {
+ dev_err(fmp->dev, "FIPS: self-tests for FMP aes-xts failed\n");
+ fmp->result.aes_xts = 0;
+ goto err_xts_cipher;
+ }
+ dev_info(fmp->dev, "FIPS: self-tests for FMP aes-xts passed\n");
+ fmp->result.aes_xts = 1;
+
+ /* Self test for AES CBC mode */
+ fmp->test_data->ci.algo_mode = EXYNOS_FMP_ALGO_MODE_AES_CBC;
+ cbc_cipher.enc.vecs = aes_cbc_enc_tv_template;
+ cbc_cipher.enc.count = AES_CBC_ENC_TEST_VECTORS;
+ ret = test_cipher(fmp, CBC_MODE, cbc_cipher.enc.vecs, cbc_cipher.enc.count);
+ if (ret) {
+ dev_err(fmp->dev, "FIPS: self-tests for FMP aes-cbc failed\n");
+ fmp->result.aes_cbc = 0;
+ goto err_cbc_cipher;
+ }
+ dev_info(fmp->dev, "FIPS: self-tests for FMP aes-cbc passed\n");
+ fmp->result.aes_cbc = 1;
+
+ ret = selftest_sha256(fmp);
+ if (ret) {
+ dev_err(fmp->dev, "FIPS: self-tests for UFSFMP %s failed\n", ALG_SHA256_FMP);
+ fmp->result.sha256 = 0;
+ goto err_xts_cipher;
+ }
+ dev_info(fmp->dev, "FIPS: self-tests for FMP %s passed\n", ALG_SHA256_FMP);
+ fmp->result.sha256 = 1;
+
+ ret = selftest_hmac_sha256(fmp);
+ if (ret) {
+ dev_err(fmp->dev, "FIPS: self-tests for UFSFMP %s failed\n", ALG_HMAC_SHA256_FMP);
+ fmp->result.hmac = 0;
+ goto err_xts_cipher;
+ }
+ dev_info(fmp->dev, "FIPS: self-tests for UFSFMP %s passed\n", ALG_HMAC_SHA256_FMP);
+ fmp->result.hmac = 1;
+
+ return 0;
+
+err_cbc_cipher:
+err_xts_cipher:
+ return -1;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _FMP_SELFTEST_H_
+int do_fmp_selftest(struct exynos_fmp *fmp);
+#endif
--- /dev/null
+/*
+ * 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/of.h>
+#include <linux/of_platform.h>
+#include <linux/crypto.h>
+#include <linux/buffer_head.h>
+#include <linux/genhd.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/blk_types.h>
+#include <crypto/fmp.h>
+
+#include "fmp_test.h"
+
+#define MAX_SCAN_PART (50)
+#define MAX_RETRY_COUNT (0x100000)
+
+static dev_t find_devt_for_test(struct exynos_fmp *fmp,
+ struct fmp_test_data *data)
+{
+ int i, idx = 0;
+ uint32_t count = 0;
+ uint64_t size;
+ uint64_t size_list[MAX_SCAN_PART];
+ dev_t devt_list[MAX_SCAN_PART];
+ dev_t devt_scan, devt;
+ struct block_device *bdev;
+ struct device *dev = fmp->dev;
+ fmode_t fmode = FMODE_WRITE | FMODE_READ;
+
+ memset(size_list, 0, sizeof(size_list));
+ memset(devt_list, 0, sizeof(devt_list));
+ if (!data || !data->block_type) {
+ dev_err(dev, "Invalid fmp test data\n");
+ return (dev_t) 0;
+ }
+
+ do {
+ for (i = 1; i < MAX_SCAN_PART; i++) {
+ devt_scan = blk_lookup_devt(data->block_type, i);
+ bdev = blkdev_get_by_dev(devt_scan, fmode, NULL);
+ if (IS_ERR(bdev))
+ continue;
+ else {
+ size_list[idx] =
+ (uint64_t) i_size_read(bdev->bd_inode);
+ devt_list[idx++] = devt_scan;
+ blkdev_put(bdev, fmode);
+ }
+ }
+ if (!idx) {
+ mdelay(100);
+ count++;
+ continue;
+ }
+ for (i = 0; i < idx; i++) {
+ if (i == 0) {
+ size = size_list[i];
+ devt = devt_list[i];
+ } else {
+ if (size < size_list[i])
+ devt = devt_list[i];
+ }
+ }
+ bdev = blkdev_get_by_dev(devt, fmode, NULL);
+ dev_dbg(dev, "Found partno %d for FMP test\n",
+ bdev->bd_part->partno);
+ blkdev_put(bdev, fmode);
+ return devt;
+ } while (count < MAX_RETRY_COUNT);
+ dev_err(dev, "Block device isn't initialized yet for FMP test\n");
+ return (dev_t) 0;
+}
+
+static int get_fmp_host_type(struct device *dev,
+ struct fmp_test_data *data)
+{
+ int ret;
+ struct device_node *node = dev->of_node;
+ const char *type;
+
+ ret =
+ of_property_read_string_index(node, "exynos,block-type", 0, &type);
+ if (ret) {
+ pr_err("%s: Could not get block type\n", __func__);
+ return ret;
+ }
+ strlcpy(data->block_type, type, FMP_BLOCK_TYPE_NAME_LEN);
+ return 0;
+}
+
+static int get_fmp_test_block_offset(struct device *dev,
+ struct fmp_test_data *data)
+{
+ int ret = 0;
+ struct device_node *node = dev->of_node;
+ uint32_t offset;
+
+ ret = of_property_read_u32(node, "exynos,fips-block_offset", &offset);
+ if (ret) {
+ pr_err("%s: Could not get fips test block offset\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ data->test_block_offset = offset;
+err:
+ return ret;
+}
+
+/* test block device init for fmp test */
+struct fmp_test_data *fmp_test_init(struct exynos_fmp *fmp)
+{
+ int ret = 0;
+ struct fmp_test_data *data;
+ struct device *dev = fmp->dev;
+ struct inode *inode;
+ struct super_block *sb;
+ unsigned long blocksize;
+ unsigned char blocksize_bits;
+ fmode_t fmode = FMODE_WRITE | FMODE_READ;
+
+ if (!fmp) {
+ dev_err(dev, "%s: Invalid exynos fmp struct\n", __func__);
+ return NULL;
+ }
+ data = kmalloc(sizeof(struct fmp_test_data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ ret = get_fmp_host_type(dev, data);
+ if (ret) {
+ dev_err(dev, "%s: Fail to get host type. ret(%d)", __func__,
+ ret);
+ goto err;
+ }
+ data->devt = find_devt_for_test(fmp, data);
+ if (!data->devt) {
+ dev_err(dev, "%s: Fail to find devt for self test\n", __func__);
+ goto err;
+ }
+ data->bdev = blkdev_get_by_dev(data->devt, fmode, NULL);
+ if (IS_ERR(data->bdev)) {
+ dev_err(dev, "%s: Fail to open block device\n", __func__);
+ goto err;
+ }
+ ret = get_fmp_test_block_offset(dev, data);
+ if (ret) {
+ dev_err(dev, "%s: Fail to get fips offset. ret(%d)\n",
+ __func__, ret);
+ goto err;
+ }
+ inode = data->bdev->bd_inode;
+ sb = inode->i_sb;
+ blocksize = sb->s_blocksize;
+ blocksize_bits = sb->s_blocksize_bits;
+ data->sector =
+ (i_size_read(inode) -
+ (blocksize * data->test_block_offset)) >> blocksize_bits;
+
+ return data;
+err:
+ kzfree(data);
+ return NULL;
+}
+
+int fmp_cipher_run(struct exynos_fmp *fmp, struct fmp_test_data *fdata,
+ uint8_t *data, uint32_t len, bool bypass, uint32_t write,
+ void *priv, struct fmp_crypto_info *ci)
+{
+ int ret = 0;
+ struct device *dev = fmp->dev;
+ static struct buffer_head *bh;
+ u32 org_algo_mode;
+ int op_flags;
+
+ if (!fmp || !fdata || !ci)
+ return -EINVAL;
+
+ bh = __getblk(fdata->bdev, fdata->sector, FMP_BLK_SIZE);
+ if (!bh) {
+ dev_err(dev, "%s: Fail to get block from bdev\n", __func__);
+ return -ENODEV;
+ }
+
+ /* set algo_mode for test */
+ org_algo_mode = ci->algo_mode;
+ if (bypass)
+ ci->algo_mode = EXYNOS_FMP_BYPASS_MODE;
+ ci->algo_mode |= EXYNOS_FMP_ALGO_MODE_TEST;
+
+ get_bh(bh);
+ /* priv is diskc for crypto test. */
+ if (!priv) {
+ /* ci is fmp_test_data->ci */
+ fmp->test_data = fdata;
+ ci->ctx = fmp;
+ ci->use_diskc = 0;
+ ci->enc_mode = EXYNOS_FMP_FILE_ENC;
+ bh->b_private = ci;
+ } else {
+ /* ci is crypto_tfm_ctx(tfm) */
+ bh->b_private = priv;
+ }
+ op_flags = REQ_CRYPT | REQ_AUX_PRIV;
+
+ if (write == WRITE_MODE) {
+ memcpy(bh->b_data, data, len);
+ set_buffer_dirty(bh);
+ ret = __sync_dirty_buffer(bh, op_flags | REQ_SYNC);
+ if (ret) {
+ 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(REQ_OP_READ, REQ_SYNC | REQ_PRIO | op_flags, bh);
+ wait_on_buffer(bh);
+ if (unlikely(!buffer_uptodate(bh))) {
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(data, bh->b_data, len);
+ }
+out:
+ if (ci)
+ ci->algo_mode = org_algo_mode;
+ put_bh(bh);
+ return ret;
+}
+
+int fmp_test_crypt(struct exynos_fmp *fmp, struct fmp_test_data *fdata,
+ uint8_t *src, uint8_t *dst, uint32_t len, uint32_t enc,
+ void *priv, struct fmp_crypto_info *ci)
+{
+ int ret = 0;
+
+ if (!fdata) {
+ pr_err("%s: Invalid exynos fmp struct\n", __func__);
+ return -1;
+ }
+
+ if (enc == ENCRYPT) {
+ ret = fmp_cipher_run(fmp, fdata, src, len, 0,
+ WRITE_MODE, priv, ci);
+ if (ret) {
+ pr_err("Fail to run fmp cipher ret(%d)\n",
+ ret);
+ goto err;
+ }
+ ret = fmp_cipher_run(fmp, fdata, dst, len, 1,
+ READ_MODE, priv, ci);
+ if (ret) {
+ pr_err("Fail to run fmp cipher ret(%d)\n",
+ ret);
+ goto err;
+ }
+ } else if (enc == DECRYPT) {
+ ret = fmp_cipher_run(fmp, fdata, src, len, 1,
+ WRITE_MODE, priv, ci);
+ if (ret) {
+ pr_err("Fail to run fmp cipher ret(%d)\n",
+ ret);
+ goto err;
+ }
+ ret = fmp_cipher_run(fmp, fdata, dst, len, 0,
+ READ_MODE, priv, ci);
+ if (ret) {
+ pr_err("Fail to run fmp cipher ret(%d)\n",
+ ret);
+ goto err;
+ }
+ } else {
+ pr_err("%s: Invalid enc %d mode\n", __func__, enc);
+ goto err;
+ }
+
+ return 0;
+err:
+ return -EINVAL;
+}
+
+/* test block device release for fmp test */
+void fmp_test_exit(struct fmp_test_data *fdata)
+{
+ fmode_t fmode = FMODE_WRITE | FMODE_READ;
+
+ if (!fdata)
+ return;
+ if (fdata->bdev)
+ blkdev_put(fdata->bdev, fmode);
+ kzfree(fdata);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _FMP_TEST_H_
+#define _FMP_TEST_H_
+
+#define FMP_BLK_SIZE (4096)
+
+#define WRITE_MODE 1
+#define READ_MODE 2
+
+#define ENCRYPT 1
+#define DECRYPT 2
+
+struct fmp_test_data *fmp_test_init(struct exynos_fmp *fmp);
+int fmp_cipher_run(struct exynos_fmp *fmp, struct fmp_test_data *fdata,
+ uint8_t *data, uint32_t len, bool bypass, uint32_t write,
+ void *priv, struct fmp_crypto_info *ci);
+int fmp_test_crypt(struct exynos_fmp *fmp, struct fmp_test_data *fdata,
+ uint8_t *src, uint8_t *dst, uint32_t len, uint32_t enc,
+ void *priv, struct fmp_crypto_info *ci);
+void fmp_test_exit(struct fmp_test_data *fdata);
+#endif /* _FMP_TEST_H_ */
--- /dev/null
+/*
+ * Cryptographic API.
+ *
+ * HMAC: Keyed-Hashing for Message Authentication (RFC2104).
+ * SHA-256 is used as an underlying hash function.
+ *
+ * Author : Igor Shcheglakov (i.shcheglako@samsung.com)
+ * Date : 15 Dec 2017
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "hmac-sha256.h"
+#include "sha256.h"
+
+int hmac_sha256_init(struct hmac_sha256_ctx *ctx, const u8 *key, unsigned int key_len)
+{
+ int ret = -1;
+ unsigned int i;
+ u8 key_buf[SHA256_BLOCK_SIZE];
+ unsigned int key_buf_len;
+ u8 inner_pad[SHA256_BLOCK_SIZE];
+ u8 outer_pad[SHA256_BLOCK_SIZE];
+ unsigned int block_size = SHA256_BLOCK_SIZE;
+ unsigned int digest_size = SHA256_DIGEST_SIZE;
+
+ if (!ctx || !key)
+ goto err;
+
+ // long keys are hashed
+ if (key_len > block_size) {
+
+ if (sha256(key, key_len, key_buf))
+ goto err;
+
+ key_buf_len = digest_size;
+ } else {
+ memcpy(key_buf, key, key_len);
+ key_buf_len = key_len;
+ }
+
+ // short keys are padded with zeroes to the right
+ if (key_buf_len != block_size)
+ memset(&key_buf[key_buf_len], 0, sizeof(key_buf) - key_buf_len);
+
+ // inner and outer padding calculation
+ for (i = 0; i < block_size; i++) {
+ inner_pad[i] = 0x36 ^ key_buf[i];
+ outer_pad[i] = 0x5c ^ key_buf[i];
+ }
+
+ // inner hash context preparation
+ if (sha256_init(&ctx->inner_ctx))
+ goto err;
+
+ if (sha256_update(&ctx->inner_ctx, inner_pad, sizeof(inner_pad)))
+ goto err;
+
+ // outer hash context preparation
+ if (sha256_init(&ctx->outer_ctx))
+ goto err;
+
+ if (sha256_update(&ctx->outer_ctx, outer_pad, sizeof(outer_pad)))
+ goto err;
+
+ ret = 0;
+
+err:
+ memset(key_buf, 0, sizeof(key_buf));
+ memset(inner_pad, 0, sizeof(inner_pad));
+ memset(outer_pad, 0, sizeof(outer_pad));
+
+ return ret;
+}
+
+int hmac_sha256_update(struct hmac_sha256_ctx *ctx, const u8 *data, unsigned int data_len)
+{
+ if (!ctx || !data)
+ return 0;
+
+ return sha256_update(&ctx->inner_ctx, data, data_len);
+}
+
+int hmac_sha256_final(struct hmac_sha256_ctx *ctx, u8 *out)
+{
+ int ret = -1;
+ u8 result[SHA256_DIGEST_SIZE];
+
+ if (!ctx || !out)
+ goto err;
+
+ if (sha256_final(&ctx->inner_ctx, result))
+ goto err;
+
+ if (sha256_update(&ctx->outer_ctx, result, sizeof(result)))
+ goto err;
+
+ if (sha256_final(&ctx->outer_ctx, result))
+ goto err;
+
+ memcpy(out, result, sizeof(result));
+
+ ret = 0;
+
+err:
+ return ret;
+}
+
+int hmac_sha256(const u8 *key, unsigned int key_len, const u8 *data, unsigned int data_len, u8 *out)
+{
+ int ret = -1;
+ struct hmac_sha256_ctx ctx;
+
+ if (hmac_sha256_init(&ctx, key, key_len))
+ goto err;
+
+ if (hmac_sha256_update(&ctx, data, data_len))
+ goto err;
+
+ if (hmac_sha256_final(&ctx, out))
+ goto err;
+
+ ret = 0;
+
+err:
+ return ret;
+}
+
+void hmac_sha256_ctx_cleanup(struct hmac_sha256_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(struct hmac_sha256_ctx));
+}
--- /dev/null
+/*
+ * Cryptographic API.
+ *
+ * HMAC: Keyed-Hashing for Message Authentication (RFC2104).
+ * SHA-256 is used as an underlying hash function.
+ *
+ * Author : Igor Shcheglakov (i.shcheglako@samsung.com)
+ * Date : 15 Dec 2017
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#ifndef _FMP_HMAC_SHA256_H
+#define _FMP_HMAC_SHA256_H
+
+#include "sha256.h"
+
+struct hmac_sha256_ctx {
+ struct shash_desc inner_ctx;
+ struct shash_desc outer_ctx;
+};
+
+typedef struct hmac_sha256_ctx HMAC_SHA256_CTX;
+
+/* Initialize hashing context using a key of key_len bytes.
+ *
+ * Return zero on success and negative otherwise
+ */
+int hmac_sha256_init(struct hmac_sha256_ctx *ctx, const u8 *key, unsigned int key_len);
+
+/* Hash data_len bytes of data
+ *
+ * Return zero on success and negative otherwise
+ */
+int hmac_sha256_update(struct hmac_sha256_ctx *ctx, const u8 *data, unsigned int data_len);
+
+/* Add final padding to a hash and write resulting HMAC to out.
+ * There must be at least SHA256_DIGEST_LENGTH bytes of space in out
+ *
+ * Return zero on success and negative otherwise
+ */
+int hmac_sha256_final(struct hmac_sha256_ctx *ctx, u8 *out);
+
+/* Hash data_len bytes of data using key of key_len bytes.
+ * Write resulting HMAC to out. There must be at least SHA256_DIGEST_LENGTH
+ * bytes of space in out
+ *
+ * Return zero on success and negative otherwise
+ */
+int hmac_sha256(const u8 *key, unsigned int key_len, const u8 *data, unsigned int data_len, u8 *out);
+
+/* Cleanup allocated resources */
+void hmac_sha256_ctx_cleanup(struct hmac_sha256_ctx *ctx);
+
+#endif /* _FMP_HMAC_SHA256_H */
+
--- /dev/null
+/*
+ * Last file for Exynos FMP FIPS integrity check
+ *
+ * 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/init.h>
+
+__attribute__ ((section(".rodata"), unused))
+const unsigned char last_fmp_rodata = 0x20;
+
+__attribute__ ((section(".text"), unused))
+void last_fmp_text(void){}
+
+__attribute__ ((section(".init.text"), optimize("-O0"), unused))
+static void last_fmp_init(void){};
--- /dev/null
+/*
+ * Cryptographic API.
+ *
+ * SHA-256, as specified in
+ * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf
+ *
+ * SHA-256 code by Jean-Luc Cooke <jlcooke@certainkey.com>.
+ *
+ * Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com>
+ * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * SHA224 Support Copyright 2007 Intel Corporation <jonathan.lynch@intel.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/types.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include "sha256.h"
+
+#define SHA256_H0 0x6a09e667UL
+#define SHA256_H1 0xbb67ae85UL
+#define SHA256_H2 0x3c6ef372UL
+#define SHA256_H3 0xa54ff53aUL
+#define SHA256_H4 0x510e527fUL
+#define SHA256_H5 0x9b05688cUL
+#define SHA256_H6 0x1f83d9abUL
+#define SHA256_H7 0x5be0cd19UL
+
+#define e0(x) (ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22))
+#define e1(x) (ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25))
+#define s0(x) (ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3))
+#define s1(x) (ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10))
+
+static const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE] = {
+ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+ 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+ 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
+};
+
+static inline u32 Ch(u32 x, u32 y, u32 z)
+{
+ return z ^ (x & (y ^ z));
+}
+
+static inline u32 Maj(u32 x, u32 y, u32 z)
+{
+ return (x & y) | (z & (x | y));
+}
+
+static inline void *shash_desc_ctx(struct shash_desc *desc)
+{
+ return &desc->__ctx;
+}
+
+static inline void LOAD_OP(int I, u32 *W, const u8 *input)
+{
+ W[I] = get_unaligned_be32((__u32 *) input + I);
+}
+
+static inline void BLEND_OP(int I, u32 *W)
+{
+ W[I] = s1(W[I - 2]) + W[I - 7] + s0(W[I - 15]) + W[I - 16];
+}
+
+static void sha256_transform(u32 *state, const u8 *input)
+{
+ u32 a, b, c, d, e, f, g, h, t1, t2;
+ u32 W[64];
+ int i;
+
+ /* load the input */
+ for (i = 0; i < 16; i++)
+ LOAD_OP(i, W, input);
+
+ /* now blend */
+ for (i = 16; i < 64; i++)
+ BLEND_OP(i, W);
+
+ /* load the state into our registers */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ f = state[5];
+ g = state[6];
+ h = state[7];
+
+ /* now iterate */
+ t1 = h + e1(e) + Ch(e, f, g) + 0x428a2f98 + W[0];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0x71374491 + W[1];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0xb5c0fbcf + W[2];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0xe9b5dba5 + W[3];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0x3956c25b + W[4];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0x59f111f1 + W[5];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0x923f82a4 + W[6];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0xab1c5ed5 + W[7];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0xd807aa98 + W[8];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0x12835b01 + W[9];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0x243185be + W[10];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0x550c7dc3 + W[11];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0x72be5d74 + W[12];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0x80deb1fe + W[13];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0x9bdc06a7 + W[14];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0xc19bf174 + W[15];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0xe49b69c1 + W[16];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0xefbe4786 + W[17];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0x0fc19dc6 + W[18];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0x240ca1cc + W[19];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0x2de92c6f + W[20];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0x4a7484aa + W[21];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0x5cb0a9dc + W[22];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0x76f988da + W[23];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0x983e5152 + W[24];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0xa831c66d + W[25];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0xb00327c8 + W[26];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0xbf597fc7 + W[27];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0xc6e00bf3 + W[28];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0xd5a79147 + W[29];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0x06ca6351 + W[30];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0x14292967 + W[31];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0x27b70a85 + W[32];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0x2e1b2138 + W[33];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0x4d2c6dfc + W[34];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0x53380d13 + W[35];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0x650a7354 + W[36];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0x766a0abb + W[37];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0x81c2c92e + W[38];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0x92722c85 + W[39];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0xa2bfe8a1 + W[40];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0xa81a664b + W[41];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0xc24b8b70 + W[42];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0xc76c51a3 + W[43];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0xd192e819 + W[44];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0xd6990624 + W[45];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0xf40e3585 + W[46];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0x106aa070 + W[47];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0x19a4c116 + W[48];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0x1e376c08 + W[49];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0x2748774c + W[50];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0x34b0bcb5 + W[51];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0x391c0cb3 + W[52];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0x4ed8aa4a + W[53];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0x5b9cca4f + W[54];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0x682e6ff3 + W[55];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ t1 = h + e1(e) + Ch(e, f, g) + 0x748f82ee + W[56];
+ t2 = e0(a) + Maj(a, b, c);
+ d += t1;
+ h = t1 + t2;
+ t1 = g + e1(d) + Ch(d, e, f) + 0x78a5636f + W[57];
+ t2 = e0(h) + Maj(h, a, b);
+ c += t1;
+ g = t1 + t2;
+ t1 = f + e1(c) + Ch(c, d, e) + 0x84c87814 + W[58];
+ t2 = e0(g) + Maj(g, h, a);
+ b += t1;
+ f = t1 + t2;
+ t1 = e + e1(b) + Ch(b, c, d) + 0x8cc70208 + W[59];
+ t2 = e0(f) + Maj(f, g, h);
+ a += t1;
+ e = t1 + t2;
+ t1 = d + e1(a) + Ch(a, b, c) + 0x90befffa + W[60];
+ t2 = e0(e) + Maj(e, f, g);
+ h += t1;
+ d = t1 + t2;
+ t1 = c + e1(h) + Ch(h, a, b) + 0xa4506ceb + W[61];
+ t2 = e0(d) + Maj(d, e, f);
+ g += t1;
+ c = t1 + t2;
+ t1 = b + e1(g) + Ch(g, h, a) + 0xbef9a3f7 + W[62];
+ t2 = e0(c) + Maj(c, d, e);
+ f += t1;
+ b = t1 + t2;
+ t1 = a + e1(f) + Ch(f, g, h) + 0xc67178f2 + W[63];
+ t2 = e0(b) + Maj(b, c, d);
+ e += t1;
+ a = t1 + t2;
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ state[5] += f;
+ state[6] += g;
+ state[7] += h;
+
+ /* clear any sensitive info... */
+ a = b = c = d = e = f = g = h = t1 = t2 = 0;
+ memzero_explicit(W, 64 * sizeof(u32));
+}
+
+static void sha256_generic_block_fn(struct sha256_state *sst, u8 const *src,
+ int blocks)
+{
+ while (blocks--) {
+ sha256_transform(sst->state, src);
+ src += SHA256_BLOCK_SIZE;
+ }
+}
+
+static int sha256_base_do_update(struct shash_desc *desc,
+ const u8 *data,
+ unsigned int len, sha256_block_fn *block_fn)
+{
+ struct sha256_state *sctx = shash_desc_ctx(desc);
+ unsigned int partial = sctx->count % SHA256_BLOCK_SIZE;
+
+ sctx->count += len;
+
+ if (unlikely((partial + len) >= SHA256_BLOCK_SIZE)) {
+ int blocks;
+
+ if (partial) {
+ int p = SHA256_BLOCK_SIZE - partial;
+
+ memcpy(sctx->buf + partial, data, p);
+ data += p;
+ len -= p;
+
+ block_fn(sctx, sctx->buf, 1);
+ }
+
+ blocks = len / SHA256_BLOCK_SIZE;
+ len %= SHA256_BLOCK_SIZE;
+
+ if (blocks) {
+ block_fn(sctx, data, blocks);
+ data += blocks * SHA256_BLOCK_SIZE;
+ }
+ partial = 0;
+ }
+ if (len)
+ memcpy(sctx->buf + partial, data, len);
+
+ return 0;
+}
+
+static int sha256_base_do_finalize(struct shash_desc *desc,
+ sha256_block_fn *block_fn)
+{
+ const int bit_offset = SHA256_BLOCK_SIZE - sizeof(__be64);
+ struct sha256_state *sctx = shash_desc_ctx(desc);
+ __be64 *bits = (__be64 *) (sctx->buf + bit_offset);
+ unsigned int partial = sctx->count % SHA256_BLOCK_SIZE;
+
+ sctx->buf[partial++] = 0x80;
+ if (partial > bit_offset) {
+ memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial);
+ partial = 0;
+
+ block_fn(sctx, sctx->buf, 1);
+ }
+
+ memset(sctx->buf + partial, 0x0, bit_offset - partial);
+ *bits = cpu_to_be64(sctx->count << 3);
+ block_fn(sctx, sctx->buf, 1);
+
+ return 0;
+}
+
+static inline int sha256_base_finish(struct shash_desc *desc, u8 *out)
+{
+ unsigned int digest_size = SHA256_DIGEST_SIZE;
+ struct sha256_state *sctx = shash_desc_ctx(desc);
+ __be32 *digest = (__be32 *) out;
+ int i;
+
+ for (i = 0; digest_size > 0; i++, digest_size -= sizeof(__be32))
+ put_unaligned_be32(sctx->state[i], digest++);
+
+ *sctx = (struct sha256_state) {
+ };
+ return 0;
+}
+
+int sha256_init(struct shash_desc *desc)
+{
+ struct sha256_state *sctx = NULL;
+
+ if (!desc)
+ return -EINVAL;
+
+ sctx = shash_desc_ctx(desc);
+
+ sctx->state[0] = SHA256_H0;
+ sctx->state[1] = SHA256_H1;
+ sctx->state[2] = SHA256_H2;
+ sctx->state[3] = SHA256_H3;
+ sctx->state[4] = SHA256_H4;
+ sctx->state[5] = SHA256_H5;
+ sctx->state[6] = SHA256_H6;
+ sctx->state[7] = SHA256_H7;
+ sctx->count = 0;
+
+ return 0;
+}
+
+int sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len)
+{
+ if (!desc || !data)
+ return -EINVAL;
+
+ return sha256_base_do_update(desc, data, len, sha256_generic_block_fn);
+}
+
+int sha256_final(struct shash_desc *desc, u8 *out)
+{
+ if (!desc || !out)
+ return -EINVAL;
+
+ sha256_base_do_finalize(desc, sha256_generic_block_fn);
+ return sha256_base_finish(desc, out);
+}
+
+int sha256_finup(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *hash)
+{
+ if (!desc || !data || !hash)
+ return -EINVAL;
+
+ sha256_base_do_update(desc, data, len, sha256_generic_block_fn);
+ return sha256_final(desc, hash);
+}
+
+int sha256_desc_copy(struct shash_desc *dst, const struct shash_desc *src)
+{
+ if (!dst || !src)
+ return -EINVAL;
+
+ memcpy(dst, src, sizeof(struct shash_desc));
+ return 0;
+}
+
+int sha256(const u8 *data, unsigned int len, u8 *out)
+{
+ struct shash_desc ctx;
+ int ret = -EINVAL;
+
+ if (!out || !data)
+ return -EINVAL;
+
+ ret = sha256_init(&ctx);
+ if (ret != 0)
+ goto exit_error;
+
+ ret = sha256_update(&ctx, data, len);
+ if (ret != 0)
+ goto exit_error;
+
+ ret = sha256_final(&ctx, out);
+ if (ret != 0)
+ goto exit_error;
+
+ memzero_explicit(&ctx, sizeof(ctx));
+ return 0;
+
+exit_error:
+ return ret;
+}
--- /dev/null
+/*
+ * Cryptographic API.
+ *
+ * SHA-256, as specified in
+ * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf
+ *
+ * SHA-256 code by Jean-Luc Cooke <jlcooke@certainkey.com>.
+ *
+ * Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com>
+ * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * SHA224 Support Copyright 2007 Intel Corporation <jonathan.lynch@intel.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.
+ *
+ */
+
+#ifndef SHA256_FMP_H
+#define SHA256_FMP_H
+
+#include <linux/types.h>
+
+#define SHA256_DIGEST_SIZE 32
+#define SHA256_BLOCK_SIZE 64
+#define SHA256_DIGEST_LENGTH SHA256_DIGEST_SIZE
+
+struct sha256_state {
+ u32 state[SHA256_DIGEST_SIZE / 4];
+ u64 count;
+ u8 buf[SHA256_BLOCK_SIZE];
+};
+
+struct shash_desc {
+ struct sha256_state __ctx;
+};
+
+typedef struct shash_desc SHA256_CTX;
+
+typedef void (sha256_block_fn)(struct sha256_state *sst, u8 const *src,
+ int blocks);
+
+/* Initialises desc
+ * Returns 0 at success, nonzero otherwise
+ */
+int sha256_init(struct shash_desc *desc);
+
+/* Adds len bytes from data to desc.
+ * Returns 0 at success, nonzero otherwise
+ */
+int sha256_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len);
+
+/* Adds the final padding to desc and writes the resulting digest
+ * to out, which must have at least SHA256_DIGEST_SIZE bytes of space.
+ * Returns 0 at success, nonzero otherwise
+ */
+int sha256_final(struct shash_desc *desc, u8 *out);
+
+/* Writes the digest of len bytes from data to out. Returns out.
+ * Out should be preallocated at least SHA256_DIGEST_SIZE length.
+ * Returns 0 at success, nonzero otherwise
+ */
+int sha256(const u8 *data, unsigned int len, u8 *out);
+
+/* desc dup
+ * Returns 0 at success, nonzero otherwise
+ */
+int sha256_desc_copy(struct shash_desc *dst, const struct shash_desc *src);
+
+#endif /* SHA256_FMP_H */
--- /dev/null
+/*
+ * UFS SMU (Secure Management Unit) driver for Exynos
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/smc.h>
+#include <linux/list.h>
+
+#include <crypto/smu.h>
+
+static LIST_HEAD(smu_devices);
+
+struct exynos_smu {
+ struct list_head list;
+ int id;
+ int command;
+ int desc_type;
+ struct device *dev;
+};
+
+static int exynos_smu_init(struct platform_device *pdev,
+ struct smu_data_setting *smu_set)
+{
+ int ret = 0;
+ struct exynos_smu *smu = NULL;
+
+ if (!pdev || !smu_set) {
+ pr_err("%s: Invalid input parameter.\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ smu = platform_get_drvdata(pdev);
+ ret = exynos_smc(SMC_CMD_SMU, smu_set->command, smu_set->id, 0);
+ if (ret)
+ dev_err(smu->dev, "%s: Fail smc call for SMU init. ret(%d)\n",
+ __func__, ret);
+err:
+ return ret;
+}
+
+static int exynos_smu_sec_config(struct platform_device *pdev,
+ struct smu_data_setting *smu_set)
+{
+ int ret = 0;
+ struct exynos_smu *smu = NULL;
+
+ if (!pdev || !smu_set) {
+ pr_err("%s: Invalid input parameter.\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ smu = platform_get_drvdata(pdev);
+ ret = exynos_smc(SMC_CMD_FMP_SECURITY, 0, smu_set->id, smu_set->desc_type);
+ if (ret)
+ dev_err(smu->dev, "%s: Fail smc for FMPSECURITY config. ret(%d)\n",
+ __func__, ret);
+err:
+ return ret;
+}
+
+static int exynos_smu_resume(struct platform_device *pdev,
+ struct smu_data_setting *smu_set)
+{
+ int ret = 0;
+ struct exynos_smu *smu = NULL;
+
+ if (!pdev || !smu_set) {
+ pr_err("%s: Invalid input parameter.\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ smu = platform_get_drvdata(pdev);
+ ret = exynos_smc(SMC_CMD_FMP_SMU_RESUME, 0, smu_set->id, 0);
+ if (ret)
+ dev_err(smu->dev, "%s: Fail smc call for SMU resume. ret(%d)\n",
+ __func__, ret);
+err:
+ return ret;
+}
+
+static int exynos_smu_abort(struct platform_device *pdev,
+ struct smu_data_setting *smu_set)
+{
+ int ret = 0;
+
+ if (!pdev || !smu_set) {
+ pr_err("%s: Invalid input parameter.\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = exynos_smc(SMC_CMD_SMU, smu_set->command, smu_set->id, 0);
+err:
+ return ret;
+}
+
+const struct exynos_smu_variant_ops exynos_smu_ops = {
+ .name = "exynos-smu",
+ .init = exynos_smu_init,
+ .sec_config = exynos_smu_sec_config,
+ .resume = exynos_smu_resume,
+ .abort = exynos_smu_abort,
+};
+
+static const struct of_device_id exynos_smu_match[] = {
+ {
+ .compatible = "samsung,exynos-smu",
+ .data = (void *)&exynos_smu_ops,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_smu_match);
+
+struct platform_device *exynos_smu_get_pdevice(struct device_node *node)
+{
+ struct platform_device *smu_pdev = NULL;
+ struct exynos_smu *smu = NULL;
+
+ if (!node) {
+ pr_err("%s: Invalid node\n", __func__);
+ goto err;
+ }
+
+ if (!of_device_is_available(node)) {
+ pr_err("%s: Unavailable device\n", __func__);
+ goto err;
+ }
+
+ if (list_empty(&smu_devices)) {
+ pr_err("%s: Invalie device list\n", __func__);
+ smu_pdev = ERR_PTR(-EPROBE_DEFER);
+ goto err;
+ }
+
+ list_for_each_entry(smu, &smu_devices, list) {
+ if (smu->dev->of_node == node) {
+ pr_info("%s: Found smu device %p\n", __func__, smu);
+ break;
+ }
+ }
+
+ smu_pdev = to_platform_device(smu->dev);
+ pr_info("%s: Matching platform device %p\n", __func__, smu_pdev);
+err:
+ return smu_pdev;
+}
+EXPORT_SYMBOL(exynos_smu_get_pdevice);
+
+struct exynos_smu_variant_ops *exynos_smu_get_variant_ops(struct device_node *node)
+{
+ if (node) {
+ const struct of_device_id *match;
+
+ match = of_match_node(exynos_smu_match, node);
+ if (match)
+ return (struct exynos_smu_variant_ops *)(match->data);
+ pr_err("%s: Error matching\n", __func__);
+ } else {
+ pr_err("%s: Invalid node\n", __func__);
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(exynos_smu_get_variant_ops);
+
+static int exynos_smu_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct exynos_smu *smu;
+
+ if (!pdev) {
+ pr_err("%s: Invalid platform_device.\n", __func__);
+ ret = -EINVAL;
+ goto err_pdev;
+ }
+
+ smu = kzalloc(sizeof(struct exynos_smu), GFP_KERNEL);
+ if (!smu) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ smu->dev = &pdev->dev;
+ if (!smu->dev) {
+ pr_err("%s: Invalid device.\n", __func__);
+ ret = -EINVAL;
+ goto err_dev;
+ }
+
+ platform_set_drvdata(pdev, smu);
+ list_add_tail(&smu->list, &smu_devices);
+
+ pr_info("%s: Exynos SMU driver is proved\n", __func__);
+ return ret;
+
+err_dev:
+ kfree(smu);
+err_mem:
+err_pdev:
+ return ret;
+}
+
+static int exynos_smu_remove(struct platform_device *pdev)
+{
+ struct exynos_smu *smu;
+
+ smu = (struct exynos_smu *)platform_get_drvdata(pdev);
+ if (!smu)
+ return 0;
+
+ kfree(smu);
+ return 0;
+}
+
+static struct platform_driver exynos_smu_driver = {
+ .driver = {
+ .name = "exynos-smu",
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ .of_match_table = exynos_smu_match,
+ },
+ .probe = exynos_smu_probe,
+ .remove = exynos_smu_remove,
+};
+
+static int __init smu_init(void)
+{
+ return platform_driver_register(&exynos_smu_driver);
+}
+subsys_initcall(smu_init);
+
+static void __exit smu_exit(void)
+{
+ platform_driver_unregister(&exynos_smu_driver);
+}
+module_exit(smu_exit);
+
+MODULE_DESCRIPTION("Exynos Specific SMU(Secure Management Unit) driver");
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _EXYNOS_FMP_H_
+#define _EXYNOS_FMP_H_
+
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+
+#define FMP_DRV_VERSION "1.5.0"
+
+#define FMP_KEY_SIZE_16 16
+#define FMP_KEY_SIZE_32 32
+#define FMP_IV_SIZE_16 16
+
+#define FMP_CBC_MAX_KEY_SIZE FMP_KEY_SIZE_16
+#define FMP_XTS_MAX_KEY_SIZE ((FMP_KEY_SIZE_32) * (2))
+#define FMP_MAX_KEY_SIZE FMP_XTS_MAX_KEY_SIZE
+
+#define FMP_HOST_TYPE_NAME_LEN 8
+#define FMP_BLOCK_TYPE_NAME_LEN 8
+
+#define FMP_SECTOR_SIZE 0x1000
+#define FMP_MIN_SECTOR_SIZE 0x200
+#define NUM_SECTOR_UNIT ((FMP_SECTOR_SIZE)/(FMP_MIN_SECTOR_SIZE))
+
+#define MAX_AES_XTS_TRANSFER_SIZE 0x1000
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+enum fmp_crypto_algo_mode {
+ EXYNOS_FMP_BYPASS_MODE = 0,
+ EXYNOS_FMP_ALGO_MODE_AES_CBC = 1,
+ EXYNOS_FMP_ALGO_MODE_AES_XTS = 2,
+};
+
+enum fmp_crypto_key_size {
+ EXYNOS_FMP_KEY_SIZE_16 = 16,
+ EXYNOS_FMP_KEY_SIZE_32 = 32,
+};
+
+enum fmp_crypto_enc_mode {
+ EXYNOS_FMP_FILE_ENC = 0,
+ EXYNOS_FMP_DISK_ENC = 1, /* use persistent key */
+ EXYNOS_FMP_ENC_MAX
+};
+
+enum fmp_disk_key_status {
+ KEY_STORED = 0,
+ KEY_SET = 1,
+ KEY_CLEAR = 2,
+ KEY_ERROR = -1,
+};
+
+struct fmp_crypto_info {
+ /* This field's stongly aligned 'crypto_diskcipher->algo' */
+ u32 use_diskc;
+ u8 key[FMP_MAX_KEY_SIZE];
+ u32 key_size;
+ enum fmp_crypto_key_size fmp_key_size;
+ enum fmp_crypto_enc_mode enc_mode;
+ enum fmp_crypto_algo_mode algo_mode;
+ void *ctx;
+};
+
+#if defined(CONFIG_MMC_DW_EXYNOS_FMP)
+struct fmp_table_setting {
+ __le32 des0; /* des0 */
+#define GET_CMDQ_LENGTH(d) \
+ (((d)->des0 & 0xffff0000) >> 16)
+ __le32 des1; /* des1 */
+ __le32 des2; /* des2 */
+#define FKL BIT(26)
+#define DKL BIT(27)
+#define SET_KEYLEN(d, v) ((d)->des2 |= (uint32_t)v)
+#define SET_FAS(d, v) \
+ ((d)->des2 = ((d)->des2 & 0xcfffffff) | v << 28)
+#define SET_DAS(d, v) \
+ ((d)->des2 = ((d)->des2 & 0x3fffffff) | v << 30)
+#define GET_FAS(d) ((d)->des2 & 0x30000000)
+#define GET_DAS(d) ((d)->des2 & 0xc0000000)
+#define GET_LENGTH(d) \
+ ((d)->des2 & 0x3ffffff)
+ __le32 des3; /* des3 */
+ /* CMDQ Operation */
+#define FKL_CMDQ BIT(0)
+#define DKL_CMDQ BIT(1)
+#define SET_CMDQ_KEYLEN(d, v) ((d)->des2 |= (uint32_t)v)
+#define SET_CMDQ_FAS(d, v) \
+ ((d)->des3 = ((d)->des3 & 0xfffffff3) | v << 2)
+#define SET_CMDQ_DAS(d, v) \
+ ((d)->des3 = ((d)->des3 & 0xffffffcf) | v << 4)
+#define GET_CMDQ_FAS(d) ((d)->des3 & 0x0000000c)
+#define GET_CMDQ_DAS(d) ((d)->des3 & 0x00000030)
+ __le32 reserved0; /* des4 */
+ __le32 reserved1;
+ __le32 reserved2;
+ __le32 reserved3;
+ __le32 file_iv0; /* des8 */
+ __le32 file_iv1;
+ __le32 file_iv2;
+ __le32 file_iv3;
+ __le32 file_enckey0; /* des12 */
+ __le32 file_enckey1;
+ __le32 file_enckey2;
+ __le32 file_enckey3;
+ __le32 file_enckey4;
+ __le32 file_enckey5;
+ __le32 file_enckey6;
+ __le32 file_enckey7;
+ __le32 file_twkey0; /* des20 */
+ __le32 file_twkey1;
+ __le32 file_twkey2;
+ __le32 file_twkey3;
+ __le32 file_twkey4;
+ __le32 file_twkey5;
+ __le32 file_twkey6;
+ __le32 file_twkey7;
+ __le32 disk_iv0; /* des28 */
+ __le32 disk_iv1;
+ __le32 disk_iv2;
+ __le32 disk_iv3;
+};
+#elif defined(CONFIG_SCSI_UFS_EXYNOS_FMP)
+struct fmp_table_setting {
+ __le32 des0; /* des0 */
+#define GET_CMDQ_LENGTH(d) \
+ (((d)->des0 & 0xffff0000) >> 16)
+ __le32 des1; /* des1 */
+ __le32 des2; /* des2 */
+ __le32 des3; /* des3 */
+/* 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)
+/* CMDQ Operation */
+#define FKL_CMDQ BIT(0)
+#define DKL_CMDQ BIT(1)
+#define SET_CMDQ_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v)
+#define SET_CMDQ_FAS(d, v) \
+ ((d)->des3 = ((d)->des3 & 0xfffffff3) | v << 2)
+#define SET_CMDQ_DAS(d, v) \
+ ((d)->des3 = ((d)->des3 & 0xffffffcf) | v << 4)
+#define GET_CMDQ_FAS(d) ((d)->des3 & 0x0000000c)
+#define GET_CMDQ_DAS(d) ((d)->des3 & 0x00000030)
+ __le32 file_iv0; /* des4 */
+ __le32 file_iv1; /* des5 */
+ __le32 file_iv2; /* des6 */
+ __le32 file_iv3; /* des7 */
+ __le32 file_enckey0; /* des8 */
+ __le32 file_enckey1; /* des9 */
+ __le32 file_enckey2; /* des10 */
+ __le32 file_enckey3; /* des11 */
+ __le32 file_enckey4; /* des12 */
+ __le32 file_enckey5; /* des13 */
+ __le32 file_enckey6; /* des14 */
+ __le32 file_enckey7; /* des15 */
+ __le32 file_twkey0; /* des16 */
+ __le32 file_twkey1; /* des17 */
+ __le32 file_twkey2; /* des18 */
+ __le32 file_twkey3; /* des19 */
+ __le32 file_twkey4; /* des20 */
+ __le32 file_twkey5; /* des21 */
+ __le32 file_twkey6; /* des22 */
+ __le32 file_twkey7; /* des23 */
+ __le32 disk_iv0; /* des24 */
+ __le32 disk_iv1; /* des25 */
+ __le32 disk_iv2; /* des26 */
+ __le32 disk_iv3; /* des27 */
+ __le32 reserved0; /* des28 */
+ __le32 reserved1; /* des29 */
+ __le32 reserved2; /* des30 */
+ __le32 reserved3; /* des31 */
+};
+#endif
+
+struct fmp_data_setting {
+ struct fmp_crypto_info crypt[EXYNOS_FMP_ENC_MAX];
+ struct fmp_table_setting *table;
+ bool cmdq_enabled;
+};
+
+#ifdef CONFIG_EXYNOS_FMP_FIPS
+struct fips_result {
+ bool overall;
+ bool aes_xts;
+ bool aes_cbc;
+ bool sha256;
+ bool hmac;
+ bool integrity;
+};
+#endif
+
+#define EXYNOS_FMP_ALGO_MODE_MASK (0x3)
+#define EXYNOS_FMP_ALGO_MODE_TEST_OFFSET (0xf)
+#define EXYNOS_FMP_ALGO_MODE_TEST (1 << EXYNOS_FMP_ALGO_MODE_TEST_OFFSET)
+
+struct fmp_test_data {
+ char block_type[FMP_BLOCK_TYPE_NAME_LEN];
+ struct block_device *bdev;
+ sector_t sector;
+ dev_t devt;
+ uint32_t test_block_offset;
+ /* iv to submitted */
+ u8 iv[FMP_IV_SIZE_16];
+ /* diskcipher for test */
+ struct fmp_crypto_info ci;
+};
+
+struct exynos_fmp {
+ struct device *dev;
+ enum fmp_disk_key_status status_disk_key;
+ struct fmp_test_data *test_data;
+#ifdef CONFIG_EXYNOS_FMP_FIPS
+ struct fips_result result;
+ struct miscdevice miscdev;
+ void *test_vops;
+#endif
+};
+
+struct fmp_request {
+ void *table;
+ bool cmdq_enabled;
+ void *iv;
+ u32 ivsize;
+};
+
+#if defined(CONFIG_MMC_DW_EXYNOS_FMP) || defined(CONFIG_SCSI_UFS_EXYNOS_FMP)
+static inline void exynos_fmp_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);
+ }
+}
+#endif
+
+int exynos_fmp_crypt(struct fmp_crypto_info *ci, void *priv);
+int exynos_fmp_clear(struct fmp_crypto_info *ci, void *priv);
+int exynos_fmp_setkey(struct fmp_crypto_info *ci,
+ char *in_key, u32 keylen, bool persistent);
+int exynos_fmp_clearkey(struct fmp_crypto_info *ci);
+void *exynos_fmp_init(struct platform_device *pdev);
+void exynos_fmp_exit(struct exynos_fmp *fmp);
+#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
+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);
+#endif
+#endif /* _EXYNOS_FMP_H_ */
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _EXYNOS_SMU_H_
+#define _EXYNOS_SMU_H_
+
+#include <linux/platform_device.h>
+
+#define CFG_DESCTYPE_0 0x0
+#define CFG_DESCTYPE_1 0x1
+#define CFG_DESCTYPE_2 0x2
+#define CFG_DESCTYPE_3 0x3
+
+#if defined(CONFIG_EXYNOS_FMP)
+#define CFG_DESCTYPE CFG_DESCTYPE_3
+#else
+#define CFG_DESCTYPE CFG_DESCTYPE_0
+#endif
+
+#define ACCESS_CONTROL_ABORT 0x14
+
+enum smu_id {
+ SMU_EMBEDDED = 0,
+ SMU_UFSCARD = 1,
+ SMU_SDCARD = 2,
+};
+
+enum smu_command {
+ SMU_INIT = 0,
+ SMU_SET = 1,
+ SMU_ABORT = 2,
+};
+
+struct smu_data_setting {
+ int id;
+ int command;
+ int desc_type;
+};
+
+struct exynos_smu_variant_ops {
+ const char *name;
+ int (*init)(struct platform_device *, struct smu_data_setting *);
+ int (*sec_config)(struct platform_device *, struct smu_data_setting *);
+ int (*resume)(struct platform_device *, struct smu_data_setting *);
+ int (*abort)(struct platform_device *, struct smu_data_setting *);
+};
+
+struct exynos_smu_variant_ops *exynos_smu_get_variant_ops(struct device_node *node);
+struct platform_device *exynos_smu_get_pdevice(struct device_node *node);
+
+#endif /* _EXYNOS_SMU_H_ */