[COMMON] fmp: add fmp driver
authorBoojin Kim <boojin.kim@samsung.com>
Wed, 24 Jan 2018 08:03:23 +0000 (17:03 +0900)
committerJaeHun Jung <jh0801.jung@samsung.com>
Tue, 8 May 2018 08:21:00 +0000 (17:21 +0900)
Change-Id: Idf587c60984db4d519464521cb2b96f9746ea0ba
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
32 files changed:
drivers/crypto/Kconfig
drivers/crypto/Makefile
drivers/crypto/exynos-diskcipher.c [new file with mode: 0644]
drivers/crypto/fmp/Kconfig [new file with mode: 0644]
drivers/crypto/fmp/Makefile [new file with mode: 0644]
drivers/crypto/fmp/fips_out.c [new file with mode: 0644]
drivers/crypto/fmp/first_file.c [new file with mode: 0644]
drivers/crypto/fmp/fmp.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_cipher.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_cipher.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_fops.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_fops.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_fops_info.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_func_test.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_func_test.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_info.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_integrity.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_integrity.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_main.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_main.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_selftest.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_fips_selftest.h [new file with mode: 0644]
drivers/crypto/fmp/fmp_test.c [new file with mode: 0644]
drivers/crypto/fmp/fmp_test.h [new file with mode: 0644]
drivers/crypto/fmp/hmac-sha256.c [new file with mode: 0644]
drivers/crypto/fmp/hmac-sha256.h [new file with mode: 0644]
drivers/crypto/fmp/last_file.c [new file with mode: 0644]
drivers/crypto/fmp/sha256.c [new file with mode: 0644]
drivers/crypto/fmp/sha256.h [new file with mode: 0644]
drivers/crypto/fmp/smu.c [new file with mode: 0644]
include/crypto/fmp.h [new file with mode: 0644]
include/crypto/smu.h [new file with mode: 0644]

index fe33c199fc1a510feca4ec7a50e7d721cf769db6..d7690f6bb8ef4dbe4d646076c1b3fac946f8f81a 100644 (file)
@@ -729,4 +729,11 @@ config CRYPTO_DEV_ARTPEC6
 
          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
index c00708d04be67513b4fde8a267247f4637fd8423..f7a77287de6178bebd3e3e7fae0c2763f67ece8e 100644 (file)
@@ -46,3 +46,7 @@ obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/
 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
diff --git a/drivers/crypto/exynos-diskcipher.c b/drivers/crypto/exynos-diskcipher.c
new file mode 100644 (file)
index 0000000..c6f1139
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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");
diff --git a/drivers/crypto/fmp/Kconfig b/drivers/crypto/fmp/Kconfig
new file mode 100644 (file)
index 0000000..4215d52
--- /dev/null
@@ -0,0 +1,53 @@
+#
+# 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
diff --git a/drivers/crypto/fmp/Makefile b/drivers/crypto/fmp/Makefile
new file mode 100644 (file)
index 0000000..3e14d20
--- /dev/null
@@ -0,0 +1,15 @@
+# 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
diff --git a/drivers/crypto/fmp/fips_out.c b/drivers/crypto/fmp/fips_out.c
new file mode 100644 (file)
index 0000000..9ec9258
--- /dev/null
@@ -0,0 +1,12 @@
+#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;
diff --git a/drivers/crypto/fmp/first_file.c b/drivers/crypto/fmp/first_file.c
new file mode 100644 (file)
index 0000000..acb91db
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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){};
diff --git a/drivers/crypto/fmp/fmp.c b/drivers/crypto/fmp/fmp.c
new file mode 100644 (file)
index 0000000..2dd1499
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * 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);
+}
diff --git a/drivers/crypto/fmp/fmp_fips_cipher.c b/drivers/crypto/fmp/fmp_fips_cipher.c
new file mode 100644 (file)
index 0000000..21d5b42
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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;
+}
diff --git a/drivers/crypto/fmp/fmp_fips_cipher.h b/drivers/crypto/fmp/fmp_fips_cipher.h
new file mode 100644 (file)
index 0000000..39b2dc3
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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
diff --git a/drivers/crypto/fmp/fmp_fips_fops.c b/drivers/crypto/fmp/fmp_fips_fops.c
new file mode 100644 (file)
index 0000000..7687783
--- /dev/null
@@ -0,0 +1,1424 @@
+/*
+ * 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;
+}
diff --git a/drivers/crypto/fmp/fmp_fips_fops.h b/drivers/crypto/fmp/fmp_fips_fops.h
new file mode 100644 (file)
index 0000000..6c678d7
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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_ */
diff --git a/drivers/crypto/fmp/fmp_fips_fops_info.h b/drivers/crypto/fmp/fmp_fips_fops_info.h
new file mode 100644 (file)
index 0000000..c6ea996
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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
diff --git a/drivers/crypto/fmp/fmp_fips_func_test.c b/drivers/crypto/fmp/fmp_fips_func_test.c
new file mode 100644 (file)
index 0000000..5d656fc
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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;
+}
diff --git a/drivers/crypto/fmp/fmp_fips_func_test.h b/drivers/crypto/fmp/fmp_fips_func_test.h
new file mode 100644 (file)
index 0000000..bab336a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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_ */
diff --git a/drivers/crypto/fmp/fmp_fips_info.h b/drivers/crypto/fmp/fmp_fips_info.h
new file mode 100644 (file)
index 0000000..ee8e51c
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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
diff --git a/drivers/crypto/fmp/fmp_fips_integrity.c b/drivers/crypto/fmp/fmp_fips_integrity.c
new file mode 100644 (file)
index 0000000..5907022
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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);
diff --git a/drivers/crypto/fmp/fmp_fips_integrity.h b/drivers/crypto/fmp/fmp_fips_integrity.h
new file mode 100644 (file)
index 0000000..8106cbf
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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
diff --git a/drivers/crypto/fmp/fmp_fips_main.c b/drivers/crypto/fmp/fmp_fips_main.c
new file mode 100644 (file)
index 0000000..6ccad73
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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);
+}
diff --git a/drivers/crypto/fmp/fmp_fips_main.h b/drivers/crypto/fmp/fmp_fips_main.h
new file mode 100644 (file)
index 0000000..fd8cbbc
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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_ */
diff --git a/drivers/crypto/fmp/fmp_fips_selftest.c b/drivers/crypto/fmp/fmp_fips_selftest.c
new file mode 100644 (file)
index 0000000..a9507f5
--- /dev/null
@@ -0,0 +1,1068 @@
+/*
+ * 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;
+}
diff --git a/drivers/crypto/fmp/fmp_fips_selftest.h b/drivers/crypto/fmp/fmp_fips_selftest.h
new file mode 100644 (file)
index 0000000..f27a094
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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
diff --git a/drivers/crypto/fmp/fmp_test.c b/drivers/crypto/fmp/fmp_test.c
new file mode 100644 (file)
index 0000000..f4a58e6
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * 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);
+}
diff --git a/drivers/crypto/fmp/fmp_test.h b/drivers/crypto/fmp/fmp_test.h
new file mode 100644 (file)
index 0000000..0990cbe
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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_ */
diff --git a/drivers/crypto/fmp/hmac-sha256.c b/drivers/crypto/fmp/hmac-sha256.c
new file mode 100644 (file)
index 0000000..751bb68
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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));
+}
diff --git a/drivers/crypto/fmp/hmac-sha256.h b/drivers/crypto/fmp/hmac-sha256.h
new file mode 100644 (file)
index 0000000..c2be607
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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 */
+
diff --git a/drivers/crypto/fmp/last_file.c b/drivers/crypto/fmp/last_file.c
new file mode 100644 (file)
index 0000000..c0d405d
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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){};
diff --git a/drivers/crypto/fmp/sha256.c b/drivers/crypto/fmp/sha256.c
new file mode 100644 (file)
index 0000000..9767bc3
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * 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;
+}
diff --git a/drivers/crypto/fmp/sha256.h b/drivers/crypto/fmp/sha256.h
new file mode 100644 (file)
index 0000000..378f261
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 */
diff --git a/drivers/crypto/fmp/smu.c b/drivers/crypto/fmp/smu.c
new file mode 100644 (file)
index 0000000..ce43fb7
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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");
diff --git a/include/crypto/fmp.h b/include/crypto/fmp.h
new file mode 100644 (file)
index 0000000..9275ed9
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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_ */
diff --git a/include/crypto/smu.h b/include/crypto/smu.h
new file mode 100644 (file)
index 0000000..d9fa4f0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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_ */