summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Douglass <dan.douglass@freescale.com>2013-11-27 03:40:44 -0600
committerDan Douglass <dan.douglass@freescale.com>2013-11-27 13:03:17 -0600
commitef660829b41cd4ce7a1465ecb503d22d19442fcc (patch)
tree58d5adeac5169f71b9772da399bbc7c788d07273
parent438bd0ffe5cf3905b5986b91be65e2d7824371d4 (diff)
ENGR00289885 [iMX6Q] Add Secure Memory and SECVIO support.
1. Pull in secure memory support from 3.0.35 kernel. 2. Pull in SECVIO support from 3.0.35 kernel. 3. Make changes to support device tree. 4. Add device tree setting for SECVIO sources. Signed-off-by: Dan Douglass <b41520@freescale.com>
-rw-r--r--arch/arm/boot/dts/imx6qdl.dtsi16
-rw-r--r--drivers/crypto/caam/Kconfig6
-rw-r--r--drivers/crypto/caam/Makefile6
-rw-r--r--drivers/crypto/caam/ctrl.c19
-rw-r--r--drivers/crypto/caam/intern.h5
-rw-r--r--drivers/crypto/caam/secvio.c335
-rw-r--r--drivers/crypto/caam/secvio.h64
-rw-r--r--drivers/crypto/caam/sm.h88
-rw-r--r--drivers/crypto/caam/sm_store.c896
-rw-r--r--drivers/crypto/caam/sm_test.c844
-rw-r--r--drivers/crypto/caam/snvsregs.h237
11 files changed, 2510 insertions, 6 deletions
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index bd6c7a0c266b..6de80a23995f 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -75,6 +75,11 @@
interrupt-parent = <&intc>;
ranges;
+ caam_sm: caam-sm@00100000 {
+ compatible = "fsl,imx6q-caam-sm";
+ reg = <0x00100000 0x3fff>;
+ };
+
dma_apbh: dma-apbh@00110000 {
compatible = "fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh";
reg = <0x00110000 0x2000>;
@@ -85,6 +90,12 @@
clocks = <&clks 106>;
};
+ irq_sec_vio: caam_secvio {
+ compatible = "fsl,imx6q-caam-secvio";
+ interrupts = <0 20 0x04>;
+ secvio_src = <0x8000001d>;
+ };
+
gpmi: gpmi-nand@00112000 {
compatible = "fsl,imx6q-gpmi-nand";
#address-cells = <1>;
@@ -606,6 +617,11 @@
fsl,anatop = <&anatop>;
};
+ caam_snvs: caam-snvs@020cc000 {
+ compatible = "fsl,imx6q-caam-snvs";
+ reg = <0x020cc000 0x4000>;
+ };
+
snvs@020cc000 {
compatible = "fsl,sec-v4.0-mon", "simple-bus";
#address-cells = <1>;
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index 493fe4073597..0df6f8c3aabe 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -107,7 +107,7 @@ config CRYPTO_DEV_FSL_CAAM_RNG_TEST
Selecting this will enable self-test for caam rng.
config CRYPTO_DEV_FSL_CAAM_SM
- boolean "CAAM Secure Memory / Keystore API (EXPERIMENTAL)"
+ tristate "CAAM Secure Memory / Keystore API (EXPERIMENTAL)"
default n
help
Enables use of a prototype kernel-level Keystore API with CAAM
@@ -130,7 +130,7 @@ config CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE
9 => 512 bytes
config CRYPTO_DEV_FSL_CAAM_SM_TEST
- boolean "CAAM Secure Memory - Keystore Test/Example (EXPERIMENTAL)"
+ tristate "CAAM Secure Memory - Keystore Test/Example (EXPERIMENTAL)"
depends on CRYPTO_DEV_FSL_CAAM_SM
default n
help
@@ -139,7 +139,7 @@ config CRYPTO_DEV_FSL_CAAM_SM_TEST
encryption/decryption.
config CRYPTO_DEV_FSL_CAAM_SECVIO
- boolean "CAAM/SNVS Security Violation Handler (EXPERIMENTAL)"
+ tristate "CAAM/SNVS Security Violation Handler (EXPERIMENTAL)"
depends on CRYPTO_DEV_FSL_CAAM
default n
help
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index b7db9e9efea5..2141ac48b163 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -6,8 +6,8 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
-#obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM) += sm_store.o
-#obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST) += sm_test.o
-#obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO) += secvio.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM) += sm_store.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST) += sm_test.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO) += secvio.o
caam-objs := ctrl.o jr.o error.o key_gen.o
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 5bc978e8c719..04df1863128b 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -12,6 +12,7 @@
#include "desc_constr.h"
#include "error.h"
#include "ctrl.h"
+#include "sm.h"
/* Used to capture the array of job rings */
struct device **caam_jr_dev;
@@ -236,6 +237,7 @@ static int caam_probe(struct platform_device *pdev)
struct device_node *nprop, *np;
struct caam_ctrl __iomem *ctrl;
struct caam_full __iomem *topregs;
+ struct snvs_full __iomem *snvsregs;
struct caam_drv_private *ctrlpriv;
#ifdef CONFIG_DEBUG_FS
struct caam_perfmon *perfmon;
@@ -265,6 +267,23 @@ static int caam_probe(struct platform_device *pdev)
/* Get the IRQ of the controller (for security violations only) */
ctrlpriv->secvio_irq = of_irq_to_resource(nprop, 0, NULL);
+ /* Get SNVS register Page */
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-snvs");
+
+ if (!np)
+ return -ENODEV;
+
+ snvsregs = of_iomap(np, 0);
+ ctrlpriv->snvs = snvsregs;
+ /* Get CAAM-SM node and of_iomap() and save */
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm");
+
+ if (!np)
+ return -ENODEV;
+
+ ctrlpriv->sm_base = of_iomap(np, 0);
+ ctrlpriv->sm_size = 0x3fff;
+
/*
* ARM targets tend to have clock control subsystems that can
* enable/disable clocking to our device. Turn clocking on to proceed
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index 0311f8572822..6bb12f3098fe 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -70,6 +70,8 @@ struct caam_drv_private_jr {
struct caam_drv_private {
struct device *dev;
+ struct device *smdev;
+ struct device *secviodev;
struct device **jrdev; /* Alloc'ed array per sub-device */
spinlock_t jr_alloc_lock;
struct platform_device *pdev;
@@ -79,6 +81,9 @@ struct caam_drv_private {
struct caam_deco **deco; /* DECO/CCB views */
struct caam_assurance *ac;
struct caam_queue_if *qi; /* QI control region */
+ struct snvs_full __iomem *snvs; /* SNVS HP+LP register space */
+ dma_addr_t __iomem *sm_base; /* Secure memory storage base */
+ u32 sm_size;
/*
* Detected geometry block. Filled in from device tree if powerpc,
diff --git a/drivers/crypto/caam/secvio.c b/drivers/crypto/caam/secvio.c
new file mode 100644
index 000000000000..defd93cb8887
--- /dev/null
+++ b/drivers/crypto/caam/secvio.c
@@ -0,0 +1,335 @@
+
+/*
+ * CAAM/SEC 4.x Security Violation Handler
+ * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved
+ */
+
+#include "compat.h"
+#include "intern.h"
+#include "secvio.h"
+#include "regs.h"
+
+/*
+ * These names are associated with each violation handler.
+ * The source names were taken from MX6, and are based on recommendations
+ * for most common SoCs.
+ */
+static const u8 *violation_src_name[] = {
+ "CAAM Security Violation",
+ "JTAG Alarm",
+ "Watchdog",
+ "(reserved)",
+ "External Boot",
+ "Tamper Detect",
+};
+
+/* Top-level security violation interrupt */
+static irqreturn_t caam_secvio_interrupt(int irq, void *snvsdev)
+{
+ struct device *dev = snvsdev;
+ struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev);
+ u32 irqstate;
+
+ /* Check the HP secvio status register */
+ irqstate = rd_reg32(&svpriv->svregs->hp.secvio_status) |
+ HP_SECVIOST_SECVIOMASK;
+
+ if (!irqstate)
+ return IRQ_NONE;
+
+ /* Mask out one or more causes for deferred service */
+ clrbits32(&svpriv->svregs->hp.secvio_int_ctl, irqstate);
+
+ /* Now ACK causes */
+ setbits32(&svpriv->svregs->hp.secvio_status, irqstate);
+
+ /* And run deferred service */
+ preempt_disable();
+ tasklet_schedule(&svpriv->irqtask[smp_processor_id()]);
+ preempt_enable();
+
+ return IRQ_HANDLED;
+}
+
+/* Deferred service handler. Tasklet arg is simply the SNVS dev */
+static void caam_secvio_dispatch(unsigned long indev)
+{
+ struct device *dev = (struct device *)indev;
+ struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev);
+ unsigned long flags, cause;
+ int i;
+
+
+ /*
+ * Capture the interrupt cause, using masked interrupts as
+ * identification. This only works if all are enabled; if
+ * this changes in the future, a "cause queue" will have to
+ * be built
+ */
+ cause = rd_reg32(&svpriv->svregs->hp.secvio_int_ctl) &
+ (HP_SECVIO_INTEN_SRC5 | HP_SECVIO_INTEN_SRC4 |
+ HP_SECVIO_INTEN_SRC3 | HP_SECVIO_INTEN_SRC2 |
+ HP_SECVIO_INTEN_SRC1 | HP_SECVIO_INTEN_SRC0);
+
+ /* Look through causes, call each handler if exists */
+ for (i = 0; i < MAX_SECVIO_SOURCES; i++)
+ if (cause & (1 << i)) {
+ spin_lock_irqsave(&svpriv->svlock, flags);
+ svpriv->intsrc[i].handler(dev, i,
+ svpriv->intsrc[i].ext);
+ spin_unlock_irqrestore(&svpriv->svlock, flags);
+ };
+
+ /* Re-enable now-serviced interrupts */
+ setbits32(&svpriv->svregs->hp.secvio_int_ctl, cause);
+}
+
+/*
+ * Default cause handler, used in lieu of an application-defined handler.
+ * All it does at this time is print a console message. It could force a halt.
+ */
+static void caam_secvio_default(struct device *dev, u32 cause, void *ext)
+{
+ struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev);
+
+ dev_err(dev, "Unhandled Security Violation Interrupt %d = %s\n",
+ cause, svpriv->intsrc[cause].intname);
+}
+
+/*
+ * Install an application-defined handler for a specified cause
+ * Arguments:
+ * - dev points to SNVS-owning device
+ * - cause interrupt source cause
+ * - handler application-defined handler, gets called with dev
+ * source cause, and locally-defined handler argument
+ * - cause_description points to a string to override the default cause
+ * name, this can be used as an alternate for error
+ * messages and such. If left NULL, the default
+ * description string is used.
+ * - ext pointer to any extra data needed by the handler.
+ */
+int caam_secvio_install_handler(struct device *dev, enum secvio_cause cause,
+ void (*handler)(struct device *dev, u32 cause,
+ void *ext),
+ u8 *cause_description, void *ext)
+{
+ unsigned long flags;
+ struct caam_drv_private_secvio *svpriv;
+
+ svpriv = dev_get_drvdata(dev);
+
+ if ((handler == NULL) || (cause > SECVIO_CAUSE_SOURCE_5))
+ return -EINVAL;
+
+ spin_lock_irqsave(&svpriv->svlock, flags);
+ svpriv->intsrc[cause].handler = handler;
+ if (cause_description != NULL)
+ svpriv->intsrc[cause].intname = cause_description;
+ if (ext != NULL)
+ svpriv->intsrc[cause].ext = ext;
+ spin_unlock_irqrestore(&svpriv->svlock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(caam_secvio_install_handler);
+
+/*
+ * Remove an application-defined handler for a specified cause (and, by
+ * implication, restore the "default".
+ * Arguments:
+ * - dev points to SNVS-owning device
+ * - cause interrupt source cause
+ */
+int caam_secvio_remove_handler(struct device *dev, enum secvio_cause cause)
+{
+ unsigned long flags;
+ struct caam_drv_private_secvio *svpriv;
+
+ svpriv = dev_get_drvdata(dev);
+
+ if (cause > SECVIO_CAUSE_SOURCE_5)
+ return -EINVAL;
+
+ spin_lock_irqsave(&svpriv->svlock, flags);
+ svpriv->intsrc[cause].intname = violation_src_name[cause];
+ svpriv->intsrc[cause].handler = caam_secvio_default;
+ svpriv->intsrc[cause].ext = NULL;
+ spin_unlock_irqrestore(&svpriv->svlock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(caam_secvio_remove_handler);
+
+int caam_secvio_startup(struct platform_device *pdev)
+{
+ struct device *ctrldev, *svdev;
+ struct caam_drv_private *ctrlpriv;
+ struct caam_drv_private_secvio *svpriv;
+ struct platform_device *svpdev;
+ struct device_node *np;
+ const void *prop;
+ int i, error, secvio_inten_src;
+
+ ctrldev = &pdev->dev;
+ ctrlpriv = dev_get_drvdata(ctrldev);
+ /*
+ * Set up the private block for secure memory
+ * Only one instance is possible
+ */
+ svpriv = kzalloc(sizeof(struct caam_drv_private_secvio), GFP_KERNEL);
+ if (svpriv == NULL) {
+ dev_err(ctrldev, "can't alloc private mem for secvio\n");
+ return -ENOMEM;
+ }
+ svpriv->parentdev = ctrldev;
+
+ /* Create the security violation dev */
+#ifdef CONFIG_OF
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-secvio");
+ if (!np)
+ return -ENODEV;
+
+ ctrlpriv->secvio_irq = of_irq_to_resource(np, 0, NULL);
+
+ prop = of_get_property(np, "secvio_src", NULL);
+ if (prop)
+ secvio_inten_src = of_read_ulong(prop, 1);
+ else
+ secvio_inten_src = HP_SECVIO_INTEN_ALL;
+
+ printk(KERN_ERR "secvio_inten_src = %x\n", secvio_inten_src);
+
+ svpdev = of_platform_device_create(np, NULL, ctrldev);
+ if (!svpdev)
+ return -ENODEV;
+
+#else
+ svpdev = platform_device_register_data(ctrldev, "caam_secvio", 0,
+ svpriv,
+ sizeof(struct caam_drv_private_secvio));
+
+ secvio_inten_src = HP_SECVIO_INTEN_ALL;
+#endif
+ if (svpdev == NULL) {
+ kfree(svpriv);
+ return -EINVAL;
+ }
+ svdev = &svpdev->dev;
+ dev_set_drvdata(svdev, svpriv);
+ ctrlpriv->secviodev = svdev;
+ svpriv->svregs = ctrlpriv->snvs;
+
+ /*
+ * Now we have all the dev data set up. Init interrupt
+ * source descriptions
+ */
+ for (i = 0; i < MAX_SECVIO_SOURCES; i++) {
+ svpriv->intsrc[i].intname = violation_src_name[i];
+ svpriv->intsrc[i].handler = caam_secvio_default;
+ }
+
+ /* Connect main handler */
+ for_each_possible_cpu(i)
+ tasklet_init(&svpriv->irqtask[i], caam_secvio_dispatch,
+ (unsigned long)svdev);
+
+ error = request_irq(ctrlpriv->secvio_irq, caam_secvio_interrupt,
+ IRQF_SHARED, "caam_secvio", svdev);
+ if (error) {
+ dev_err(svdev, "can't connect secvio interrupt\n");
+ irq_dispose_mapping(ctrlpriv->secvio_irq);
+ ctrlpriv->secvio_irq = 0;
+ return -EINVAL;
+ }
+
+ /* Enable all sources */
+ wr_reg32(&svpriv->svregs->hp.secvio_int_ctl, secvio_inten_src);
+
+ dev_info(svdev, "security violation service handlers armed\n");
+
+ return 0;
+}
+
+void caam_secvio_shutdown(struct platform_device *pdev)
+{
+ struct device *ctrldev, *svdev;
+ struct caam_drv_private *priv;
+ struct caam_drv_private_secvio *svpriv;
+ int i;
+
+ ctrldev = &pdev->dev;
+ priv = dev_get_drvdata(ctrldev);
+ svdev = priv->secviodev;
+ svpriv = dev_get_drvdata(svdev);
+
+ /* Shut off all sources */
+
+ wr_reg32(&svpriv->svregs->hp.secvio_int_ctl, 0);
+
+ /* Remove tasklets and release interrupt */
+ for_each_possible_cpu(i)
+ tasklet_kill(&svpriv->irqtask[i]);
+
+ free_irq(priv->secvio_irq, svdev);
+
+ kfree(svpriv);
+}
+
+
+#ifdef CONFIG_OF
+static void __exit caam_secvio_exit(void)
+{
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+ if (!dev_node)
+ return;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return;
+
+ of_node_get(dev_node);
+
+ caam_secvio_shutdown(pdev);
+
+}
+
+static int __init caam_secvio_init(void)
+{
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ /*
+ * Do of_find_compatible_node() then of_find_device_by_node()
+ * once a functional device tree is available
+ */
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL,
+ "arm,imx6-caam-secvio");
+ if (!dev_node)
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return -ENODEV;
+
+ of_node_put(dev_node);
+
+ return caam_secvio_startup(pdev);
+}
+
+module_init(caam_secvio_init);
+module_exit(caam_secvio_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL CAAM/SNVS Security Violation Handler");
+MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD");
+#endif
diff --git a/drivers/crypto/caam/secvio.h b/drivers/crypto/caam/secvio.h
new file mode 100644
index 000000000000..b0efe20bdc5d
--- /dev/null
+++ b/drivers/crypto/caam/secvio.h
@@ -0,0 +1,64 @@
+
+/*
+ * CAAM Security Violation Handler
+ * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved
+ */
+
+#ifndef SECVIO_H
+#define SECVIO_H
+
+#include "snvsregs.h"
+
+
+/*
+ * Defines the published interfaces to install/remove application-specified
+ * handlers for catching violations
+ */
+
+#define MAX_SECVIO_SOURCES 6
+
+/* these are the untranslated causes */
+enum secvio_cause {
+ SECVIO_CAUSE_SOURCE_0,
+ SECVIO_CAUSE_SOURCE_1,
+ SECVIO_CAUSE_SOURCE_2,
+ SECVIO_CAUSE_SOURCE_3,
+ SECVIO_CAUSE_SOURCE_4,
+ SECVIO_CAUSE_SOURCE_5
+};
+
+/* These are common "recommended" cause definitions for most devices */
+#define SECVIO_CAUSE_CAAM_VIOLATION SECVIO_CAUSE_SOURCE_0
+#define SECVIO_CAUSE JTAG_ALARM SECVIO_CAUSE_SOURCE_1
+#define SECVIO_CAUSE_WATCHDOG SECVIO_CAUSE_SOURCE_2
+#define SECVIO_CAUSE_EXTERNAL_BOOT SECVIO_CAUSE_SOURCE_4
+#define SECVIO_CAUSE_TAMPER_DETECT SECVIO_CAUSE_SOURCE_5
+
+int caam_secvio_install_handler(struct device *dev, enum secvio_cause cause,
+ void (*handler)(struct device *dev, u32 cause,
+ void *ext),
+ u8 *cause_description, void *ext);
+int caam_secvio_remove_handler(struct device *dev, enum secvio_cause cause);
+
+/*
+ * Private data definitions for the secvio "driver"
+ */
+
+struct secvio_int_src {
+ const u8 *intname; /* Points to a descriptive name for source */
+ void *ext; /* Extended data to pass to the handler */
+ void (*handler)(struct device *dev, u32 cause, void *ext);
+};
+
+struct caam_drv_private_secvio {
+ struct device *parentdev; /* points back to the controller */
+ spinlock_t svlock ____cacheline_aligned;
+ struct tasklet_struct irqtask[NR_CPUS];
+ struct snvs_full __iomem *svregs; /* both HP and LP domains */
+
+ /* Registered handlers for each violation */
+ struct secvio_int_src intsrc[MAX_SECVIO_SOURCES];
+
+};
+
+#endif /* SECVIO_H */
diff --git a/drivers/crypto/caam/sm.h b/drivers/crypto/caam/sm.h
new file mode 100644
index 000000000000..88dc0cd93d79
--- /dev/null
+++ b/drivers/crypto/caam/sm.h
@@ -0,0 +1,88 @@
+
+/*
+ * CAAM Secure Memory/Keywrap API Definitions
+ * Copyright (C) 2008-2013 Freescale Semiconductor, Inc.
+ */
+
+#ifndef SM_H
+#define SM_H
+
+
+/* Storage access permissions */
+#define SM_PERM_READ 0x01
+#define SM_PERM_WRITE 0x02
+#define SM_PERM_BLOB 0x03
+
+
+/* Keystore maintenance functions */
+void sm_init_keystore(struct device *dev);
+u32 sm_detect_keystore_units(struct device *dev);
+int sm_establish_keystore(struct device *dev, u32 unit);
+void sm_release_keystore(struct device *dev, u32 unit);
+void caam_sm_shutdown(struct platform_device *pdev);
+int caam_sm_example_init(struct platform_device *pdev);
+
+/* Keystore accessor functions */
+extern int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size,
+ u32 *slot);
+extern int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot);
+extern int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot,
+ const u8 *key_data, u32 key_length);
+extern int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot,
+ u32 key_length, u8 *key_data);
+extern int sm_keystore_slot_encapsulate(struct device *dev, u32 unit,
+ u32 inslot, u32 outslot, u16 secretlen,
+ u8 *keymod, u16 keymodlen);
+extern int sm_keystore_slot_decapsulate(struct device *dev, u32 unit,
+ u32 inslot, u32 outslot, u16 secretlen,
+ u8 *keymod, u16 keymodlen);
+
+/* Data structure to hold per-slot information */
+struct keystore_data_slot_info {
+ u8 allocated; /* Track slot assignments */
+ u32 key_length; /* Size of the key */
+};
+
+/* Data structure to hold keystore information */
+struct keystore_data {
+ void *base_address; /* Base of the Secure Partition */
+ u32 slot_count; /* Number of slots in the keystore */
+ struct keystore_data_slot_info *slot; /* Per-slot information */
+};
+
+/* store the detected attributes of a secure memory page */
+struct sm_page_descriptor {
+ u16 phys_pagenum; /* may be discontiguous */
+ u16 own_part; /* Owning partition */
+ void *pg_base; /* Calculated virtual address */
+ struct keystore_data *ksdata;
+};
+
+struct caam_drv_private_sm {
+ struct device *parentdev; /* this ends up as the controller */
+ struct device *smringdev; /* ring that owns this instance */
+ spinlock_t kslock ____cacheline_aligned;
+
+ /* Default parameters for geometry */
+ u32 max_pages; /* maximum pages this instance can support */
+ u32 top_partition; /* highest partition number in this instance */
+ u32 top_page; /* highest page number in this instance */
+ u32 page_size; /* page size */
+ u32 slot_size; /* selected size of each storage block */
+
+ /* Partition/Page Allocation Map */
+ u32 localpages; /* Number of pages we can access */
+ struct sm_page_descriptor *pagedesc; /* Allocated per-page */
+
+ /* Installed handlers for keystore access */
+ int (*data_init)(struct device *dev, u32 unit);
+ void (*data_cleanup)(struct device *dev, u32 unit);
+ int (*slot_alloc)(struct device *dev, u32 unit, u32 size, u32 *slot);
+ int (*slot_dealloc)(struct device *dev, u32 unit, u32 slot);
+ void *(*slot_get_address)(struct device *dev, u32 unit, u32 handle);
+ u32 (*slot_get_base)(struct device *dev, u32 unit, u32 handle);
+ u32 (*slot_get_offset)(struct device *dev, u32 unit, u32 handle);
+ u32 (*slot_get_slot_size)(struct device *dev, u32 unit, u32 handle);
+};
+
+#endif /* SM_H */
diff --git a/drivers/crypto/caam/sm_store.c b/drivers/crypto/caam/sm_store.c
new file mode 100644
index 000000000000..6e63aa3807e5
--- /dev/null
+++ b/drivers/crypto/caam/sm_store.c
@@ -0,0 +1,896 @@
+
+/*
+ * CAAM Secure Memory Storage Interface
+ * Copyright (C) 2008-2013 Freescale Semiconductor, Inc.
+ *
+ * Loosely based on the SHW Keystore API for SCC/SCC2
+ * Experimental implementation and NOT intended for upstream use. Expect
+ * this interface to be amended significantly in the future once it becomes
+ * integrated into live applications.
+ *
+ * Known issues:
+ *
+ * - Executes one instance of an secure memory "driver". This is tied to the
+ * fact that job rings can't run as standalone instances in the present
+ * configuration.
+ *
+ * - It does not expose a userspace interface. The value of a userspace
+ * interface for access to secrets is a point for further architectural
+ * discussion.
+ *
+ * - Partition/permission management is not part of this interface. It
+ * depends on some level of "knowledge" agreed upon between bootloader,
+ * provisioning applications, and OS-hosted software (which uses this
+ * driver).
+ *
+ * - No means of identifying the location or purpose of secrets managed by
+ * this interface exists; "slot location" and format of a given secret
+ * needs to be agreed upon between bootloader, provisioner, and OS-hosted
+ * application.
+ */
+
+#include "compat.h"
+#include "regs.h"
+#include "jr.h"
+#include "desc.h"
+#include "intern.h"
+#include "error.h"
+#include "sm.h"
+
+#ifdef SM_DEBUG_CONT
+void sm_show_page(struct device *dev, struct sm_page_descriptor *pgdesc)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ u32 i, *smdata;
+
+ dev_info(dev, "physical page %d content at 0x%08x\n",
+ pgdesc->phys_pagenum, pgdesc->pg_base);
+ smdata = pgdesc->pg_base;
+ for (i = 0; i < (smpriv->page_size / sizeof(u32)); i += 4)
+ dev_info(dev, "[0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ (u32)&smdata[i], smdata[i], smdata[i+1], smdata[i+2],
+ smdata[i+3]);
+}
+#endif
+
+/*
+ * Construct a secure memory blob encapsulation job descriptor
+ *
+ * - desc pointer to hold new (to be allocated) pointer to the generated
+ * descriptor for later use. Calling thread can kfree the
+ * descriptor after execution.
+ * - keymod Physical pointer to key modifier (contiguous piece).
+ * - keymodsz Size of key modifier in bytes (should normally be 8).
+ * - secretbuf Physical pointer (within an accessible secure memory page)
+ * of the secret to be encapsulated.
+ * - outbuf Physical pointer (within an accessible secure memory page)
+ * of the encapsulated output. This will be larger than the
+ * input secret because of the added encapsulation data.
+ * - secretsz Size of input secret, in bytes.
+ * - auth If nonzero, use AES-CCM for encapsulation, else use ECB
+ *
+ * Note: this uses 32-bit pointers at present
+ */
+#define INITIAL_DESCSZ 16 /* size of tmp buffer for descriptor const. */
+static int blob_encap_desc(u32 **desc, dma_addr_t keymod, u16 keymodsz,
+ dma_addr_t secretbuf, dma_addr_t outbuf,
+ u16 secretsz, bool auth)
+{
+ u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+ u16 dsize, idx;
+
+ memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32));
+ idx = 1;
+
+ /* Load key modifier */
+ tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY |
+ ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) |
+ (keymodsz & LDST_LEN_MASK);
+
+ tmpdesc[idx++] = (u32)keymod;
+
+ /* Encapsulate to secure memory */
+ tmpdesc[idx++] = CMD_SEQ_IN_PTR | secretsz;
+ tmpdesc[idx++] = (u32)secretbuf;
+
+ /* Add space for BKEK and MAC tag */
+ tmpdesc[idx++] = CMD_SEQ_IN_PTR | (secretsz + (32 + 16));
+
+ tmpdesc[idx++] = (u32)outbuf;
+ tmpdesc[idx] = CMD_OPERATION | OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB |
+ OP_PCL_BLOB_PTXT_SECMEM;
+ if (auth)
+ tmpdesc[idx] |= OP_PCL_BLOB_EKT;
+
+ idx++;
+ tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK);
+ dsize = idx * sizeof(u32);
+
+ tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA);
+ if (tdesc == NULL)
+ return 0;
+
+ memcpy(tdesc, tmpdesc, dsize);
+ *desc = tdesc;
+ return dsize;
+}
+
+/*
+ * Construct a secure memory blob decapsulation job descriptor
+ *
+ * - desc pointer to hold new (to be allocated) pointer to the generated
+ * descriptor for later use. Calling thread can kfree the
+ * descriptor after execution.
+ * - keymod Physical pointer to key modifier (contiguous piece).
+ * - keymodsz Size of key modifier in bytes (should normally be 16).
+ * - blobbuf Physical pointer (within an accessible secure memory page)
+ * of the blob to be decapsulated.
+ * - outbuf Physical pointer (within an accessible secure memory page)
+ * of the decapsulated output.
+ * - secretsz Size of input blob, in bytes.
+ * - auth If nonzero, assume AES-CCM for decapsulation, else use ECB
+ *
+ * Note: this uses 32-bit pointers at present
+ */
+static int blob_decap_desc(u32 **desc, dma_addr_t keymod, u16 keymodsz,
+ dma_addr_t blobbuf, dma_addr_t outbuf,
+ u16 blobsz, bool auth)
+{
+ u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+ u16 dsize, idx;
+
+ memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32));
+ idx = 1;
+
+ /* Load key modifier */
+ tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY |
+ ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) |
+ (keymodsz & LDST_LEN_MASK);
+
+ tmpdesc[idx++] = (u32)keymod;
+
+ /* Compensate BKEK + MAC tag */
+ tmpdesc[idx++] = CMD_SEQ_IN_PTR | (blobsz + 32 + 16);
+
+ tmpdesc[idx++] = (u32)blobbuf;
+ tmpdesc[idx++] = CMD_SEQ_OUT_PTR | blobsz;
+ tmpdesc[idx++] = (u32)outbuf;
+
+ /* Decapsulate from secure memory partition to black blob */
+ tmpdesc[idx] = CMD_OPERATION | OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB |
+ OP_PCL_BLOB_PTXT_SECMEM | OP_PCL_BLOB_BLACK;
+ if (auth)
+ tmpdesc[idx] |= OP_PCL_BLOB_EKT;
+
+ idx++;
+ tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK);
+ dsize = idx * sizeof(u32);
+
+ tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA);
+ if (tdesc == NULL)
+ return 0;
+
+ memcpy(tdesc, tmpdesc, dsize);
+ *desc = tdesc;
+ return dsize;
+}
+
+/*
+ * Pseudo-synchronous ring access functions for carrying out key
+ * encapsulation and decapsulation
+ */
+
+struct sm_key_job_result {
+ int error;
+ struct completion completion;
+};
+
+void sm_key_job_done(struct device *dev, u32 *desc, u32 err, void *context)
+{
+ struct sm_key_job_result *res = context;
+
+ res->error = err; /* save off the error for postprocessing */
+ complete(&res->completion); /* mark us complete */
+}
+
+static int sm_key_job(struct device *ksdev, u32 *jobdesc)
+{
+ struct sm_key_job_result testres;
+ struct caam_drv_private_sm *kspriv;
+ int rtn = 0;
+
+ kspriv = dev_get_drvdata(ksdev);
+
+ init_completion(&testres.completion);
+
+ rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, sm_key_job_done,
+ &testres);
+ if (!rtn) {
+ wait_for_completion_interruptible(&testres.completion);
+ rtn = testres.error;
+ }
+ return rtn;
+}
+
+/*
+ * Following section establishes the default methods for keystore access
+ * They are NOT intended for use external to this module
+ *
+ * In the present version, these are the only means for the higher-level
+ * interface to deal with the mechanics of accessing the phyiscal keystore
+ */
+
+
+int slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+ u32 i;
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_alloc(): requesting slot for %d bytes\n", size);
+#endif
+
+ if (size > smpriv->slot_size)
+ return -EKEYREJECTED;
+
+ for (i = 0; i < ksdata->slot_count; i++) {
+ if (ksdata->slot[i].allocated == 0) {
+ ksdata->slot[i].allocated = 1;
+ (*slot) = i;
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_alloc(): new slot %d allocated\n",
+ *slot);
+#endif
+ return 0;
+ }
+ }
+
+ return -ENOSPC;
+}
+EXPORT_SYMBOL(slot_alloc);
+
+int slot_dealloc(struct device *dev, u32 unit, u32 slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+ u8 __iomem *slotdata;
+
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_dealloc(): releasing slot %d\n", slot);
+#endif
+ if (slot >= ksdata->slot_count)
+ return -EINVAL;
+ slotdata = ksdata->base_address + slot * smpriv->slot_size;
+
+ if (ksdata->slot[slot].allocated == 1) {
+ /* Forcibly overwrite the data from the keystore */
+ memset(ksdata->base_address + slot * smpriv->slot_size, 0,
+ smpriv->slot_size);
+
+ ksdata->slot[slot].allocated = 0;
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_dealloc(): slot %d released\n", slot);
+#endif
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(slot_dealloc);
+
+void *slot_get_address(struct device *dev, u32 unit, u32 slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+ if (slot >= ksdata->slot_count)
+ return NULL;
+
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_get_address(): slot %d is 0x%08x\n", slot,
+ (u32)ksdata->base_address + slot * smpriv->slot_size);
+#endif
+
+ return ksdata->base_address + slot * smpriv->slot_size;
+}
+
+u32 slot_get_base(struct device *dev, u32 unit, u32 slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+ /*
+ * There could potentially be more than one secure partition object
+ * associated with this keystore. For now, there is just one.
+ */
+
+ (void)slot;
+
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_get_base(): slot %d = 0x%08x\n",
+ slot, (u32)ksdata->base_address);
+#endif
+
+ return (u32)(ksdata->base_address);
+}
+
+u32 slot_get_offset(struct device *dev, u32 unit, u32 slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+ if (slot >= ksdata->slot_count)
+ return -EINVAL;
+
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_get_offset(): slot %d = %d\n", slot,
+ slot * smpriv->slot_size);
+#endif
+
+ return slot * smpriv->slot_size;
+}
+
+u32 slot_get_slot_size(struct device *dev, u32 unit, u32 slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+
+#ifdef SM_DEBUG
+ dev_info(dev, "slot_get_slot_size(): slot %d = %d\n", slot,
+ smpriv->slot_size);
+#endif
+ /* All slots are the same size in the default implementation */
+ return smpriv->slot_size;
+}
+
+
+
+int kso_init_data(struct device *dev, u32 unit)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = -EINVAL;
+ struct keystore_data *keystore_data = NULL;
+ u32 slot_count;
+ u32 keystore_data_size;
+
+ /*
+ * Calculate the required size of the keystore data structure, based
+ * on the number of keys that can fit in the partition.
+ */
+ slot_count = smpriv->page_size / smpriv->slot_size;
+#ifdef SM_DEBUG
+ dev_info(dev, "kso_init_data: %d slots initializing\n", slot_count);
+#endif
+
+ keystore_data_size = sizeof(struct keystore_data) +
+ slot_count *
+ sizeof(struct keystore_data_slot_info);
+
+ keystore_data = kzalloc(keystore_data_size, GFP_KERNEL);
+
+ if (keystore_data == NULL) {
+ retval = -ENOSPC;
+ goto out;
+ }
+
+#ifdef SM_DEBUG
+ dev_info(dev, "kso_init_data: keystore data size = %d\n",
+ keystore_data_size);
+#endif
+
+ /*
+ * Place the slot information structure directly after the keystore data
+ * structure.
+ */
+ keystore_data->slot = (struct keystore_data_slot_info *)
+ (keystore_data + 1);
+ keystore_data->slot_count = slot_count;
+
+ smpriv->pagedesc[unit].ksdata = keystore_data;
+ smpriv->pagedesc[unit].ksdata->base_address =
+ smpriv->pagedesc[unit].pg_base;
+
+ retval = 0;
+
+out:
+ if (retval != 0)
+ if (keystore_data != NULL)
+ kfree(keystore_data);
+
+
+ return retval;
+}
+
+void kso_cleanup_data(struct device *dev, u32 unit)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ struct keystore_data *keystore_data = NULL;
+
+ if (smpriv->pagedesc[unit].ksdata != NULL)
+ keystore_data = smpriv->pagedesc[unit].ksdata;
+
+ /* Release the allocated keystore management data */
+ kfree(smpriv->pagedesc[unit].ksdata);
+
+ return;
+}
+
+
+
+/*
+ * Keystore management section
+ */
+
+void sm_init_keystore(struct device *dev)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+ smpriv->data_init = kso_init_data;
+ smpriv->data_cleanup = kso_cleanup_data;
+ smpriv->slot_alloc = slot_alloc;
+ smpriv->slot_dealloc = slot_dealloc;
+ smpriv->slot_get_address = slot_get_address;
+ smpriv->slot_get_base = slot_get_base;
+ smpriv->slot_get_offset = slot_get_offset;
+ smpriv->slot_get_slot_size = slot_get_slot_size;
+#ifdef SM_DEBUG
+ dev_info(dev, "sm_init_keystore(): handlers installed\n");
+#endif
+}
+EXPORT_SYMBOL(sm_init_keystore);
+
+/* Return available pages/units */
+u32 sm_detect_keystore_units(struct device *dev)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+ return smpriv->localpages;
+}
+EXPORT_SYMBOL(sm_detect_keystore_units);
+
+/*
+ * Do any keystore specific initializations
+ */
+int sm_establish_keystore(struct device *dev, u32 unit)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+#ifdef SM_DEBUG
+ dev_info(dev, "sm_establish_keystore(): unit %d initializing\n", unit);
+#endif
+
+ if (smpriv->data_init == NULL)
+ return -EINVAL;
+
+ /* Call the data_init function for any user setup */
+ return smpriv->data_init(dev, unit);
+}
+EXPORT_SYMBOL(sm_establish_keystore);
+
+void sm_release_keystore(struct device *dev, u32 unit)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+#ifdef SM_DEBUG
+ dev_info(dev, "sm_establish_keystore(): unit %d releasing\n", unit);
+#endif
+ if ((smpriv != NULL) && (smpriv->data_cleanup != NULL))
+ smpriv->data_cleanup(dev, unit);
+
+ return;
+}
+EXPORT_SYMBOL(sm_release_keystore);
+
+/*
+ * Subsequent interfacce (sm_keystore_*) forms the accessor interfacce to
+ * the keystore
+ */
+int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = -EINVAL;
+
+ spin_lock(&smpriv->kslock);
+
+ if ((smpriv->slot_alloc == NULL) ||
+ (smpriv->pagedesc[unit].ksdata == NULL))
+ goto out;
+
+ retval = smpriv->slot_alloc(dev, unit, size, slot);
+
+out:
+ spin_unlock(&smpriv->kslock);
+ return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_alloc);
+
+int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = -EINVAL;
+
+ spin_lock(&smpriv->kslock);
+
+ if ((smpriv->slot_alloc == NULL) ||
+ (smpriv->pagedesc[unit].ksdata == NULL))
+ goto out;
+
+ retval = smpriv->slot_dealloc(dev, unit, slot);
+out:
+ spin_unlock(&smpriv->kslock);
+ return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_dealloc);
+
+int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot,
+ const u8 *key_data, u32 key_length)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = -EINVAL;
+ u32 slot_size;
+ u32 i;
+ u8 __iomem *slot_location;
+
+ spin_lock(&smpriv->kslock);
+
+ slot_size = smpriv->slot_get_slot_size(dev, unit, slot);
+
+ if (key_length > slot_size) {
+ retval = -EFBIG;
+ goto out;
+ }
+
+ slot_location = smpriv->slot_get_address(dev, unit, slot);
+
+ for (i = 0; i < key_length; i++)
+ slot_location[i] = key_data[i];
+
+ retval = 0;
+
+out:
+ spin_unlock(&smpriv->kslock);
+ return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_load);
+
+int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot,
+ u32 key_length, u8 *key_data)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = -EINVAL;
+ u8 __iomem *slot_addr;
+ u32 slot_size;
+
+ spin_lock(&smpriv->kslock);
+
+ slot_addr = smpriv->slot_get_address(dev, unit, slot);
+ slot_size = smpriv->slot_get_slot_size(dev, unit, slot);
+
+ if (key_length > slot_size) {
+ retval = -EKEYREJECTED;
+ goto out;
+ }
+
+ memcpy(key_data, slot_addr, key_length);
+ retval = 0;
+
+out:
+ spin_unlock(&smpriv->kslock);
+ return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_read);
+
+int sm_keystore_slot_encapsulate(struct device *dev, u32 unit, u32 inslot,
+ u32 outslot, u16 secretlen, u8 *keymod,
+ u16 keymodlen)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = 0;
+ u32 slot_length, dsize, jstat;
+ u32 __iomem *encapdesc = NULL;
+ u8 __iomem *lkeymod, *inpslotaddr, *outslotaddr;
+ dma_addr_t keymod_dma;
+
+ /* Ensure that the full blob will fit in the key slot */
+ slot_length = smpriv->slot_get_slot_size(dev, unit, outslot);
+ if ((secretlen + 48) > slot_length)
+ goto out;
+
+ /* Get the base addresses of both keystore slots */
+ inpslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, inslot);
+ outslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, outslot);
+
+ /* Build the key modifier */
+ lkeymod = kmalloc(keymodlen, GFP_KERNEL | GFP_DMA);
+ memcpy(lkeymod, keymod, keymodlen);
+ keymod_dma = dma_map_single(dev, lkeymod, keymodlen, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, keymod_dma, keymodlen, DMA_TO_DEVICE);
+
+ /* Build the encapsulation job descriptor */
+ dsize = blob_encap_desc(&encapdesc, keymod_dma, keymodlen,
+ __pa(inpslotaddr), __pa(outslotaddr),
+ secretlen, 0);
+ if (!dsize) {
+ dev_err(dev, "can't alloc an encap descriptor\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ jstat = sm_key_job(dev, encapdesc);
+
+ dma_unmap_single(dev, keymod_dma, keymodlen, DMA_TO_DEVICE);
+ kfree(encapdesc);
+
+out:
+ return retval;
+
+}
+EXPORT_SYMBOL(sm_keystore_slot_encapsulate);
+
+int sm_keystore_slot_decapsulate(struct device *dev, u32 unit, u32 inslot,
+ u32 outslot, u16 secretlen, u8 *keymod,
+ u16 keymodlen)
+{
+ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+ int retval = 0;
+ u32 slot_length, dsize, jstat;
+ u32 __iomem *decapdesc = NULL;
+ u8 __iomem *lkeymod, *inpslotaddr, *outslotaddr;
+ dma_addr_t keymod_dma;
+
+ /* Ensure that the decap data will fit in the key slot */
+ slot_length = smpriv->slot_get_slot_size(dev, unit, outslot);
+ if (secretlen > slot_length)
+ goto out;
+
+ /* Get the base addresses of both keystore slots */
+ inpslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, inslot);
+ outslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, outslot);
+
+ /* Build the key modifier */
+ lkeymod = kmalloc(keymodlen, GFP_KERNEL | GFP_DMA);
+ memcpy(lkeymod, keymod, keymodlen);
+ keymod_dma = dma_map_single(dev, lkeymod, keymodlen, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, keymod_dma, keymodlen, DMA_TO_DEVICE);
+
+ /* Build the decapsulation job descriptor */
+ dsize = blob_decap_desc(&decapdesc, keymod_dma, keymodlen,
+ __pa(inpslotaddr), __pa(outslotaddr),
+ secretlen, 0);
+ if (!dsize) {
+ dev_err(dev, "can't alloc a decap descriptor\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ jstat = sm_key_job(dev, decapdesc);
+
+ dma_unmap_single(dev, keymod_dma, keymodlen, DMA_TO_DEVICE);
+ kfree(decapdesc);
+
+out:
+ return retval;
+
+}
+EXPORT_SYMBOL(sm_keystore_slot_decapsulate);
+
+
+/*
+ * Initialization/shutdown subsystem
+ * Assumes statically-invoked startup/shutdown from the controller driver
+ * for the present time, to be reworked when a device tree becomes
+ * available. This code will not modularize in present form.
+ *
+ * Also, simply uses ring 0 for execution at the present
+ */
+
+int caam_sm_startup(struct platform_device *pdev)
+{
+ struct device *ctrldev, *smdev;
+ struct caam_drv_private *ctrlpriv;
+ struct caam_drv_private_sm *smpriv;
+ struct caam_drv_private_jr *jrpriv; /* need this for reg page */
+ struct platform_device *sm_pdev;
+ struct sm_page_descriptor *lpagedesc;
+ u32 page, pgstat, lpagect, detectedpage;
+
+ struct device_node *np;
+ ctrldev = &pdev->dev;
+ ctrlpriv = dev_get_drvdata(ctrldev);
+
+ /*
+ * Set up the private block for secure memory
+ * Only one instance is possible
+ */
+ smpriv = kzalloc(sizeof(struct caam_drv_private_sm), GFP_KERNEL);
+ if (smpriv == NULL) {
+ dev_err(ctrldev, "can't alloc private mem for secure memory\n");
+ return -ENOMEM;
+ }
+ smpriv->parentdev = ctrldev; /* copy of parent dev is handy */
+
+ /* Create the dev */
+#ifdef CONFIG_OF
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm");
+ sm_pdev = of_platform_device_create(np, "caam_sm", ctrldev);
+#else
+ sm_pdev = platform_device_register_data(ctrldev, "caam_sm", 0,
+ smpriv,
+ sizeof(struct caam_drv_private_sm));
+#endif
+ if (sm_pdev == NULL) {
+ kfree(smpriv);
+ return -EINVAL;
+ }
+ smdev = &sm_pdev->dev;
+ dev_set_drvdata(smdev, smpriv);
+ ctrlpriv->smdev = smdev;
+
+ /*
+ * Collect configuration limit data for reference
+ * This batch comes from the partition data/vid registers in perfmon
+ */
+ smpriv->max_pages = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart)
+ & SMPART_MAX_NUMPG_MASK) >>
+ SMPART_MAX_NUMPG_SHIFT) + 1;
+ smpriv->top_partition = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart)
+ & SMPART_MAX_PNUM_MASK) >>
+ SMPART_MAX_PNUM_SHIFT) + 1;
+ smpriv->top_page = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart)
+ & SMPART_MAX_PG_MASK) >> SMPART_MAX_PG_SHIFT) + 1;
+ smpriv->page_size = 1024 << ((rd_reg32(&ctrlpriv->ctrl->perfmon.smvid)
+ & SMVID_PG_SIZE_MASK) >> SMVID_PG_SIZE_SHIFT);
+ smpriv->slot_size = 1 << CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE;
+
+#ifdef SM_DEBUG
+ dev_info(smdev, "max pages = %d, top partition = %d\n",
+ smpriv->max_pages, smpriv->top_partition);
+ dev_info(smdev, "top page = %d, page size = %d (total = %d)\n",
+ smpriv->top_page, smpriv->page_size,
+ smpriv->top_page * smpriv->page_size);
+ dev_info(smdev, "selected slot size = %d\n", smpriv->slot_size);
+#endif
+
+ /*
+ * Now probe for partitions/pages to which we have access. Note that
+ * these have likely been set up by a bootloader or platform
+ * provisioning application, so we have to assume that we "inherit"
+ * a configuration and work within the constraints of what it might be.
+ *
+ * Assume use of the zeroth ring in the present iteration (until
+ * we can divorce the controller and ring drivers, and then assign
+ * an SM instance to any ring instance).
+ */
+ smpriv->smringdev = ctrlpriv->jrdev[0];
+ jrpriv = dev_get_drvdata(smpriv->smringdev);
+ lpagect = 0;
+ lpagedesc = kzalloc(sizeof(struct sm_page_descriptor)
+ * smpriv->max_pages, GFP_KERNEL);
+ if (lpagedesc == NULL) {
+ kfree(smpriv);
+ return -ENOMEM;
+ }
+
+ for (page = 0; page < smpriv->max_pages; page++) {
+ wr_reg32(&jrpriv->rregs->sm_cmd,
+ ((page << SMC_PAGE_SHIFT) & SMC_PAGE_MASK) |
+ (SMC_CMD_PAGE_INQUIRY & SMC_CMD_MASK));
+ pgstat = rd_reg32(&jrpriv->rregs->sm_status);
+ if (((pgstat & SMCS_PGWON_MASK) >> SMCS_PGOWN_SHIFT)
+ == SMCS_PGOWN_OWNED) { /* our page? */
+ lpagedesc[page].phys_pagenum =
+ (pgstat & SMCS_PAGE_MASK) >> SMCS_PAGE_SHIFT;
+ lpagedesc[page].own_part =
+ (pgstat & SMCS_PART_SHIFT) >> SMCS_PART_MASK;
+ lpagedesc[page].pg_base = ctrlpriv->sm_base +
+ ((smpriv->page_size * page) / sizeof(u32));
+ lpagect++;
+#ifdef SM_DEBUG
+ dev_info(smdev,
+ "physical page %d, owning partition = %d\n",
+ lpagedesc[page].phys_pagenum,
+ lpagedesc[page].own_part);
+#endif
+ }
+ }
+
+ smpriv->pagedesc = kzalloc(sizeof(struct sm_page_descriptor) * lpagect,
+ GFP_KERNEL);
+ if (smpriv->pagedesc == NULL) {
+ kfree(lpagedesc);
+ kfree(smpriv);
+ return -ENOMEM;
+ }
+ smpriv->localpages = lpagect;
+
+ detectedpage = 0;
+ for (page = 0; page < smpriv->max_pages; page++) {
+ if (lpagedesc[page].pg_base != NULL) { /* e.g. live entry */
+ memcpy(&smpriv->pagedesc[detectedpage],
+ &lpagedesc[page],
+ sizeof(struct sm_page_descriptor));
+#ifdef SM_DEBUG_CONT
+ sm_show_page(smdev, &smpriv->pagedesc[detectedpage]);
+#endif
+ detectedpage++;
+ }
+ }
+
+ kfree(lpagedesc);
+
+ sm_init_keystore(smdev);
+
+ return 0;
+}
+
+void caam_sm_shutdown(struct platform_device *pdev)
+{
+ struct device *ctrldev, *smdev;
+ struct caam_drv_private *priv;
+ struct caam_drv_private_sm *smpriv;
+
+ ctrldev = &pdev->dev;
+ priv = dev_get_drvdata(ctrldev);
+ smdev = priv->smdev;
+ smpriv = dev_get_drvdata(smdev);
+
+ kfree(smpriv->pagedesc);
+ kfree(smpriv);
+}
+EXPORT_SYMBOL(caam_sm_shutdown);
+#ifdef CONFIG_OF
+static void __exit caam_sm_exit(void)
+{
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+ if (!dev_node)
+ return;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return;
+
+ of_node_put(dev_node);
+
+ caam_sm_shutdown(pdev);
+
+ return;
+}
+
+static int __init caam_sm_init(void)
+{
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ /*
+ * Do of_find_compatible_node() then of_find_device_by_node()
+ * once a functional device tree is available
+ */
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+ if (!dev_node)
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return -ENODEV;
+
+ of_node_get(dev_node);
+
+ caam_sm_startup(pdev);
+
+ return 0;
+}
+
+module_init(caam_sm_init);
+module_exit(caam_sm_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL CAAM Secure Memory / Keystore");
+MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD");
+#endif
diff --git a/drivers/crypto/caam/sm_test.c b/drivers/crypto/caam/sm_test.c
new file mode 100644
index 000000000000..43590ca52155
--- /dev/null
+++ b/drivers/crypto/caam/sm_test.c
@@ -0,0 +1,844 @@
+/*
+ * Secure Memory / Keystore Exemplification Module
+ * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved
+ *
+ * Serves as a functional example, and as a self-contained unit test for
+ * the functionality contained in sm_store.c.
+ *
+ * The example function, caam_sm_example_init(), runs a thread that:
+ *
+ * - initializes a set of fixed keys
+ * - stores one copy in clear buffers
+ * - stores them again in secure memory
+ * - extracts stored keys back out for use
+ * - intializes 3 data buffers for a test:
+ * (1) containing cleartext
+ * (2) to hold ciphertext encrypted with an extracted black key
+ * (3) to hold extracted cleartext decrypted with an equivalent clear key
+ *
+ * The function then builds simple job descriptors that reference the key
+ * material and buffers as initialized, and executes an encryption job
+ * with a black key, and a decryption job using a the same key held in the
+ * clear. The output of the decryption job is compared to the original
+ * cleartext; if they don't compare correctly, one can assume a key problem
+ * exists, where the function will exit with an error.
+ *
+ * This module can use a substantial amount of refactoring, which may occur
+ * after the API gets some mileage. Furthermore, expect this module to
+ * eventually disappear once the API is integrated into "real" software.
+ */
+
+#include "compat.h"
+#include "intern.h"
+#include "desc.h"
+#include "error.h"
+#include "jr.h"
+#include "sm.h"
+
+static u8 skeymod[] = {
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+static u8 symkey[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+};
+
+static u8 symdata[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x0f, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+static int mk_job_desc(u32 *desc, dma_addr_t key, u16 keysz, dma_addr_t indata,
+ dma_addr_t outdata, u16 sz, u32 cipherdir, u32 keymode)
+{
+ desc[1] = CMD_KEY | CLASS_1 | (keysz & KEY_LENGTH_MASK) | keymode;
+ desc[2] = (u32)key;
+ desc[3] = CMD_OPERATION | OP_TYPE_CLASS1_ALG | OP_ALG_AAI_ECB |
+ cipherdir;
+ desc[4] = CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1 | sz;
+ desc[5] = (u32)indata;
+ desc[6] = CMD_FIFO_STORE | FIFOST_TYPE_MESSAGE_DATA | sz;
+ desc[7] = (u32)outdata;
+
+ desc[0] = CMD_DESC_HDR | HDR_ONE | (8 & HDR_DESCLEN_MASK);
+ return 8 * sizeof(u32);
+}
+
+struct exec_test_result {
+ int error;
+ struct completion completion;
+};
+
+void exec_test_done(struct device *dev, u32 *desc, u32 err, void *context)
+{
+ struct exec_test_result *res = context;
+
+ if (err) {
+ char tmp[CAAM_ERROR_STR_MAX];
+ dev_err(dev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
+ }
+
+ res->error = err;
+ complete(&res->completion);
+}
+
+static int exec_test_job(struct device *ksdev, u32 *jobdesc)
+{
+ struct exec_test_result testres;
+ struct caam_drv_private_sm *kspriv;
+ int rtn = 0;
+
+ kspriv = dev_get_drvdata(ksdev);
+
+ init_completion(&testres.completion);
+
+ rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, exec_test_done,
+ &testres);
+ if (!rtn) {
+ wait_for_completion_interruptible(&testres.completion);
+ rtn = testres.error;
+ }
+ return rtn;
+}
+
+
+int caam_sm_example_init(struct platform_device *pdev)
+{
+ struct device *ctrldev, *ksdev;
+ struct caam_drv_private *ctrlpriv;
+ struct caam_drv_private_sm *kspriv;
+ u32 unit, units, jdescsz;
+ int stat, jstat, rtnval = 0;
+ u8 __iomem *syminp, *symint, *symout = NULL;
+ dma_addr_t syminp_dma, symint_dma, symout_dma;
+ u8 __iomem *black_key_des, *black_key_aes128;
+ u8 __iomem *black_key_aes256;
+ dma_addr_t black_key_des_dma, black_key_aes128_dma;
+ dma_addr_t black_key_aes256_dma;
+ u8 __iomem *clear_key_des, *clear_key_aes128, *clear_key_aes256;
+ dma_addr_t clear_key_des_dma, clear_key_aes128_dma;
+ dma_addr_t clear_key_aes256_dma;
+ u32 __iomem *jdesc;
+ u32 keyslot_des, keyslot_aes128, keyslot_aes256 = 0;
+
+ jdesc = NULL;
+ black_key_des = black_key_aes128 = black_key_aes256 = NULL;
+ clear_key_des = clear_key_aes128 = clear_key_aes256 = NULL;
+
+ /* We can lose this cruft once we can get a pdev by name */
+ ctrldev = &pdev->dev;
+ ctrlpriv = dev_get_drvdata(ctrldev);
+ ksdev = ctrlpriv->smdev;
+ kspriv = dev_get_drvdata(ksdev);
+ if (kspriv == NULL)
+ return -ENODEV;
+
+ /* Now that we have the dev for the single SM instance, connect */
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test_init() running\n");
+#endif
+ /* Probe to see what keystores are available to us */
+ units = sm_detect_keystore_units(ksdev);
+ if (!units)
+ dev_err(ksdev, "caam_sm_test: no keystore units available\n");
+
+ /*
+ * MX6 bootloader stores some stuff in unit 0, so let's
+ * use 1 or above
+ */
+ if (units < 2) {
+ dev_err(ksdev, "caam_sm_test: insufficient keystore units\n");
+ return -ENODEV;
+ }
+ unit = 1;
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: %d keystore units available\n", units);
+#endif
+
+ /* Initialize/Establish Keystore */
+ sm_establish_keystore(ksdev, unit); /* Initalize store in #1 */
+
+ /*
+ * Top of main test thread
+ */
+
+ /* Allocate test data blocks (input, intermediate, output) */
+ syminp = kmalloc(256, GFP_KERNEL | GFP_DMA);
+ symint = kmalloc(256, GFP_KERNEL | GFP_DMA);
+ symout = kmalloc(256, GFP_KERNEL | GFP_DMA);
+ if ((syminp == NULL) || (symint == NULL) || (symout == NULL)) {
+ rtnval = -ENOMEM;
+ dev_err(ksdev, "caam_sm_test: can't get test data buffers\n");
+ goto freemem;
+ }
+
+ /* Allocate storage for 3 black keys: encapsulated 8, 16, 32 */
+ black_key_des = kmalloc(16, GFP_KERNEL | GFP_DMA); /* padded to 16... */
+ black_key_aes128 = kmalloc(16, GFP_KERNEL | GFP_DMA);
+ black_key_aes256 = kmalloc(16, GFP_KERNEL | GFP_DMA);
+ if ((black_key_des == NULL) || (black_key_aes128 == NULL) ||
+ (black_key_aes256 == NULL)) {
+ rtnval = -ENOMEM;
+ dev_err(ksdev, "caam_sm_test: can't black key buffers\n");
+ goto freemem;
+ }
+
+ clear_key_des = kmalloc(8, GFP_KERNEL | GFP_DMA);
+ clear_key_aes128 = kmalloc(16, GFP_KERNEL | GFP_DMA);
+ clear_key_aes256 = kmalloc(32, GFP_KERNEL | GFP_DMA);
+ if ((clear_key_des == NULL) || (clear_key_aes128 == NULL) ||
+ (clear_key_aes256 == NULL)) {
+ rtnval = -ENOMEM;
+ dev_err(ksdev, "caam_sm_test: can't get clear key buffers\n");
+ goto freemem;
+ }
+
+ /* Allocate storage for job descriptor */
+ jdesc = kmalloc(8 * sizeof(u32), GFP_KERNEL | GFP_DMA);
+ if (jdesc == NULL) {
+ rtnval = -ENOMEM;
+ dev_err(ksdev, "caam_sm_test: can't get descriptor buffers\n");
+ goto freemem;
+ }
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: all buffers allocated\n");
+#endif
+
+ /* Load up input data block, clear outputs */
+ memcpy(syminp, symdata, 256);
+ memset(symint, 0, 256);
+ memset(symout, 0, 256);
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[0], syminp[1], syminp[2], syminp[3],
+ syminp[4], syminp[5], syminp[6], syminp[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[0], symout[1], symout[2], symout[3],
+ symout[4], symout[5], symout[6], symout[7]);
+
+ dev_info(ksdev, "caam_sm_test: data buffers initialized\n");
+#endif
+
+ /* Load up clear keys */
+ memcpy(clear_key_des, symkey, 8);
+ memcpy(clear_key_aes128, symkey, 16);
+ memcpy(clear_key_aes256, symkey, 32);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: all clear keys loaded\n");
+#endif
+
+ /*
+ * Place clear keys in keystore.
+ * All the interesting stuff happens here.
+ */
+ /* 8 bit DES key */
+ stat = sm_keystore_slot_alloc(ksdev, unit, 8, &keyslot_des);
+ if (stat)
+ goto freemem;
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: 8 byte key slot in %d\n", keyslot_des);
+#endif
+ stat = sm_keystore_slot_load(ksdev, unit, keyslot_des, clear_key_des,
+ 8);
+ if (stat) {
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: can't load 8 byte key in %d\n",
+ keyslot_des);
+#endif
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des);
+ goto freemem;
+ }
+
+ /* 16 bit AES key */
+ stat = sm_keystore_slot_alloc(ksdev, unit, 16, &keyslot_aes128);
+ if (stat) {
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des);
+ goto freemem;
+ }
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: 16 byte key slot in %d\n",
+ keyslot_aes128);
+#endif
+ stat = sm_keystore_slot_load(ksdev, unit, keyslot_aes128,
+ clear_key_aes128, 16);
+ if (stat) {
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: can't load 16 byte key in %d\n",
+ keyslot_aes128);
+#endif
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128);
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des);
+ goto freemem;
+ }
+
+ /* 32 bit AES key */
+ stat = sm_keystore_slot_alloc(ksdev, unit, 32, &keyslot_aes256);
+ if (stat) {
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128);
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des);
+ goto freemem;
+ }
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: 32 byte key slot in %d\n",
+ keyslot_aes256);
+#endif
+ stat = sm_keystore_slot_load(ksdev, unit, keyslot_aes256,
+ clear_key_aes256, 32);
+ if (stat) {
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: can't load 32 byte key in %d\n",
+ keyslot_aes128);
+#endif
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes256);
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128);
+ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des);
+ goto freemem;
+ }
+
+ /* Encapsulate all keys as SM blobs */
+ stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_des,
+ keyslot_des, 8, skeymod, 8);
+ if (stat) {
+ dev_info(ksdev, "caam_sm_test: can't encapsulate DES key\n");
+ goto freekeys;
+ }
+
+ stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_aes128,
+ keyslot_aes128, 16, skeymod, 8);
+ if (stat) {
+ dev_info(ksdev, "caam_sm_test: can't encapsulate AES128 key\n");
+ goto freekeys;
+ }
+
+ stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_aes256,
+ keyslot_aes256, 32, skeymod, 8);
+ if (stat) {
+ dev_info(ksdev, "caam_sm_test: can't encapsulate AES256 key\n");
+ goto freekeys;
+ }
+
+ /* Now decapsulate as black key blobs */
+ stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_des,
+ keyslot_des, 8, skeymod, 8);
+ if (stat) {
+ dev_info(ksdev, "caam_sm_test: can't decapsulate DES key\n");
+ goto freekeys;
+ }
+
+ stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_aes128,
+ keyslot_aes128, 16, skeymod, 8);
+ if (stat) {
+ dev_info(ksdev, "caam_sm_test: can't decapsulate AES128 key\n");
+ goto freekeys;
+ }
+
+ stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_aes256,
+ keyslot_aes256, 32, skeymod, 8);
+ if (stat) {
+ dev_info(ksdev, "caam_sm_test: can't decapsulate AES128 key\n");
+ goto freekeys;
+ }
+
+ /* Extract 8/16/32 byte black keys */
+ sm_keystore_slot_read(ksdev, unit, keyslot_des, 8, black_key_des);
+ sm_keystore_slot_read(ksdev, unit, keyslot_aes128, 16,
+ black_key_aes128);
+ sm_keystore_slot_read(ksdev, unit, keyslot_aes256, 32,
+ black_key_aes256);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: all black keys extracted\n");
+#endif
+
+ /* DES encrypt using 8 byte black key */
+ black_key_des_dma = dma_map_single(ksdev, black_key_des, 8,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, black_key_des_dma, 8, DMA_TO_DEVICE);
+ syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE);
+ symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE);
+
+ jdescsz = mk_job_desc(jdesc, black_key_des_dma, 8, syminp_dma,
+ symint_dma, 256,
+ OP_ALG_ENCRYPT | OP_ALG_ALGSEL_DES, 0);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "jobdesc:\n");
+ dev_info(ksdev, "0x%08x\n", jdesc[0]);
+ dev_info(ksdev, "0x%08x\n", jdesc[1]);
+ dev_info(ksdev, "0x%08x\n", jdesc[2]);
+ dev_info(ksdev, "0x%08x\n", jdesc[3]);
+ dev_info(ksdev, "0x%08x\n", jdesc[4]);
+ dev_info(ksdev, "0x%08x\n", jdesc[5]);
+ dev_info(ksdev, "0x%08x\n", jdesc[6]);
+ dev_info(ksdev, "0x%08x\n", jdesc[7]);
+#endif
+
+ jstat = exec_test_job(ksdev, jdesc);
+
+ dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE);
+ dma_unmap_single(ksdev, black_key_des_dma, 8, DMA_TO_DEVICE);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "input block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[0], syminp[1], syminp[2], syminp[3],
+ syminp[4], syminp[5], syminp[6], syminp[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[8], syminp[9], syminp[10], syminp[11],
+ syminp[12], syminp[13], syminp[14], syminp[15]);
+ dev_info(ksdev, "intermediate block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[8], symint[9], symint[10], symint[11],
+ symint[12], symint[13], symint[14], symint[15]);
+ dev_info(ksdev, "caam_sm_test: encrypt cycle with 8 byte key\n");
+#endif
+
+ /* DES decrypt using 8 byte clear key */
+ clear_key_des_dma = dma_map_single(ksdev, clear_key_des, 8,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, clear_key_des_dma, 8, DMA_TO_DEVICE);
+ symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE);
+ symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE);
+
+ jdescsz = mk_job_desc(jdesc, clear_key_des_dma, 8, symint_dma,
+ symout_dma, 256,
+ OP_ALG_DECRYPT | OP_ALG_ALGSEL_DES, 0);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "jobdesc:\n");
+ dev_info(ksdev, "0x%08x\n", jdesc[0]);
+ dev_info(ksdev, "0x%08x\n", jdesc[1]);
+ dev_info(ksdev, "0x%08x\n", jdesc[2]);
+ dev_info(ksdev, "0x%08x\n", jdesc[3]);
+ dev_info(ksdev, "0x%08x\n", jdesc[4]);
+ dev_info(ksdev, "0x%08x\n", jdesc[5]);
+ dev_info(ksdev, "0x%08x\n", jdesc[6]);
+ dev_info(ksdev, "0x%08x\n", jdesc[7]);
+#endif
+
+ jstat = exec_test_job(ksdev, jdesc);
+
+ dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE);
+ dma_unmap_single(ksdev, clear_key_des_dma, 8, DMA_TO_DEVICE);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "intermediate block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[8], symint[9], symint[10], symint[11],
+ symint[12], symint[13], symint[14], symint[15]);
+ dev_info(ksdev, "decrypted block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[0], symout[1], symout[2], symout[3],
+ symout[4], symout[5], symout[6], symout[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[8], symout[9], symout[10], symout[11],
+ symout[12], symout[13], symout[14], symout[15]);
+ dev_info(ksdev, "caam_sm_test: decrypt cycle with 8 byte key\n");
+#endif
+
+ /* Check result */
+ if (memcmp(symout, syminp, 256)) {
+ dev_info(ksdev, "caam_sm_test: 8-byte key test mismatch\n");
+ rtnval = -1;
+ goto freekeys;
+ } else
+ dev_info(ksdev, "caam_sm_test: 8-byte key test match OK\n");
+
+ /* AES-128 encrypt using 16 byte black key */
+ black_key_aes128_dma = dma_map_single(ksdev, black_key_aes128, 16,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, black_key_aes128_dma, 16,
+ DMA_TO_DEVICE);
+ syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE);
+ symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE);
+
+ jdescsz = mk_job_desc(jdesc, black_key_aes128_dma, 16, syminp_dma,
+ symint_dma, 256,
+ OP_ALG_ENCRYPT | OP_ALG_ALGSEL_AES, 0);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "jobdesc:\n");
+ dev_info(ksdev, "0x%08x\n", jdesc[0]);
+ dev_info(ksdev, "0x%08x\n", jdesc[1]);
+ dev_info(ksdev, "0x%08x\n", jdesc[2]);
+ dev_info(ksdev, "0x%08x\n", jdesc[3]);
+ dev_info(ksdev, "0x%08x\n", jdesc[4]);
+ dev_info(ksdev, "0x%08x\n", jdesc[5]);
+ dev_info(ksdev, "0x%08x\n", jdesc[6]);
+ dev_info(ksdev, "0x%08x\n", jdesc[7]);
+#endif
+
+ jstat = exec_test_job(ksdev, jdesc);
+
+ dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE);
+ dma_unmap_single(ksdev, black_key_aes128_dma, 16, DMA_TO_DEVICE);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "input block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[0], syminp[1], syminp[2], syminp[3],
+ syminp[4], syminp[5], syminp[6], syminp[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[8], syminp[9], syminp[10], syminp[11],
+ syminp[12], syminp[13], syminp[14], syminp[15]);
+ dev_info(ksdev, "intermediate block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[8], symint[9], symint[10], symint[11],
+ symint[12], symint[13], symint[14], symint[15]);
+ dev_info(ksdev, "caam_sm_test: encrypt cycle with 16 byte key\n");
+#endif
+
+ /* AES-128 decrypt using 16 byte clear key */
+ clear_key_aes128_dma = dma_map_single(ksdev, clear_key_aes128, 16,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, clear_key_aes128_dma, 16,
+ DMA_TO_DEVICE);
+ symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE);
+ symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE);
+
+ jdescsz = mk_job_desc(jdesc, clear_key_aes128_dma, 16, symint_dma,
+ symout_dma, 256,
+ OP_ALG_DECRYPT | OP_ALG_ALGSEL_AES, 0);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "jobdesc:\n");
+ dev_info(ksdev, "0x%08x\n", jdesc[0]);
+ dev_info(ksdev, "0x%08x\n", jdesc[1]);
+ dev_info(ksdev, "0x%08x\n", jdesc[2]);
+ dev_info(ksdev, "0x%08x\n", jdesc[3]);
+ dev_info(ksdev, "0x%08x\n", jdesc[4]);
+ dev_info(ksdev, "0x%08x\n", jdesc[5]);
+ dev_info(ksdev, "0x%08x\n", jdesc[6]);
+ dev_info(ksdev, "0x%08x\n", jdesc[7]);
+#endif
+ jstat = exec_test_job(ksdev, jdesc);
+
+ dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE);
+ dma_unmap_single(ksdev, clear_key_aes128_dma, 16, DMA_TO_DEVICE);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "intermediate block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[8], symint[9], symint[10], symint[11],
+ symint[12], symint[13], symint[14], symint[15]);
+ dev_info(ksdev, "decrypted block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[0], symout[1], symout[2], symout[3],
+ symout[4], symout[5], symout[6], symout[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[8], symout[9], symout[10], symout[11],
+ symout[12], symout[13], symout[14], symout[15]);
+ dev_info(ksdev, "caam_sm_test: decrypt cycle with 16 byte key\n");
+#endif
+
+ /* Check result */
+ if (memcmp(symout, syminp, 256)) {
+ dev_info(ksdev, "caam_sm_test: 16-byte key test mismatch\n");
+ rtnval = -1;
+ goto freekeys;
+ } else
+ dev_info(ksdev, "caam_sm_test: 16-byte key test match OK\n");
+
+ /* AES-256 encrypt using 32 byte black key */
+ black_key_aes256_dma = dma_map_single(ksdev, black_key_aes256, 32,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, black_key_aes256_dma, 32,
+ DMA_TO_DEVICE);
+ syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE);
+ symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE);
+
+ jdescsz = mk_job_desc(jdesc, black_key_aes256_dma, 32, syminp_dma,
+ symint_dma, 256,
+ OP_ALG_ENCRYPT | OP_ALG_ALGSEL_AES, 0);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "jobdesc:\n");
+ dev_info(ksdev, "0x%08x\n", jdesc[0]);
+ dev_info(ksdev, "0x%08x\n", jdesc[1]);
+ dev_info(ksdev, "0x%08x\n", jdesc[2]);
+ dev_info(ksdev, "0x%08x\n", jdesc[3]);
+ dev_info(ksdev, "0x%08x\n", jdesc[4]);
+ dev_info(ksdev, "0x%08x\n", jdesc[5]);
+ dev_info(ksdev, "0x%08x\n", jdesc[6]);
+ dev_info(ksdev, "0x%08x\n", jdesc[7]);
+#endif
+
+ jstat = exec_test_job(ksdev, jdesc);
+
+ dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE);
+ dma_unmap_single(ksdev, black_key_aes256_dma, 32, DMA_TO_DEVICE);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "input block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[0], syminp[1], syminp[2], syminp[3],
+ syminp[4], syminp[5], syminp[6], syminp[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ syminp[8], syminp[9], syminp[10], syminp[11],
+ syminp[12], syminp[13], syminp[14], syminp[15]);
+ dev_info(ksdev, "intermediate block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[8], symint[9], symint[10], symint[11],
+ symint[12], symint[13], symint[14], symint[15]);
+ dev_info(ksdev, "caam_sm_test: encrypt cycle with 32 byte key\n");
+#endif
+
+ /* AES-256 decrypt using 32-byte black key */
+ clear_key_aes256_dma = dma_map_single(ksdev, clear_key_aes256, 32,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, clear_key_aes256_dma, 32,
+ DMA_TO_DEVICE);
+ symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE);
+ dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE);
+ symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE);
+
+ jdescsz = mk_job_desc(jdesc, clear_key_aes256_dma, 32, symint_dma,
+ symout_dma, 256,
+ OP_ALG_DECRYPT | OP_ALG_ALGSEL_AES, 0);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "jobdesc:\n");
+ dev_info(ksdev, "0x%08x\n", jdesc[0]);
+ dev_info(ksdev, "0x%08x\n", jdesc[1]);
+ dev_info(ksdev, "0x%08x\n", jdesc[2]);
+ dev_info(ksdev, "0x%08x\n", jdesc[3]);
+ dev_info(ksdev, "0x%08x\n", jdesc[4]);
+ dev_info(ksdev, "0x%08x\n", jdesc[5]);
+ dev_info(ksdev, "0x%08x\n", jdesc[6]);
+ dev_info(ksdev, "0x%08x\n", jdesc[7]);
+#endif
+
+ jstat = exec_test_job(ksdev, jdesc);
+
+ dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE);
+ dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE);
+ dma_unmap_single(ksdev, clear_key_aes256_dma, 32, DMA_TO_DEVICE);
+
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "intermediate block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[0], symint[1], symint[2], symint[3],
+ symint[4], symint[5], symint[6], symint[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symint[8], symint[9], symint[10], symint[11],
+ symint[12], symint[13], symint[14], symint[15]);
+ dev_info(ksdev, "decrypted block:\n");
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[0], symout[1], symout[2], symout[3],
+ symout[4], symout[5], symout[6], symout[7]);
+ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ symout[8], symout[9], symout[10], symout[11],
+ symout[12], symout[13], symout[14], symout[15]);
+ dev_info(ksdev, "caam_sm_test: decrypt cycle with 32 byte key\n");
+#endif
+
+ /* Check result */
+ if (memcmp(symout, syminp, 256)) {
+ dev_info(ksdev, "caam_sm_test: 32-byte key test mismatch\n");
+ rtnval = -1;
+ goto freekeys;
+ } else
+ dev_info(ksdev, "caam_sm_test: 32-byte key test match OK\n");
+
+
+ /* Remove 8/16/32 byte keys from keystore */
+freekeys:
+ stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_des);
+ if (stat)
+ dev_info(ksdev, "caam_sm_test: can't release slot %d\n",
+ keyslot_des);
+
+ stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128);
+ if (stat)
+ dev_info(ksdev, "caam_sm_test: can't release slot %d\n",
+ keyslot_aes128);
+
+ stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes256);
+ if (stat)
+ dev_info(ksdev, "caam_sm_test: can't release slot %d\n",
+ keyslot_aes256);
+
+
+ /* Free resources */
+freemem:
+#ifdef SM_TEST_DETAIL
+ dev_info(ksdev, "caam_sm_test: cleaning up\n");
+#endif
+ kfree(syminp);
+ kfree(symint);
+ kfree(symout);
+ kfree(clear_key_des);
+ kfree(clear_key_aes128);
+ kfree(clear_key_aes256);
+ kfree(black_key_des);
+ kfree(black_key_aes128);
+ kfree(black_key_aes256);
+ kfree(jdesc);
+
+ /* Disconnect from keystore and leave */
+ sm_release_keystore(ksdev, unit);
+
+ return rtnval;
+}
+EXPORT_SYMBOL(caam_sm_example_init);
+
+void caam_sm_example_shutdown(void)
+{
+ /* unused in present version */
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ /*
+ * Do of_find_compatible_node() then of_find_device_by_node()
+ * once a functional device tree is available
+ */
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+ if (!dev_node)
+ return;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return;
+
+ of_node_get(dev_node);
+
+}
+
+static int __init caam_sm_test_init(void)
+{
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ /*
+ * Do of_find_compatible_node() then of_find_device_by_node()
+ * once a functional device tree is available
+ */
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+ if (!dev_node)
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return -ENODEV;
+
+ of_node_put(dev_node);
+
+ caam_sm_example_init(pdev);
+
+ return 0;
+}
+
+
+/* Module-based initialization needs to wait for dev tree */
+#ifdef CONFIG_OF
+module_init(caam_sm_test_init);
+module_exit(caam_sm_example_shutdown);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL CAAM Keystore Usage Example");
+MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD");
+#endif
diff --git a/drivers/crypto/caam/snvsregs.h b/drivers/crypto/caam/snvsregs.h
new file mode 100644
index 000000000000..677e8906e3d0
--- /dev/null
+++ b/drivers/crypto/caam/snvsregs.h
@@ -0,0 +1,237 @@
+/*
+ * SNVS hardware register-level view
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved
+ */
+
+#ifndef SNVSREGS_H
+#define SNVSREGS_H
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+/*
+ * SNVS High Power Domain
+ * Includes security violations, HA counter, RTC, alarm
+ */
+struct snvs_hp {
+ u32 lock;
+ u32 cmd;
+ u32 ctl;
+ u32 secvio_int_en; /* Security Violation Interrupt Enable */
+ u32 secvio_int_ctl; /* Security Violation Interrupt Control */
+ u32 status;
+ u32 secvio_status; /* Security Violation Status */
+ u32 ha_counteriv; /* High Assurance Counter IV */
+ u32 ha_counter; /* High Assurance Counter */
+ u32 rtc_msb; /* Real Time Clock/Counter MSB */
+ u32 rtc_lsb; /* Real Time Counter LSB */
+ u32 time_alarm_msb; /* Time Alarm MSB */
+ u32 time_alarm_lsb; /* Time Alarm LSB */
+};
+
+#define HP_LOCK_HAC_LCK 0x00040000
+#define HP_LOCK_HPSICR_LCK 0x00020000
+#define HP_LOCK_HPSVCR_LCK 0x00010000
+#define HP_LOCK_MKEYSEL_LCK 0x00000200
+#define HP_LOCK_TAMPCFG_LCK 0x00000100
+#define HP_LOCK_TAMPFLT_LCK 0x00000080
+#define HP_LOCK_SECVIO_LCK 0x00000040
+#define HP_LOCK_GENP_LCK 0x00000020
+#define HP_LOCK_MONOCTR_LCK 0x00000010
+#define HP_LOCK_CALIB_LCK 0x00000008
+#define HP_LOCK_SRTC_LCK 0x00000004
+#define HP_LOCK_ZMK_RD_LCK 0x00000002
+#define HP_LOCK_ZMK_WT_LCK 0x00000001
+
+#define HP_CMD_NONPRIV_AXS 0x80000000
+#define HP_CMD_HAC_STOP 0x00080000
+#define HP_CMD_HAC_CLEAR 0x00040000
+#define HP_CMD_HAC_LOAD 0x00020000
+#define HP_CMD_HAC_CFG_EN 0x00010000
+#define HP_CMD_SNVS_MSTR_KEY 0x00002000
+#define HP_CMD_PROG_ZMK 0x00001000
+#define HP_CMD_SW_LPSV 0x00000400
+#define HP_CMD_SW_FSV 0x00000200
+#define HP_CMD_SW_SV 0x00000100
+#define HP_CMD_LP_SWR_DIS 0x00000020
+#define HP_CMD_LP_SWR 0x00000010
+#define HP_CMD_SSM_SFNS_DIS 0x00000004
+#define HP_CMD_SSM_ST_DIS 0x00000002
+#define HP_CMD_SMM_ST 0x00000001
+
+#define HP_CTL_TIME_SYNC 0x00010000
+#define HP_CTL_CAL_VAL_SHIFT 10
+#define HP_CTL_CAL_VAL_MASK (0x1f << HP_CTL_CALIB_SHIFT)
+#define HP_CTL_CALIB_EN 0x00000100
+#define HP_CTL_PI_FREQ_SHIFT 4
+#define HP_CTL_PI_FREQ_MASK (0xf << HP_CTL_PI_FREQ_SHIFT)
+#define HP_CTL_PI_EN 0x00000008
+#define HP_CTL_TIMEALARM_EN 0x00000002
+#define HP_CTL_RTC_EN 0x00000001
+
+#define HP_SECVIO_INTEN_EN 0x10000000
+#define HP_SECVIO_INTEN_SRC5 0x00000020
+#define HP_SECVIO_INTEN_SRC4 0x00000010
+#define HP_SECVIO_INTEN_SRC3 0x00000008
+#define HP_SECVIO_INTEN_SRC2 0x00000004
+#define HP_SECVIO_INTEN_SRC1 0x00000002
+#define HP_SECVIO_INTEN_SRC0 0x00000001
+#define HP_SECVIO_INTEN_ALL 0x8000003f
+
+#define HP_SECVIO_ICTL_CFG_SHIFT 30
+#define HP_SECVIO_ICTL_CFG_MASK (0x3 << HP_SECVIO_ICTL_CFG_SHIFT)
+#define HP_SECVIO_ICTL_CFG5_SHIFT 5
+#define HP_SECVIO_ICTL_CFG5_MASK (0x3 << HP_SECVIO_ICTL_CFG5_SHIFT)
+#define HP_SECVIO_ICTL_CFG_DISABLE 0
+#define HP_SECVIO_ICTL_CFG_NONFATAL 1
+#define HP_SECVIO_ICTL_CFG_FATAL 2
+#define HP_SECVIO_ICTL_CFG4_FATAL 0x00000010
+#define HP_SECVIO_ICTL_CFG3_FATAL 0x00000008
+#define HP_SECVIO_ICTL_CFG2_FATAL 0x00000004
+#define HP_SECVIO_ICTL_CFG1_FATAL 0x00000002
+#define HP_SECVIO_ICTL_CFG0_FATAL 0x00000001
+
+#define HP_STATUS_ZMK_ZERO 0x80000000
+#define HP_STATUS_OTPMK_ZERO 0x08000000
+#define HP_STATUS_OTPMK_SYN_SHIFT 16
+#define HP_STATUS_OTPMK_SYN_MASK (0x1ff << HP_STATUS_OTPMK_SYN_SHIFT)
+#define HP_STATUS_SSM_ST_SHIFT 8
+#define HP_STATUS_SSM_ST_MASK (0xf << HP_STATUS_SSM_ST_SHIFT)
+#define HP_STATUS_SSM_ST_INIT 0
+#define HP_STATUS_SSM_ST_HARDFAIL 1
+#define HP_STATUS_SSM_ST_SOFTFAIL 3
+#define HP_STATUS_SSM_ST_INITINT 8
+#define HP_STATUS_SSM_ST_CHECK 9
+#define HP_STATUS_SSM_ST_NONSECURE 11
+#define HP_STATUS_SSM_ST_TRUSTED 13
+#define HP_STATUS_SSM_ST_SECURE 15
+
+#define HP_SECVIOST_ZMK_ECC_FAIL 0x08000000 /* write to clear */
+#define HP_SECVIOST_ZMK_SYN_SHIFT 16
+#define HP_SECVIOST_ZMK_SYN_MASK (0x1ff << HP_SECVIOST_ZMK_SYN_SHIFT)
+#define HP_SECVIOST_SECVIO5 0x00000020
+#define HP_SECVIOST_SECVIO4 0x00000010
+#define HP_SECVIOST_SECVIO3 0x00000008
+#define HP_SECVIOST_SECVIO2 0x00000004
+#define HP_SECVIOST_SECVIO1 0x00000002
+#define HP_SECVIOST_SECVIO0 0x00000001
+#define HP_SECVIOST_SECVIOMASK 0x0000003f
+
+/*
+ * SNVS Low Power Domain
+ * Includes glitch detector, SRTC, alarm, monotonic counter, ZMK
+ */
+struct snvs_lp {
+ u32 lock;
+ u32 ctl;
+ u32 mstr_key_ctl; /* Master Key Control */
+ u32 secvio_ctl; /* Security Violation Control */
+ u32 tamper_filt_cfg; /* Tamper Glitch Filters Configuration */
+ u32 tamper_det_cfg; /* Tamper Detectors Configuration */
+ u32 status;
+ u32 srtc_msb; /* Secure Real Time Clock/Counter MSB */
+ u32 srtc_lsb; /* Secure Real Time Clock/Counter LSB */
+ u32 time_alarm; /* Time Alarm */
+ u32 smc_msb; /* Secure Monotonic Counter MSB */
+ u32 smc_lsb; /* Secure Monotonic Counter LSB */
+ u32 pwr_glitch_det; /* Power Glitch Detector */
+ u32 gen_purpose;
+ u32 zmk[8]; /* Zeroizable Master Key */
+};
+
+#define LP_LOCK_MKEYSEL_LCK 0x00000200
+#define LP_LOCK_TAMPDET_LCK 0x00000100
+#define LP_LOCK_TAMPFLT_LCK 0x00000080
+#define LP_LOCK_SECVIO_LCK 0x00000040
+#define LP_LOCK_GENP_LCK 0x00000020
+#define LP_LOCK_MONOCTR_LCK 0x00000010
+#define LP_LOCK_CALIB_LCK 0x00000008
+#define LP_LOCK_SRTC_LCK 0x00000004
+#define LP_LOCK_ZMK_RD_LCK 0x00000002
+#define LP_LOCK_ZMK_WT_LCK 0x00000001
+
+#define LP_CTL_CAL_VAL_SHIFT 10
+#define LP_CTL_CAL_VAL_MASK (0x1f << LP_CTL_CAL_VAL_SHIFT)
+#define LP_CTL_CALIB_EN 0x00000100
+#define LP_CTL_SRTC_INVAL_EN 0x00000010
+#define LP_CTL_WAKE_INT_EN 0x00000008
+#define LP_CTL_MONOCTR_EN 0x00000004
+#define LP_CTL_TIMEALARM_EN 0x00000002
+#define LP_CTL_SRTC_EN 0x00000001
+
+#define LP_MKEYCTL_ZMKECC_SHIFT 8
+#define LP_MKEYCTL_ZMKECC_MASK (0xff << LP_MKEYCTL_ZMKECC_SHIFT)
+#define LP_MKEYCTL_ZMKECC_EN 0x00000010
+#define LP_MKEYCTL_ZMKECC_VAL 0x00000008
+#define LP_MKEYCTL_ZMKECC_PROG 0x00000004
+#define LP_MKEYCTL_MKSEL_SHIFT 0
+#define LP_MKEYCTL_MKSEL_MASK (3 << LP_MKEYCTL_MKSEL_SHIFT)
+#define LP_MKEYCTL_MK_OTP 0
+#define LP_MKEYCTL_MK_ZMK 2
+#define LP_MKEYCTL_MK_COMB 3
+
+#define LP_SECVIO_CTL_SRC5 0x20
+#define LP_SECVIO_CTL_SRC4 0x10
+#define LP_SECVIO_CTL_SRC3 0x08
+#define LP_SECVIO_CTL_SRC2 0x04
+#define LP_SECVIO_CTL_SRC1 0x02
+#define LP_SECVIO_CTL_SRC0 0x01
+
+#define LP_TAMPFILT_EXT2_EN 0x80000000
+#define LP_TAMPFILT_EXT2_SHIFT 24
+#define LP_TAMPFILT_EXT2_MASK (0x1f << LP_TAMPFILT_EXT2_SHIFT)
+#define LP_TAMPFILT_EXT1_EN 0x00800000
+#define LP_TAMPFILT_EXT1_SHIFT 16
+#define LP_TAMPFILT_EXT1_MASK (0x1f << LP_TAMPFILT_EXT1_SHIFT)
+#define LP_TAMPFILT_WM_EN 0x00000080
+#define LP_TAMPFILT_WM_SHIFT 0
+#define LP_TAMPFILT_WM_MASK (0x1f << LP_TAMPFILT_WM_SHIFT)
+
+#define LP_TAMPDET_OSC_BPS 0x10000000
+#define LP_TAMPDET_VRC_SHIFT 24
+#define LP_TAMPDET_VRC_MASK (3 << LP_TAMPFILT_VRC_SHIFT)
+#define LP_TAMPDET_HTDC_SHIFT 20
+#define LP_TAMPDET_HTDC_MASK (3 << LP_TAMPFILT_HTDC_SHIFT)
+#define LP_TAMPDET_LTDC_SHIFT 16
+#define LP_TAMPDET_LTDC_MASK (3 << LP_TAMPFILT_LTDC_SHIFT)
+#define LP_TAMPDET_POR_OBS 0x00008000
+#define LP_TAMPDET_PFD_OBS 0x00004000
+#define LP_TAMPDET_ET2_EN 0x00000400
+#define LP_TAMPDET_ET1_EN 0x00000200
+#define LP_TAMPDET_WMT2_EN 0x00000100
+#define LP_TAMPDET_WMT1_EN 0x00000080
+#define LP_TAMPDET_VT_EN 0x00000040
+#define LP_TAMPDET_TT_EN 0x00000020
+#define LP_TAMPDET_CT_EN 0x00000010
+#define LP_TAMPDET_MCR_EN 0x00000004
+#define LP_TAMPDET_SRTCR_EN 0x00000002
+
+#define LP_STATUS_SECURE
+#define LP_STATUS_NONSECURE
+#define LP_STATUS_SCANEXIT 0x00100000 /* all write 1 clear here on */
+#define LP_STATUS_EXT_SECVIO 0x00010000
+#define LP_STATUS_ET2 0x00000400
+#define LP_STATUS_ET1 0x00000200
+#define LP_STATUS_WMT2 0x00000100
+#define LP_STATUS_WMT1 0x00000080
+#define LP_STATUS_VTD 0x00000040
+#define LP_STATUS_TTD 0x00000020
+#define LP_STATUS_CTD 0x00000010
+#define LP_STATUS_PGD 0x00000008
+#define LP_STATUS_MCR 0x00000004
+#define LP_STATUS_SRTCR 0x00000002
+#define LP_STATUS_LPTA 0x00000001
+
+/* Full SNVS register page, including version/options */
+struct snvs_full {
+ struct snvs_hp hp;
+ struct snvs_lp lp;
+ u32 rsvd[731]; /* deadspace 0x08c-0xbf7 */
+
+ /* Version / Revision / Option ID space - end of register page */
+ u32 vid; /* 0xbf8 HP Version ID (VID 1) */
+ u32 opt_rev; /* 0xbfc HP Options / Revision (VID 2) */
+};
+
+#endif /* SNVSREGS_H */