]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00289885 [iMX6Q] Add Secure Memory and SECVIO support.
authorDan Douglass <dan.douglass@freescale.com>
Wed, 27 Nov 2013 09:40:44 +0000 (03:40 -0600)
committerNitin Garg <nitin.garg@freescale.com>
Fri, 16 Jan 2015 03:19:54 +0000 (21:19 -0600)
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.

[<vicki.milhoan@freescale.com>: Edited to apply to 3.14]
Signed-off-by: Dan Douglass <b41520@freescale.com>
(cherry picked from commit f3bfd42e2db3af8326734bebf750e94e74734f6e)
Signed-off-by: Victoria Milhoan <vicki.milhoan@freescale.com>
arch/arm/boot/dts/imx6qdl.dtsi
drivers/crypto/caam/Kconfig
drivers/crypto/caam/Makefile
drivers/crypto/caam/ctrl.c
drivers/crypto/caam/intern.h
drivers/crypto/caam/secvio.c [new file with mode: 0644]
drivers/crypto/caam/secvio.h [new file with mode: 0644]
drivers/crypto/caam/sm.h [new file with mode: 0644]
drivers/crypto/caam/sm_store.c [new file with mode: 0644]
drivers/crypto/caam/sm_test.c [new file with mode: 0644]
drivers/crypto/caam/snvsregs.h [new file with mode: 0644]

index cfe8c68429245634f03a5cfd4e0dd0c686abf34a..7cb7c22d6052974a3dc2280db5bfb74441fa60fd 100644 (file)
                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>;
                        clocks = <&clks IMX6QDL_CLK_APBH_DMA>;
                };
 
+               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>;
                                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>;
index 640f609dbf79a96b36d31decabca659d06cb3d7c..77ae267ed6bb6538846feebd63aff825e6cbdd76 100644 (file)
@@ -121,6 +121,47 @@ config CRYPTO_DEV_FSL_CAAM_RNG_TEST
          caam RNG. This test is several minutes long and executes
          just before the RNG is registered with the hw_random API.
 
+config CRYPTO_DEV_FSL_CAAM_SM
+       tristate "CAAM Secure Memory / Keystore API (EXPERIMENTAL)"
+       default n
+       help
+         Enables use of a prototype kernel-level Keystore API with CAAM
+         Secure Memory for insertion/extraction of bus-protected secrets.
+
+config CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE
+       int "Size of each keystore slot in Secure Memory"
+       depends on CRYPTO_DEV_FSL_CAAM_SM
+       range 5 9
+       default 7
+       help
+         Select size of allocation units to divide Secure Memory pages into
+         (the size of a "slot" as referenced inside the API code).
+         Established as powers of two.
+         Examples:
+               5 => 32 bytes
+               6 => 64 bytes
+               7 => 128 bytes
+               8 => 256 bytes
+               9 => 512 bytes
+
+config CRYPTO_DEV_FSL_CAAM_SM_TEST
+       tristate "CAAM Secure Memory - Keystore Test/Example (EXPERIMENTAL)"
+       depends on CRYPTO_DEV_FSL_CAAM_SM
+       default n
+       help
+         Example thread to exercise the Keystore API and to verify that
+         stored and recovered secrets can be used for general purpose
+         encryption/decryption.
+
+config CRYPTO_DEV_FSL_CAAM_SECVIO
+       tristate "CAAM/SNVS Security Violation Handler (EXPERIMENTAL)"
+       depends on CRYPTO_DEV_FSL_CAAM
+       default n
+       help
+         Enables installation of an interrupt handler with registrable
+          handler functions which can be specified to act on the consequences
+          of a security violation.
+
 config CRYPTO_DEV_FSL_CAAM_DEBUG
        bool "Enable debug output in CAAM driver"
        depends on CRYPTO_DEV_FSL_CAAM
index 550758a333e7c8e920a422da0cc20fc8807476d1..33741c38225c5a9dd07a3eeb2204a7b6c6233003 100644 (file)
@@ -10,6 +10,9 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.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
 
 caam-objs := ctrl.o
 caam_jr-objs := jr.o key_gen.o error.o
index 02b44f24dbde323c0348b524753fbef4ac038428..caf4824ae0ae9018d0710b95f881b80f62ed8183 100644 (file)
@@ -15,6 +15,7 @@
 #include "desc_constr.h"
 #include "error.h"
 #include "ctrl.h"
+#include "sm.h"
 
 /*
  * Descriptor to instantiate RNG State Handle 0 in normal mode and
@@ -400,6 +401,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;
@@ -430,6 +432,23 @@ static int caam_probe(struct platform_device *pdev)
        /* Get the IRQ of the controller (for security violations only) */
        ctrlpriv->secvio_irq = irq_of_parse_and_map(nprop, 0);
 
+       /* 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
index f4148e52f79dcd28645387ab770919a7e13fade2..4a5360724a0a08c8397df311c4fd0a1a1dc465a0 100644 (file)
@@ -66,6 +66,8 @@ struct caam_drv_private_jr {
 struct caam_drv_private {
 
        struct device *dev;
+       struct device *smdev;
+       struct device *secviodev;
        struct platform_device **jrpdev; /* Alloc'ed array per sub-device */
        struct platform_device *pdev;
 
@@ -74,6 +76,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 (file)
index 0000000..dcb756e
--- /dev/null
@@ -0,0 +1,335 @@
+
+/*
+ * CAAM/SEC 4.x Security Violation Handler
+ * Copyright (C) 2015 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 (file)
index 0000000..e0689bc
--- /dev/null
@@ -0,0 +1,64 @@
+
+/*
+ * CAAM Security Violation Handler
+ * Copyright (C) 2015 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 (file)
index 0000000..da14a94
--- /dev/null
@@ -0,0 +1,88 @@
+
+/*
+ * CAAM Secure Memory/Keywrap API Definitions
+ * Copyright (C) 2008-2015 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 (file)
index 0000000..e50e63b
--- /dev/null
@@ -0,0 +1,896 @@
+
+/*
+ * CAAM Secure Memory Storage Interface
+ * Copyright (C) 2008-2015 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 = caam_jr_alloc();
+       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 (file)
index 0000000..bddf8fd
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ * Secure Memory / Keystore Exemplification Module
+ * Copyright (C) 2015 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 (file)
index 0000000..fd74311
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * SNVS hardware register-level view
+ *
+ * Copyright (C) 2015 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 */